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

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

fix #16723 - Display changes count in changeset manager

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