source: josm/trunk/src/org/openstreetmap/josm/data/osm/Changeset.java@ 13388

Last change on this file since 13388 was 13173, checked in by Don-vip, 6 years ago

see #15310 - remove most of deprecated APIs

  • Property svn:eol-style set to native
File size: 13.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.Collections;
7import java.util.Date;
8import java.util.HashMap;
9import java.util.List;
10import java.util.Map;
11import java.util.Objects;
12import java.util.Optional;
13
14import org.openstreetmap.josm.data.Bounds;
15import org.openstreetmap.josm.data.coor.LatLon;
16import org.openstreetmap.josm.tools.CheckParameterUtil;
17import org.openstreetmap.josm.tools.date.DateUtils;
18
19/**
20 * Represents a single changeset in JOSM. For now its only used during
21 * upload but in the future we may do more.
22 * @since 625
23 */
24public final class Changeset implements Tagged, Comparable<Changeset> {
25
26 /** The maximum changeset tag length allowed by API 0.6 **/
27 public static final int MAX_CHANGESET_TAG_LENGTH = 255;
28
29 /** the changeset id */
30 private int id;
31 /** the user who owns the changeset */
32 private User user;
33 /** date this changeset was created at */
34 private Date createdAt;
35 /** the date this changeset was closed at*/
36 private Date closedAt;
37 /** indicates whether this changeset is still open or not */
38 private boolean open;
39 /** the min. coordinates of the bounding box of this changeset */
40 private LatLon min;
41 /** the max. coordinates of the bounding box of this changeset */
42 private LatLon max;
43 /** the number of comments for this changeset */
44 private int commentsCount;
45 /** the map of tags */
46 private Map<String, String> tags;
47 /** indicates whether this changeset is incomplete. For an incomplete changeset we only know its id */
48 private boolean incomplete;
49 /** the changeset content */
50 private ChangesetDataSet content;
51 /** the changeset discussion */
52 private List<ChangesetDiscussionComment> discussion;
53
54 /**
55 * Creates a new changeset with id 0.
56 */
57 public Changeset() {
58 this(0);
59 }
60
61 /**
62 * Creates a changeset with id <code>id</code>. If id &gt; 0, sets incomplete to true.
63 *
64 * @param id the id
65 */
66 public Changeset(int id) {
67 this.id = id;
68 this.incomplete = id > 0;
69 this.tags = new HashMap<>();
70 }
71
72 /**
73 * Creates a clone of <code>other</code>
74 *
75 * @param other the other changeset. If null, creates a new changeset with id 0.
76 */
77 public Changeset(Changeset other) {
78 if (other == null) {
79 this.id = 0;
80 this.tags = new HashMap<>();
81 } else if (other.isIncomplete()) {
82 setId(other.getId());
83 this.incomplete = true;
84 this.tags = new HashMap<>();
85 } else {
86 this.id = other.id;
87 mergeFrom(other);
88 this.incomplete = false;
89 }
90 }
91
92 /**
93 * Creates a changeset with the data obtained from the given preset, i.e.,
94 * the {@link AbstractPrimitive#getChangesetId() changeset id}, {@link AbstractPrimitive#getUser() user}, and
95 * {@link AbstractPrimitive#getTimestamp() timestamp}.
96 * @param primitive the primitive to use
97 * @return the created changeset
98 */
99 public static Changeset fromPrimitive(final OsmPrimitive primitive) {
100 final Changeset changeset = new Changeset(primitive.getChangesetId());
101 changeset.setUser(primitive.getUser());
102 changeset.setCreatedAt(primitive.getTimestamp()); // not accurate in all cases
103 return changeset;
104 }
105
106 /**
107 * Compares this changeset to another, based on their identifier.
108 * @param other other changeset
109 * @return the value {@code 0} if {@code getId() == other.getId()};
110 * a value less than {@code 0} if {@code getId() < other.getId()}; and
111 * a value greater than {@code 0} if {@code getId() > other.getId()}
112 */
113 @Override
114 public int compareTo(Changeset other) {
115 return Integer.compare(getId(), other.getId());
116 }
117
118 /**
119 * Returns the changeset name.
120 * @return the changeset name (untranslated: "changeset &lt;identifier&gt;")
121 */
122 public String getName() {
123 // no translation
124 return "changeset " + getId();
125 }
126
127 /**
128 * Returns the changeset display name, as per given name formatter.
129 * @param formatter name formatter
130 * @return the changeset display name, as per given name formatter
131 */
132 public String getDisplayName(NameFormatter formatter) {
133 return formatter.format(this);
134 }
135
136 /**
137 * Returns the changeset identifier.
138 * @return the changeset identifier
139 */
140 public int getId() {
141 return id;
142 }
143
144 /**
145 * Sets the changeset identifier.
146 * @param id changeset identifier
147 */
148 public void setId(int id) {
149 this.id = id;
150 }
151
152 /**
153 * Returns the changeset user.
154 * @return the changeset user
155 */
156 public User getUser() {
157 return user;
158 }
159
160 /**
161 * Sets the changeset user.
162 * @param user changeset user
163 */
164 public void setUser(User user) {
165 this.user = user;
166 }
167
168 /**
169 * Returns the changeset creation date.
170 * @return the changeset creation date
171 */
172 public Date getCreatedAt() {
173 return DateUtils.cloneDate(createdAt);
174 }
175
176 /**
177 * Sets the changeset creation date.
178 * @param createdAt changeset creation date
179 */
180 public void setCreatedAt(Date createdAt) {
181 this.createdAt = DateUtils.cloneDate(createdAt);
182 }
183
184 /**
185 * Returns the changeset closure date.
186 * @return the changeset closure date
187 */
188 public Date getClosedAt() {
189 return DateUtils.cloneDate(closedAt);
190 }
191
192 /**
193 * Sets the changeset closure date.
194 * @param closedAt changeset closure date
195 */
196 public void setClosedAt(Date closedAt) {
197 this.closedAt = DateUtils.cloneDate(closedAt);
198 }
199
200 /**
201 * Determines if this changeset is open.
202 * @return {@code true} if this changeset is open
203 */
204 public boolean isOpen() {
205 return open;
206 }
207
208 /**
209 * Sets whether this changeset is open.
210 * @param open {@code true} if this changeset is open
211 */
212 public void setOpen(boolean open) {
213 this.open = open;
214 }
215
216 /**
217 * Returns the min lat/lon of the changeset bounding box.
218 * @return the min lat/lon of the changeset bounding box
219 */
220 public LatLon getMin() {
221 return min;
222 }
223
224 /**
225 * Sets the min lat/lon of the changeset bounding box.
226 * @param min min lat/lon of the changeset bounding box
227 */
228 public void setMin(LatLon min) {
229 this.min = min;
230 }
231
232 /**
233 * Returns the max lat/lon of the changeset bounding box.
234 * @return the max lat/lon of the changeset bounding box
235 */
236 public LatLon getMax() {
237 return max;
238 }
239
240 /**
241 * Sets the max lat/lon of the changeset bounding box.
242 * @param max min lat/lon of the changeset bounding box
243 */
244 public void setMax(LatLon max) {
245 this.max = max;
246 }
247
248 /**
249 * Returns the changeset bounding box.
250 * @return the changeset bounding box
251 */
252 public Bounds getBounds() {
253 if (min != null && max != null)
254 return new Bounds(min, max);
255 return null;
256 }
257
258 /**
259 * Replies this changeset comment.
260 * @return this changeset comment (empty string if missing)
261 * @since 12494
262 */
263 public String getComment() {
264 return Optional.ofNullable(get("comment")).orElse("");
265 }
266
267 /**
268 * Replies the number of comments for this changeset discussion.
269 * @return the number of comments for this changeset discussion
270 * @since 7700
271 */
272 public int getCommentsCount() {
273 return commentsCount;
274 }
275
276 /**
277 * Sets the number of comments for this changeset discussion.
278 * @param commentsCount the number of comments for this changeset discussion
279 * @since 7700
280 */
281 public void setCommentsCount(int commentsCount) {
282 this.commentsCount = commentsCount;
283 }
284
285 @Override
286 public Map<String, String> getKeys() {
287 return tags;
288 }
289
290 @Override
291 public void setKeys(Map<String, String> keys) {
292 CheckParameterUtil.ensureParameterNotNull(keys, "keys");
293 keys.values().stream()
294 .filter(value -> value != null && value.length() > MAX_CHANGESET_TAG_LENGTH)
295 .findFirst()
296 .ifPresent(value -> {
297 throw new IllegalArgumentException("Changeset tag value is too long: "+value);
298 });
299 this.tags = keys;
300 }
301
302 /**
303 * Determines if this changeset is incomplete.
304 * @return {@code true} if this changeset is incomplete
305 */
306 public boolean isIncomplete() {
307 return incomplete;
308 }
309
310 /**
311 * Sets whether this changeset is incomplete
312 * @param incomplete {@code true} if this changeset is incomplete
313 */
314 public void setIncomplete(boolean incomplete) {
315 this.incomplete = incomplete;
316 }
317
318 @Override
319 public void put(String key, String value) {
320 CheckParameterUtil.ensureParameterNotNull(key, "key");
321 if (value != null && value.length() > MAX_CHANGESET_TAG_LENGTH) {
322 throw new IllegalArgumentException("Changeset tag value is too long: "+value);
323 }
324 this.tags.put(key, value);
325 }
326
327 @Override
328 public String get(String key) {
329 return this.tags.get(key);
330 }
331
332 @Override
333 public void remove(String key) {
334 this.tags.remove(key);
335 }
336
337 @Override
338 public void removeAll() {
339 this.tags.clear();
340 }
341
342 /**
343 * Determines if this changeset has equals semantic attributes with another one.
344 * @param other other changeset
345 * @return {@code true} if this changeset has equals semantic attributes with other changeset
346 */
347 public boolean hasEqualSemanticAttributes(Changeset other) {
348 if (other == null)
349 return false;
350 if (closedAt == null) {
351 if (other.closedAt != null)
352 return false;
353 } else if (!closedAt.equals(other.closedAt))
354 return false;
355 if (createdAt == null) {
356 if (other.createdAt != null)
357 return false;
358 } else if (!createdAt.equals(other.createdAt))
359 return false;
360 if (id != other.id)
361 return false;
362 if (max == null) {
363 if (other.max != null)
364 return false;
365 } else if (!max.equals(other.max))
366 return false;
367 if (min == null) {
368 if (other.min != null)
369 return false;
370 } else if (!min.equals(other.min))
371 return false;
372 if (open != other.open)
373 return false;
374 if (!tags.equals(other.tags))
375 return false;
376 if (user == null) {
377 if (other.user != null)
378 return false;
379 } else if (!user.equals(other.user))
380 return false;
381 return commentsCount == other.commentsCount;
382 }
383
384 @Override
385 public int hashCode() {
386 return Objects.hash(id);
387 }
388
389 @Override
390 public boolean equals(Object obj) {
391 if (this == obj) return true;
392 if (obj == null || getClass() != obj.getClass()) return false;
393 Changeset changeset = (Changeset) obj;
394 return id == changeset.id;
395 }
396
397 @Override
398 public boolean hasKeys() {
399 return !tags.keySet().isEmpty();
400 }
401
402 @Override
403 public Collection<String> keySet() {
404 return tags.keySet();
405 }
406
407 /**
408 * Determines if this changeset is new.
409 * @return {@code true} if this changeset is new ({@code id <= 0})
410 */
411 public boolean isNew() {
412 return id <= 0;
413 }
414
415 /**
416 * Merges changeset metadata from another changeset.
417 * @param other other changeset
418 */
419 public void mergeFrom(Changeset other) {
420 if (other == null)
421 return;
422 if (id != other.id)
423 return;
424 this.user = other.user;
425 this.createdAt = DateUtils.cloneDate(other.createdAt);
426 this.closedAt = DateUtils.cloneDate(other.closedAt);
427 this.open = other.open;
428 this.min = other.min;
429 this.max = other.max;
430 this.commentsCount = other.commentsCount;
431 this.tags = new HashMap<>(other.tags);
432 this.incomplete = other.incomplete;
433 this.discussion = other.discussion != null ? new ArrayList<>(other.discussion) : null;
434
435 // FIXME: merging of content required?
436 this.content = other.content;
437 }
438
439 /**
440 * Determines if this changeset has contents.
441 * @return {@code true} if this changeset has contents
442 */
443 public boolean hasContent() {
444 return content != null;
445 }
446
447 /**
448 * Returns the changeset contents.
449 * @return the changeset contents, can be null
450 */
451 public ChangesetDataSet getContent() {
452 return content;
453 }
454
455 /**
456 * Sets the changeset contents.
457 * @param content changeset contents, can be null
458 */
459 public void setContent(ChangesetDataSet content) {
460 this.content = content;
461 }
462
463 /**
464 * Replies the list of comments in the changeset discussion, if any.
465 * @return the list of comments in the changeset discussion. May be empty but never null
466 * @since 7704
467 */
468 public synchronized List<ChangesetDiscussionComment> getDiscussion() {
469 if (discussion == null) {
470 return Collections.emptyList();
471 }
472 return new ArrayList<>(discussion);
473 }
474
475 /**
476 * Adds a comment to the changeset discussion.
477 * @param comment the comment to add. Ignored if null
478 * @since 7704
479 */
480 public synchronized void addDiscussionComment(ChangesetDiscussionComment comment) {
481 if (comment == null) {
482 return;
483 }
484 if (discussion == null) {
485 discussion = new ArrayList<>();
486 }
487 discussion.add(comment);
488 }
489}
Note: See TracBrowser for help on using the repository browser.