source: josm/trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserModel.java

Last change on this file was 17903, checked in by simon04, 3 years ago

see #20879 - History browser: display version "*" if tag change has not been committed

  • Property svn:eol-style set to native
File size: 27.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.history;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.util.HashSet;
8import java.util.Objects;
9import java.util.Set;
10
11import javax.swing.JTable;
12import javax.swing.table.TableModel;
13
14import org.openstreetmap.josm.data.UserIdentityManager;
15import org.openstreetmap.josm.data.osm.DataSet;
16import org.openstreetmap.josm.data.osm.Node;
17import org.openstreetmap.josm.data.osm.OsmPrimitive;
18import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
19import org.openstreetmap.josm.data.osm.Relation;
20import org.openstreetmap.josm.data.osm.RelationMember;
21import org.openstreetmap.josm.data.osm.RelationMemberData;
22import org.openstreetmap.josm.data.osm.User;
23import org.openstreetmap.josm.data.osm.UserInfo;
24import org.openstreetmap.josm.data.osm.Way;
25import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
26import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
27import org.openstreetmap.josm.data.osm.event.DataSetListener;
28import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
29import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
30import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
31import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
32import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
33import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
34import org.openstreetmap.josm.data.osm.history.History;
35import org.openstreetmap.josm.data.osm.history.HistoryNode;
36import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
37import org.openstreetmap.josm.data.osm.history.HistoryRelation;
38import org.openstreetmap.josm.data.osm.history.HistoryWay;
39import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor;
40import org.openstreetmap.josm.gui.MainApplication;
41import org.openstreetmap.josm.gui.layer.Layer;
42import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
43import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
44import org.openstreetmap.josm.gui.layer.OsmDataLayer;
45import org.openstreetmap.josm.gui.util.ChangeNotifier;
46import org.openstreetmap.josm.tools.CheckParameterUtil;
47import org.openstreetmap.josm.tools.ColorScale;
48import org.openstreetmap.josm.tools.Logging;
49
50/**
51 * This is the model used by the history browser.
52 *
53 * The model state consists of the following elements:
54 * <ul>
55 * <li>the {@link History} of a specific {@link OsmPrimitive}</li>
56 * <li>a dedicated version in this {@link History} called the {@link PointInTimeType#REFERENCE_POINT_IN_TIME}</li>
57 * <li>another version in this {@link History} called the {@link PointInTimeType#CURRENT_POINT_IN_TIME}</li>
58 * </ul>
59 * {@link HistoryBrowser} always compares the {@link PointInTimeType#REFERENCE_POINT_IN_TIME} with the
60 * {@link PointInTimeType#CURRENT_POINT_IN_TIME}.
61
62 * This model provides various {@link TableModel}s for {@link JTable}s used in {@link HistoryBrowser}, for
63 * instance:
64 * <ul>
65 * <li>{@link #getTagTableModel(PointInTimeType)} replies a {@link TableModel} for the tags of either of
66 * the two selected versions</li>
67 * <li>{@link #getNodeListTableModel(PointInTimeType)} replies a {@link TableModel} for the list of nodes of
68 * the two selected versions (if the current history provides information about a {@link Way}</li>
69 * <li> {@link #getRelationMemberTableModel(PointInTimeType)} replies a {@link TableModel} for the list of relation
70 * members of the two selected versions (if the current history provides information about a {@link Relation}</li>
71 * </ul>
72 *
73 * @see HistoryBrowser
74 */
75public class HistoryBrowserModel extends ChangeNotifier implements ActiveLayerChangeListener, DataSetListener {
76 /** the history of an OsmPrimitive */
77 private History history;
78 private HistoryOsmPrimitive reference;
79 private HistoryOsmPrimitive current;
80 /**
81 * latest isn't a reference of history. It's a clone of the currently edited
82 * {@link OsmPrimitive} in the current edit layer.
83 */
84 private HistoryOsmPrimitive latest;
85
86 private final VersionTableModel versionTableModel;
87 private final TagTableModel currentTagTableModel;
88 private final TagTableModel referenceTagTableModel;
89 private final DiffTableModel currentRelationMemberTableModel;
90 private final DiffTableModel referenceRelationMemberTableModel;
91 private final DiffTableModel referenceNodeListTableModel;
92 private final DiffTableModel currentNodeListTableModel;
93 private final ColorScale dateScale;
94
95 /**
96 * constructor
97 */
98 public HistoryBrowserModel() {
99 versionTableModel = new VersionTableModel(this);
100 currentTagTableModel = new TagTableModel(this, PointInTimeType.CURRENT_POINT_IN_TIME);
101 referenceTagTableModel = new TagTableModel(this, PointInTimeType.REFERENCE_POINT_IN_TIME);
102 referenceNodeListTableModel = new DiffTableModel();
103 currentNodeListTableModel = new DiffTableModel();
104 currentRelationMemberTableModel = new DiffTableModel();
105 referenceRelationMemberTableModel = new DiffTableModel();
106 dateScale = ColorScale.createHSBScale(256);
107
108 DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
109 if (ds != null) {
110 ds.addDataSetListener(this);
111 }
112 MainApplication.getLayerManager().addActiveLayerChangeListener(this);
113 }
114
115 /**
116 * Creates a new history browser model for a given history.
117 *
118 * @param history the history. Must not be null.
119 * @throws IllegalArgumentException if history is null
120 */
121 public HistoryBrowserModel(History history) {
122 this();
123 CheckParameterUtil.ensureParameterNotNull(history, "history");
124 setHistory(history);
125 }
126
127 /**
128 * replies the history managed by this model
129 * @return the history
130 */
131 public History getHistory() {
132 return history;
133 }
134
135 boolean isSamePrimitive(History history) {
136 return getHistory() != null && Objects.equals(getHistory().getPrimitiveId(), history.getPrimitiveId());
137 }
138
139 private boolean canShowAsLatest(OsmPrimitive primitive) {
140 if (primitive == null)
141 return false;
142 if (primitive.isNew() || !primitive.isUsable())
143 return false;
144
145 //try creating a history primitive. if that fails, the primitive cannot be used.
146 try {
147 HistoryOsmPrimitive.forOsmPrimitive(primitive);
148 } catch (IllegalArgumentException ign) {
149 Logging.trace(ign);
150 return false;
151 }
152
153 if (history == null)
154 return false;
155 // only show latest of the same version if it is modified
156 if (history.getByVersion(primitive.getVersion()) != null)
157 return primitive.isModified();
158
159 // if latest version from history is higher than a non existing primitive version,
160 // that means this version has been redacted and the primitive cannot be used.
161 return history.getLatest().getVersion() <= primitive.getVersion();
162
163 // latest has a higher version than one of the primitives
164 // in the history (probably because the history got out of sync
165 // with uploaded data) -> show the primitive as latest
166 }
167
168 /**
169 * sets the history to be managed by this model
170 *
171 * @param history the history
172 *
173 */
174 public void setHistory(History history) {
175 boolean samePrimitive = isSamePrimitive(history); // needs to be before `this.history = history`
176 this.history = history;
177 if (samePrimitive && history.getNumVersions() > 0) {
178 reference = history.getByVersion(reference.getVersion());
179 current = history.getByVersion(current.getVersion());
180 } else if (history.getNumVersions() > 0) {
181 HistoryOsmPrimitive newLatest = null;
182 DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
183 if (ds != null) {
184 OsmPrimitive p = ds.getPrimitiveById(history.getId(), history.getType());
185 if (canShowAsLatest(p)) {
186 newLatest = new HistoryPrimitiveBuilder().build(p);
187 }
188 }
189 if (newLatest == null) {
190 current = history.getLatest();
191 int prevIndex = history.getNumVersions() - 2;
192 reference = prevIndex < 0 ? history.getEarliest() : history.get(prevIndex);
193 } else {
194 reference = history.getLatest();
195 current = newLatest;
196 }
197 setLatest(newLatest);
198 }
199 if (!history.isEmpty()) {
200 this.dateScale.setRange(
201 history.getEarliest().getInstant().toEpochMilli(),
202 history.getLatest().getInstant().toEpochMilli());
203 }
204 initTagTableModels();
205 fireModelChange();
206 }
207
208 private void fireModelChange() {
209 initNodeListTableModels();
210 initMemberListTableModels();
211 fireStateChanged();
212 versionTableModel.fireTableDataChanged();
213 }
214
215 /**
216 * Replies the table model to be used in a {@link JTable} which
217 * shows the list of versions in this history.
218 *
219 * @return the table model
220 */
221 public VersionTableModel getVersionTableModel() {
222 return versionTableModel;
223 }
224
225 private void initTagTableModels() {
226 currentTagTableModel.initKeyList();
227 referenceTagTableModel.initKeyList();
228 }
229
230 /**
231 * Should be called every time either reference of current changes to update the diff.
232 * TODO: Maybe rename to reflect this? eg. updateNodeListTableModels
233 */
234 private void initNodeListTableModels() {
235 if (current == null || current.getType() != OsmPrimitiveType.WAY
236 || reference == null || reference.getType() != OsmPrimitiveType.WAY)
237 return;
238 TwoColumnDiff diff = new TwoColumnDiff(
239 ((HistoryWay) reference).getNodes().toArray(),
240 ((HistoryWay) current).getNodes().toArray());
241 referenceNodeListTableModel.setRows(diff.referenceDiff, diff.referenceReversed);
242 currentNodeListTableModel.setRows(diff.currentDiff, false);
243 }
244
245 private void initMemberListTableModels() {
246 if (current == null || current.getType() != OsmPrimitiveType.RELATION
247 || reference == null || reference.getType() != OsmPrimitiveType.RELATION)
248 return;
249 TwoColumnDiff diff = new TwoColumnDiff(
250 ((HistoryRelation) reference).getMembers().toArray(),
251 ((HistoryRelation) current).getMembers().toArray());
252 referenceRelationMemberTableModel.setRows(diff.referenceDiff, diff.referenceReversed);
253 currentRelationMemberTableModel.setRows(diff.currentDiff, false);
254 }
255
256 /**
257 * Replies the tag table model for the respective point in time.
258 *
259 * @param pointInTimeType the type of the point in time (must not be null)
260 * @return the tag table model
261 * @throws IllegalArgumentException if pointInTimeType is null
262 */
263 public TagTableModel getTagTableModel(PointInTimeType pointInTimeType) {
264 CheckParameterUtil.ensureParameterNotNull(pointInTimeType, "pointInTimeType");
265 if (pointInTimeType == PointInTimeType.CURRENT_POINT_IN_TIME)
266 return currentTagTableModel;
267 else // REFERENCE_POINT_IN_TIME
268 return referenceTagTableModel;
269 }
270
271 /**
272 * Replies the node list table model for the respective point in time.
273 *
274 * @param pointInTimeType the type of the point in time (must not be null)
275 * @return the node list table model
276 * @throws IllegalArgumentException if pointInTimeType is null
277 */
278 public DiffTableModel getNodeListTableModel(PointInTimeType pointInTimeType) {
279 CheckParameterUtil.ensureParameterNotNull(pointInTimeType, "pointInTimeType");
280 if (pointInTimeType == PointInTimeType.CURRENT_POINT_IN_TIME)
281 return currentNodeListTableModel;
282 else // REFERENCE_POINT_IN_TIME
283 return referenceNodeListTableModel;
284 }
285
286 /**
287 * Replies the relation member table model for the respective point in time.
288 *
289 * @param pointInTimeType the type of the point in time (must not be null)
290 * @return the relation member table model
291 * @throws IllegalArgumentException if pointInTimeType is null
292 */
293 public DiffTableModel getRelationMemberTableModel(PointInTimeType pointInTimeType) {
294 CheckParameterUtil.ensureParameterNotNull(pointInTimeType, "pointInTimeType");
295 if (pointInTimeType == PointInTimeType.CURRENT_POINT_IN_TIME)
296 return currentRelationMemberTableModel;
297 else // REFERENCE_POINT_IN_TIME
298 return referenceRelationMemberTableModel;
299 }
300
301 /**
302 * Sets the {@link HistoryOsmPrimitive} which plays the role of a reference point
303 * in time (see {@link PointInTimeType}).
304 *
305 * @param reference the reference history primitive. Must not be null.
306 * @throws IllegalArgumentException if reference is null
307 * @throws IllegalStateException if this model isn't a assigned a history yet
308 * @throws IllegalArgumentException if reference isn't an history primitive for the history managed by this mode
309 *
310 * @see #setHistory(History)
311 * @see PointInTimeType
312 */
313 public void setReferencePointInTime(HistoryOsmPrimitive reference) {
314 CheckParameterUtil.ensureParameterNotNull(reference, "reference");
315 if (history == null)
316 throw new IllegalStateException(tr("History not initialized yet. Failed to set reference primitive."));
317 if (reference.getId() != history.getId())
318 throw new IllegalArgumentException(
319 tr("Failed to set reference. Reference ID {0} does not match history ID {1}.", reference.getId(), history.getId()));
320 if (history.getByVersion(reference.getVersion()) == null)
321 throw new IllegalArgumentException(
322 tr("Failed to set reference. Reference version {0} not available in history.", reference.getVersion()));
323
324 this.reference = reference;
325 initTagTableModels();
326 initNodeListTableModels();
327 initMemberListTableModels();
328 fireStateChanged();
329 }
330
331 /**
332 * Sets the {@link HistoryOsmPrimitive} which plays the role of the current point
333 * in time (see {@link PointInTimeType}).
334 *
335 * @param current the reference history primitive. Must not be {@code null}.
336 * @throws IllegalArgumentException if reference is {@code null}
337 * @throws IllegalStateException if this model isn't a assigned a history yet
338 * @throws IllegalArgumentException if reference isn't an history primitive for the history managed by this mode
339 *
340 * @see #setHistory(History)
341 * @see PointInTimeType
342 */
343 public void setCurrentPointInTime(HistoryOsmPrimitive current) {
344 CheckParameterUtil.ensureParameterNotNull(current, "current");
345 if (history == null)
346 throw new IllegalStateException(tr("History not initialized yet. Failed to set current primitive."));
347 if (current.getId() != history.getId())
348 throw new IllegalArgumentException(
349 tr("Failed to set reference. Reference ID {0} does not match history ID {1}.", current.getId(), history.getId()));
350 if (history.getByVersion(current.getVersion()) == null)
351 throw new IllegalArgumentException(
352 tr("Failed to set current primitive. Current version {0} not available in history.", current.getVersion()));
353 this.current = current;
354 initTagTableModels();
355 initNodeListTableModels();
356 initMemberListTableModels();
357 fireStateChanged();
358 }
359
360 /**
361 * Replies the history OSM primitive for the {@link PointInTimeType#CURRENT_POINT_IN_TIME}
362 *
363 * @return the history OSM primitive for the {@link PointInTimeType#CURRENT_POINT_IN_TIME} (may be null)
364 */
365 public HistoryOsmPrimitive getCurrentPointInTime() {
366 return getPointInTime(PointInTimeType.CURRENT_POINT_IN_TIME);
367 }
368
369 /**
370 * Replies the history OSM primitive for the {@link PointInTimeType#REFERENCE_POINT_IN_TIME}
371 *
372 * @return the history OSM primitive for the {@link PointInTimeType#REFERENCE_POINT_IN_TIME} (may be null)
373 */
374 public HistoryOsmPrimitive getReferencePointInTime() {
375 return getPointInTime(PointInTimeType.REFERENCE_POINT_IN_TIME);
376 }
377
378 /**
379 * replies the history OSM primitive for a given point in time
380 *
381 * @param type the type of the point in time (must not be null)
382 * @return the respective primitive. Can be null.
383 * @throws IllegalArgumentException if type is null
384 */
385 public HistoryOsmPrimitive getPointInTime(PointInTimeType type) {
386 CheckParameterUtil.ensureParameterNotNull(type, "type");
387 if (type == PointInTimeType.CURRENT_POINT_IN_TIME)
388 return current;
389 else if (type == PointInTimeType.REFERENCE_POINT_IN_TIME)
390 return reference;
391
392 // should not happen
393 return null;
394 }
395
396 /**
397 * Returns true if <code>primitive</code> is the latest primitive
398 * representing the version currently edited in the current data layer.
399 *
400 * @param primitive the primitive to check
401 * @return true if <code>primitive</code> is the latest primitive
402 */
403 public boolean isLatest(HistoryOsmPrimitive primitive) {
404 return primitive != null && primitive == latest;
405 }
406
407 /**
408 * Sets the reference point in time to the given row.
409 * @param row row number
410 */
411 public void setReferencePointInTime(int row) {
412 if (history == null)
413 return;
414 if (row == history.getNumVersions()) {
415 if (latest != null) {
416 setReferencePointInTime(latest);
417 }
418 return;
419 }
420 if (row < 0 || row > history.getNumVersions())
421 return;
422 setReferencePointInTime(history.get(row));
423 }
424
425 /**
426 * Sets the current point in time to the given row.
427 * @param row row number
428 */
429 public void setCurrentPointInTime(int row) {
430 if (history == null)
431 return;
432 if (row == history.getNumVersions()) {
433 if (latest != null) {
434 setCurrentPointInTime(latest);
435 }
436 return;
437 }
438 if (row < 0 || row > history.getNumVersions())
439 return;
440 setCurrentPointInTime(history.get(row));
441 }
442
443 /**
444 * Determines if the given row is the reference point in time.
445 * @param row row number
446 * @return {@code true} if the given row is the reference point in time
447 */
448 public boolean isReferencePointInTime(int row) {
449 if (history == null)
450 return false;
451 if (row == history.getNumVersions())
452 return latest == reference;
453 if (row < 0 || row > history.getNumVersions())
454 return false;
455 return history.get(row) == reference;
456 }
457
458 /**
459 * Determines if the given row is the current point in time.
460 * @param row row number
461 * @return {@code true} if the given row is the current point in time
462 */
463 public boolean isCurrentPointInTime(int row) {
464 if (history == null)
465 return false;
466 if (row == history.getNumVersions())
467 return latest == current;
468 if (row < 0 || row > history.getNumVersions())
469 return false;
470 return history.get(row) == current;
471 }
472
473 /**
474 * Returns the {@code HistoryPrimitive} at the given row.
475 * @param row row number
476 * @return the {@code HistoryPrimitive} at the given row
477 */
478 public HistoryOsmPrimitive getPrimitive(int row) {
479 if (history == null)
480 return null;
481 return isLatest(row) ? latest : history.get(row);
482 }
483
484 /**
485 * Determines if the given row is the latest.
486 * @param row row number
487 * @return {@code true} if the given row is the latest
488 */
489 public boolean isLatest(int row) {
490 return row >= history.getNumVersions();
491 }
492
493 /**
494 * Returns the latest {@code HistoryOsmPrimitive}.
495 * @return the latest {@code HistoryOsmPrimitive}
496 * @since 11646
497 */
498 public HistoryOsmPrimitive getLatest() {
499 return latest;
500 }
501
502 /**
503 * Returns the key set (union of current and reference point in type key sets).
504 * @return the key set (union of current and reference point in type key sets)
505 * @since 11647
506 */
507 public Set<String> getKeySet() {
508 Set<String> keySet = new HashSet<>();
509 if (current != null) {
510 keySet.addAll(current.getTags().keySet());
511 }
512 if (reference != null) {
513 keySet.addAll(reference.getTags().keySet());
514 }
515 return keySet;
516 }
517
518 /**
519 * Sets the latest {@code HistoryOsmPrimitive}.
520 * @param latest the latest {@code HistoryOsmPrimitive}
521 */
522 protected void setLatest(HistoryOsmPrimitive latest) {
523 if (latest == null) {
524 if (this.current == this.latest) {
525 this.current = history != null ? history.getLatest() : null;
526 }
527 if (this.reference == this.latest) {
528 this.reference = history != null ? history.getLatest() : null;
529 }
530 this.latest = null;
531 } else {
532 if (this.current == this.latest) {
533 this.current = latest;
534 }
535 if (this.reference == this.latest) {
536 this.reference = latest;
537 }
538 this.latest = latest;
539 }
540 fireModelChange();
541 }
542
543 /**
544 * Removes this model as listener for data change and layer change events.
545 *
546 */
547 public void unlinkAsListener() {
548 DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
549 if (ds != null) {
550 ds.removeDataSetListener(this);
551 }
552 MainApplication.getLayerManager().removeActiveLayerChangeListener(this);
553 }
554
555 /* ---------------------------------------------------------------------- */
556 /* DataSetListener */
557 /* ---------------------------------------------------------------------- */
558 @Override
559 public void nodeMoved(NodeMovedEvent event) {
560 Node node = event.getNode();
561 if (!node.isNew() && node.getId() == history.getId()) {
562 setLatest(new HistoryPrimitiveBuilder().build(node));
563 }
564 }
565
566 @Override
567 public void primitivesAdded(PrimitivesAddedEvent event) {
568 for (OsmPrimitive p: event.getPrimitives()) {
569 if (canShowAsLatest(p)) {
570 setLatest(new HistoryPrimitiveBuilder().build(p));
571 }
572 }
573 }
574
575 @Override
576 public void primitivesRemoved(PrimitivesRemovedEvent event) {
577 for (OsmPrimitive p: event.getPrimitives()) {
578 if (!p.isNew() && p.getId() == history.getId()) {
579 setLatest(null);
580 }
581 }
582 }
583
584 @Override
585 public void relationMembersChanged(RelationMembersChangedEvent event) {
586 Relation r = event.getRelation();
587 if (!r.isNew() && r.getId() == history.getId()) {
588 setLatest(new HistoryPrimitiveBuilder().build(r));
589 }
590 }
591
592 @Override
593 public void tagsChanged(TagsChangedEvent event) {
594 OsmPrimitive prim = event.getPrimitive();
595 if (!prim.isNew() && prim.getId() == history.getId()) {
596 setLatest(new HistoryPrimitiveBuilder().build(prim));
597 }
598 }
599
600 @Override
601 public void wayNodesChanged(WayNodesChangedEvent event) {
602 Way way = event.getChangedWay();
603 if (!way.isNew() && way.getId() == history.getId()) {
604 setLatest(new HistoryPrimitiveBuilder().build(way));
605 }
606 }
607
608 @Override
609 public void dataChanged(DataChangedEvent event) {
610 if (history == null)
611 return;
612 OsmPrimitive primitive = event.getDataset().getPrimitiveById(history.getId(), history.getType());
613 HistoryOsmPrimitive newLatest;
614 if (canShowAsLatest(primitive)) {
615 newLatest = new HistoryPrimitiveBuilder().build(primitive);
616 } else {
617 newLatest = null;
618 }
619 setLatest(newLatest);
620 fireModelChange();
621 }
622
623 @Override
624 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
625 // Irrelevant
626 }
627
628 /* ---------------------------------------------------------------------- */
629 /* ActiveLayerChangeListener */
630 /* ---------------------------------------------------------------------- */
631 @Override
632 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
633 Layer oldLayer = e.getPreviousActiveLayer();
634 if (oldLayer instanceof OsmDataLayer) {
635 OsmDataLayer l = (OsmDataLayer) oldLayer;
636 l.getDataSet().removeDataSetListener(this);
637 }
638 Layer newLayer = e.getSource().getActiveLayer();
639 if (!(newLayer instanceof OsmDataLayer)) {
640 latest = null;
641 fireModelChange();
642 return;
643 }
644 OsmDataLayer l = (OsmDataLayer) newLayer;
645 l.getDataSet().addDataSetListener(this);
646 OsmPrimitive primitive = history != null ? l.data.getPrimitiveById(history.getId(), history.getType()) : null;
647 HistoryOsmPrimitive newLatest;
648 if (canShowAsLatest(primitive)) {
649 newLatest = new HistoryPrimitiveBuilder().build(primitive);
650 } else {
651 newLatest = null;
652 }
653 setLatest(newLatest);
654 fireModelChange();
655 }
656
657 /**
658 * Creates a {@link HistoryOsmPrimitive} from a {@link OsmPrimitive}
659 *
660 */
661 static class HistoryPrimitiveBuilder implements OsmPrimitiveVisitor {
662 private HistoryOsmPrimitive clone;
663
664 @Override
665 public void visit(Node n) {
666 clone = new HistoryNode(n.getId(), n.getVersion(), n.isVisible(), getCurrentUser(), 0, null, n.getCoor(), false);
667 clone.setTags(n.getKeys());
668 }
669
670 @Override
671 public void visit(Relation r) {
672 clone = new HistoryRelation(r.getId(), r.getVersion(), r.isVisible(), getCurrentUser(), 0, null, false);
673 clone.setTags(r.getKeys());
674 HistoryRelation hr = (HistoryRelation) clone;
675 for (RelationMember rm : r.getMembers()) {
676 hr.addMember(new RelationMemberData(rm.getRole(), rm.getType(), rm.getUniqueId()));
677 }
678 }
679
680 @Override
681 public void visit(Way w) {
682 clone = new HistoryWay(w.getId(), w.getVersion(), w.isVisible(), getCurrentUser(), 0, null, false);
683 clone.setTags(w.getKeys());
684 for (Node n: w.getNodes()) {
685 ((HistoryWay) clone).addNode(n.getUniqueId());
686 }
687 }
688
689 private static User getCurrentUser() {
690 UserInfo info = UserIdentityManager.getInstance().getUserInfo();
691 return info == null ? User.getAnonymous() : User.createOsmUser(info.getId(), info.getDisplayName());
692 }
693
694 HistoryOsmPrimitive build(OsmPrimitive primitive) {
695 primitive.accept(this);
696 return clone;
697 }
698 }
699
700 /**
701 * Returns the color for the primitive in the given row
702 * @param row row number
703 * @return the color for the primitive in the given row
704 */
705 public Color getVersionColor(int row) {
706 HistoryOsmPrimitive primitive = getPrimitive(row);
707 return primitive != null && primitive.getInstant() != null ? getVersionColor(primitive) : null;
708 }
709
710 /**
711 * Returns the color for the given primitive timestamp
712 * @param primitive the history primitive
713 * @return the color for the given primitive timestamp
714 */
715 public Color getVersionColor(HistoryOsmPrimitive primitive) {
716 return dateScale.getColor(isLatest(primitive) ? System.currentTimeMillis() : primitive.getInstant().toEpochMilli());
717 }
718}
Note: See TracBrowser for help on using the repository browser.