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

Last change on this file since 12183 was 12033, checked in by Don-vip, 7 years ago

add more unit tests

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