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

Last change on this file since 13030 was 12809, checked in by bastiK, 7 years ago

replace abstract class AbstractVisitor by interface OsmPrimitiveVisitor; deprecate Visitor

  • data.osm.visitor.Visitor awkwardly mixes OsmPrimitive types and Changeset class; this may have been used in the past, but is no longer needed; AbstractVisitor should have been a super-interface of Visitor in the first place
  • hopefully, this is binary compatible and plugins can be updated gracefully
  • Property svn:eol-style set to native
File size: 13.9 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 * Visitor pattern.
108 * @param v visitor
109 * @deprecated no longer supported
110 */
111 @Deprecated
112 public void visit(org.openstreetmap.josm.data.osm.visitor.Visitor v) {
113 v.visit(this);
114 }
115
116 /**
117 * Compares this changeset to another, based on their identifier.
118 * @param other other changeset
119 * @return the value {@code 0} if {@code getId() == other.getId()};
120 * a value less than {@code 0} if {@code getId() < other.getId()}; and
121 * a value greater than {@code 0} if {@code getId() > other.getId()}
122 */
123 @Override
124 public int compareTo(Changeset other) {
125 return Integer.compare(getId(), other.getId());
126 }
127
128 /**
129 * Returns the changeset name.
130 * @return the changeset name (untranslated: "changeset &lt;identifier&gt;")
131 */
132 public String getName() {
133 // no translation
134 return "changeset " + getId();
135 }
136
137 /**
138 * Returns the changeset display name, as per given name formatter.
139 * @param formatter name formatter
140 * @return the changeset display name, as per given name formatter
141 */
142 public String getDisplayName(NameFormatter formatter) {
143 return formatter.format(this);
144 }
145
146 /**
147 * Returns the changeset identifier.
148 * @return the changeset identifier
149 */
150 public int getId() {
151 return id;
152 }
153
154 /**
155 * Sets the changeset identifier.
156 * @param id changeset identifier
157 */
158 public void setId(int id) {
159 this.id = id;
160 }
161
162 /**
163 * Returns the changeset user.
164 * @return the changeset user
165 */
166 public User getUser() {
167 return user;
168 }
169
170 /**
171 * Sets the changeset user.
172 * @param user changeset user
173 */
174 public void setUser(User user) {
175 this.user = user;
176 }
177
178 /**
179 * Returns the changeset creation date.
180 * @return the changeset creation date
181 */
182 public Date getCreatedAt() {
183 return DateUtils.cloneDate(createdAt);
184 }
185
186 /**
187 * Sets the changeset creation date.
188 * @param createdAt changeset creation date
189 */
190 public void setCreatedAt(Date createdAt) {
191 this.createdAt = DateUtils.cloneDate(createdAt);
192 }
193
194 /**
195 * Returns the changeset closure date.
196 * @return the changeset closure date
197 */
198 public Date getClosedAt() {
199 return DateUtils.cloneDate(closedAt);
200 }
201
202 /**
203 * Sets the changeset closure date.
204 * @param closedAt changeset closure date
205 */
206 public void setClosedAt(Date closedAt) {
207 this.closedAt = DateUtils.cloneDate(closedAt);
208 }
209
210 /**
211 * Determines if this changeset is open.
212 * @return {@code true} if this changeset is open
213 */
214 public boolean isOpen() {
215 return open;
216 }
217
218 /**
219 * Sets whether this changeset is open.
220 * @param open {@code true} if this changeset is open
221 */
222 public void setOpen(boolean open) {
223 this.open = open;
224 }
225
226 /**
227 * Returns the min lat/lon of the changeset bounding box.
228 * @return the min lat/lon of the changeset bounding box
229 */
230 public LatLon getMin() {
231 return min;
232 }
233
234 /**
235 * Sets the min lat/lon of the changeset bounding box.
236 * @param min min lat/lon of the changeset bounding box
237 */
238 public void setMin(LatLon min) {
239 this.min = min;
240 }
241
242 /**
243 * Returns the max lat/lon of the changeset bounding box.
244 * @return the max lat/lon of the changeset bounding box
245 */
246 public LatLon getMax() {
247 return max;
248 }
249
250 /**
251 * Sets the max lat/lon of the changeset bounding box.
252 * @param max min lat/lon of the changeset bounding box
253 */
254 public void setMax(LatLon max) {
255 this.max = max;
256 }
257
258 /**
259 * Returns the changeset bounding box.
260 * @return the changeset bounding box
261 */
262 public Bounds getBounds() {
263 if (min != null && max != null)
264 return new Bounds(min, max);
265 return null;
266 }
267
268 /**
269 * Replies this changeset comment.
270 * @return this changeset comment (empty string if missing)
271 * @since 12494
272 */
273 public String getComment() {
274 return Optional.ofNullable(get("comment")).orElse("");
275 }
276
277 /**
278 * Replies the number of comments for this changeset discussion.
279 * @return the number of comments for this changeset discussion
280 * @since 7700
281 */
282 public int getCommentsCount() {
283 return commentsCount;
284 }
285
286 /**
287 * Sets the number of comments for this changeset discussion.
288 * @param commentsCount the number of comments for this changeset discussion
289 * @since 7700
290 */
291 public void setCommentsCount(int commentsCount) {
292 this.commentsCount = commentsCount;
293 }
294
295 @Override
296 public Map<String, String> getKeys() {
297 return tags;
298 }
299
300 @Override
301 public void setKeys(Map<String, String> keys) {
302 CheckParameterUtil.ensureParameterNotNull(keys, "keys");
303 keys.values().stream()
304 .filter(value -> value != null && value.length() > MAX_CHANGESET_TAG_LENGTH)
305 .findFirst()
306 .ifPresent(value -> {
307 throw new IllegalArgumentException("Changeset tag value is too long: "+value);
308 });
309 this.tags = keys;
310 }
311
312 /**
313 * Determines if this changeset is incomplete.
314 * @return {@code true} if this changeset is incomplete
315 */
316 public boolean isIncomplete() {
317 return incomplete;
318 }
319
320 /**
321 * Sets whether this changeset is incomplete
322 * @param incomplete {@code true} if this changeset is incomplete
323 */
324 public void setIncomplete(boolean incomplete) {
325 this.incomplete = incomplete;
326 }
327
328 @Override
329 public void put(String key, String value) {
330 CheckParameterUtil.ensureParameterNotNull(key, "key");
331 if (value != null && value.length() > MAX_CHANGESET_TAG_LENGTH) {
332 throw new IllegalArgumentException("Changeset tag value is too long: "+value);
333 }
334 this.tags.put(key, value);
335 }
336
337 @Override
338 public String get(String key) {
339 return this.tags.get(key);
340 }
341
342 @Override
343 public void remove(String key) {
344 this.tags.remove(key);
345 }
346
347 @Override
348 public void removeAll() {
349 this.tags.clear();
350 }
351
352 /**
353 * Determines if this changeset has equals semantic attributes with another one.
354 * @param other other changeset
355 * @return {@code true} if this changeset has equals semantic attributes with other changeset
356 */
357 public boolean hasEqualSemanticAttributes(Changeset other) {
358 if (other == null)
359 return false;
360 if (closedAt == null) {
361 if (other.closedAt != null)
362 return false;
363 } else if (!closedAt.equals(other.closedAt))
364 return false;
365 if (createdAt == null) {
366 if (other.createdAt != null)
367 return false;
368 } else if (!createdAt.equals(other.createdAt))
369 return false;
370 if (id != other.id)
371 return false;
372 if (max == null) {
373 if (other.max != null)
374 return false;
375 } else if (!max.equals(other.max))
376 return false;
377 if (min == null) {
378 if (other.min != null)
379 return false;
380 } else if (!min.equals(other.min))
381 return false;
382 if (open != other.open)
383 return false;
384 if (!tags.equals(other.tags))
385 return false;
386 if (user == null) {
387 if (other.user != null)
388 return false;
389 } else if (!user.equals(other.user))
390 return false;
391 return commentsCount == other.commentsCount;
392 }
393
394 @Override
395 public int hashCode() {
396 return Objects.hash(id);
397 }
398
399 @Override
400 public boolean equals(Object obj) {
401 if (this == obj) return true;
402 if (obj == null || getClass() != obj.getClass()) return false;
403 Changeset changeset = (Changeset) obj;
404 return id == changeset.id;
405 }
406
407 @Override
408 public boolean hasKeys() {
409 return !tags.keySet().isEmpty();
410 }
411
412 @Override
413 public Collection<String> keySet() {
414 return tags.keySet();
415 }
416
417 /**
418 * Determines if this changeset is new.
419 * @return {@code true} if this changeset is new ({@code id <= 0})
420 */
421 public boolean isNew() {
422 return id <= 0;
423 }
424
425 /**
426 * Merges changeset metadata from another changeset.
427 * @param other other changeset
428 */
429 public void mergeFrom(Changeset other) {
430 if (other == null)
431 return;
432 if (id != other.id)
433 return;
434 this.user = other.user;
435 this.createdAt = DateUtils.cloneDate(other.createdAt);
436 this.closedAt = DateUtils.cloneDate(other.closedAt);
437 this.open = other.open;
438 this.min = other.min;
439 this.max = other.max;
440 this.commentsCount = other.commentsCount;
441 this.tags = new HashMap<>(other.tags);
442 this.incomplete = other.incomplete;
443 this.discussion = other.discussion != null ? new ArrayList<>(other.discussion) : null;
444
445 // FIXME: merging of content required?
446 this.content = other.content;
447 }
448
449 /**
450 * Determines if this changeset has contents.
451 * @return {@code true} if this changeset has contents
452 */
453 public boolean hasContent() {
454 return content != null;
455 }
456
457 /**
458 * Returns the changeset contents.
459 * @return the changeset contents, can be null
460 */
461 public ChangesetDataSet getContent() {
462 return content;
463 }
464
465 /**
466 * Sets the changeset contents.
467 * @param content changeset contents, can be null
468 */
469 public void setContent(ChangesetDataSet content) {
470 this.content = content;
471 }
472
473 /**
474 * Replies the list of comments in the changeset discussion, if any.
475 * @return the list of comments in the changeset discussion. May be empty but never null
476 * @since 7704
477 */
478 public synchronized List<ChangesetDiscussionComment> getDiscussion() {
479 if (discussion == null) {
480 return Collections.emptyList();
481 }
482 return new ArrayList<>(discussion);
483 }
484
485 /**
486 * Adds a comment to the changeset discussion.
487 * @param comment the comment to add. Ignored if null
488 * @since 7704
489 */
490 public synchronized void addDiscussionComment(ChangesetDiscussionComment comment) {
491 if (comment == null) {
492 return;
493 }
494 if (discussion == null) {
495 discussion = new ArrayList<>();
496 }
497 discussion.add(comment);
498 }
499}
Note: See TracBrowser for help on using the repository browser.