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

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

fix #5869 - Download dialog, bookmarks: add "home location" bookmark (if set in OSM user settings) + last 15 changesets bookmarks

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