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

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

fix potential NPEs and Sonar issues related to serialization

  • Property svn:eol-style set to native
File size: 29.7 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.text.DateFormat;
7import java.util.ArrayList;
8import java.util.Collections;
9import java.util.HashSet;
10import java.util.List;
11import java.util.Observable;
12
13import javax.swing.JTable;
14import javax.swing.table.AbstractTableModel;
15import javax.swing.table.TableModel;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.data.osm.Node;
19import org.openstreetmap.josm.data.osm.OsmPrimitive;
20import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
21import org.openstreetmap.josm.data.osm.Relation;
22import org.openstreetmap.josm.data.osm.RelationMember;
23import org.openstreetmap.josm.data.osm.RelationMemberData;
24import org.openstreetmap.josm.data.osm.User;
25import org.openstreetmap.josm.data.osm.UserInfo;
26import org.openstreetmap.josm.data.osm.Way;
27import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
28import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
29import org.openstreetmap.josm.data.osm.event.DataSetListener;
30import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
31import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
32import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
33import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
34import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
35import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
36import org.openstreetmap.josm.data.osm.history.History;
37import org.openstreetmap.josm.data.osm.history.HistoryNode;
38import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
39import org.openstreetmap.josm.data.osm.history.HistoryRelation;
40import org.openstreetmap.josm.data.osm.history.HistoryWay;
41import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
42import org.openstreetmap.josm.gui.JosmUserIdentityManager;
43import org.openstreetmap.josm.gui.MapView;
44import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
45import org.openstreetmap.josm.gui.layer.Layer;
46import org.openstreetmap.josm.gui.layer.OsmDataLayer;
47import org.openstreetmap.josm.tools.CheckParameterUtil;
48import org.openstreetmap.josm.tools.date.DateUtils;
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 Observable implements LayerChangeListener, 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
94 /**
95 * constructor
96 */
97 public HistoryBrowserModel() {
98 versionTableModel = new VersionTableModel();
99 currentTagTableModel = new TagTableModel(PointInTimeType.CURRENT_POINT_IN_TIME);
100 referenceTagTableModel = new TagTableModel(PointInTimeType.REFERENCE_POINT_IN_TIME);
101 referenceNodeListTableModel = new DiffTableModel();
102 currentNodeListTableModel = new DiffTableModel();
103 currentRelationMemberTableModel = new DiffTableModel();
104 referenceRelationMemberTableModel = new DiffTableModel();
105
106 OsmDataLayer editLayer = Main.main.getEditLayer();
107 if (editLayer != null) {
108 editLayer.data.addDataSetListener(this);
109 }
110 MapView.addLayerChangeListener(this);
111 }
112
113 /**
114 * Creates a new history browser model for a given history.
115 *
116 * @param history the history. Must not be null.
117 * @throws IllegalArgumentException if history is null
118 */
119 public HistoryBrowserModel(History history) {
120 this();
121 CheckParameterUtil.ensureParameterNotNull(history, "history");
122 setHistory(history);
123 }
124
125 /**
126 * replies the history managed by this model
127 * @return the history
128 */
129 public History getHistory() {
130 return history;
131 }
132
133 protected boolean hasNewNodes(Way way) {
134 for (Node n: way.getNodes()) {
135 if (n.isNew()) return true;
136 }
137 return false;
138 }
139 protected boolean canShowAsLatest(OsmPrimitive primitive) {
140 if (primitive == null) return false;
141 if (primitive.isNew() || !primitive.isUsable()) return false;
142
143 //try creating a history primitive. if that fails, the primitive cannot be used.
144 try {
145 HistoryOsmPrimitive.forOsmPrimitive(primitive);
146 } catch (Exception ign) {
147 return false;
148 }
149
150 if (history == null) return false;
151 // only show latest of the same version if it is modified
152 if (history.getByVersion(primitive.getVersion()) != null)
153 return primitive.isModified();
154
155 // if latest version from history is higher than a non existing primitive version,
156 // that means this version has been redacted and the primitive cannot be used.
157 if (history.getLatest().getVersion() > primitive.getVersion())
158 return false;
159
160 // latest has a higher version than one of the primitives
161 // in the history (probably because the history got out of sync
162 // with uploaded data) -> show the primitive as latest
163 return true;
164 }
165
166 /**
167 * sets the history to be managed by this model
168 *
169 * @param history the history
170 *
171 */
172 public void setHistory(History history) {
173 this.history = history;
174 if (history.getNumVersions() > 0) {
175 HistoryOsmPrimitive newLatest = null;
176 OsmDataLayer editLayer = Main.main.getEditLayer();
177 if (editLayer != null) {
178 OsmPrimitive p = editLayer.data.getPrimitiveById(history.getId(), history.getType());
179 if (canShowAsLatest(p)) {
180 newLatest = new HistoryPrimitiveBuilder().build(p);
181 }
182 }
183 if (newLatest == null) {
184 current = history.getLatest();
185 int prevIndex = history.getNumVersions() - 2;
186 reference = prevIndex < 0 ? history.getEarliest() : history.get(prevIndex);
187 } else {
188 reference = history.getLatest();
189 current = newLatest;
190 }
191 setLatest(newLatest);
192 }
193 initTagTableModels();
194 fireModelChange();
195 }
196
197 protected void fireModelChange() {
198 initNodeListTableModels();
199 initMemberListTableModels();
200 setChanged();
201 notifyObservers();
202 versionTableModel.fireTableDataChanged();
203 }
204
205 /**
206 * Replies the table model to be used in a {@link JTable} which
207 * shows the list of versions in this history.
208 *
209 * @return the table model
210 */
211 public VersionTableModel getVersionTableModel() {
212 return versionTableModel;
213 }
214
215 protected void initTagTableModels() {
216 currentTagTableModel.initKeyList();
217 referenceTagTableModel.initKeyList();
218 }
219
220 /**
221 * Should be called everytime either reference of current changes to update the diff.
222 * TODO: Maybe rename to reflect this? eg. updateNodeListTableModels
223 */
224 protected void initNodeListTableModels() {
225 if(current.getType() != OsmPrimitiveType.WAY || reference.getType() != OsmPrimitiveType.WAY)
226 return;
227 TwoColumnDiff diff = new TwoColumnDiff(
228 ((HistoryWay)reference).getNodes().toArray(),
229 ((HistoryWay)current).getNodes().toArray());
230 referenceNodeListTableModel.setRows(diff.referenceDiff, diff.referenceReversed);
231 currentNodeListTableModel.setRows(diff.currentDiff, false);
232 }
233
234 protected void initMemberListTableModels() {
235 if(current.getType() != OsmPrimitiveType.RELATION || reference.getType() != OsmPrimitiveType.RELATION)
236 return;
237 TwoColumnDiff diff = new TwoColumnDiff(
238 ((HistoryRelation)reference).getMembers().toArray(),
239 ((HistoryRelation)current).getMembers().toArray());
240 referenceRelationMemberTableModel.setRows(diff.referenceDiff, diff.referenceReversed);
241 currentRelationMemberTableModel.setRows(diff.currentDiff, false);
242 }
243
244 /**
245 * replies the tag table model for the respective point in time
246 *
247 * @param pointInTimeType the type of the point in time (must not be null)
248 * @return the tag table model
249 * @throws IllegalArgumentException if pointInTimeType is null
250 */
251 public TagTableModel getTagTableModel(PointInTimeType pointInTimeType) {
252 CheckParameterUtil.ensureParameterNotNull(pointInTimeType, "pointInTimeType");
253 if (pointInTimeType.equals(PointInTimeType.CURRENT_POINT_IN_TIME))
254 return currentTagTableModel;
255 else if (pointInTimeType.equals(PointInTimeType.REFERENCE_POINT_IN_TIME))
256 return referenceTagTableModel;
257
258 // should not happen
259 return null;
260 }
261
262 public DiffTableModel getNodeListTableModel(PointInTimeType pointInTimeType) {
263 CheckParameterUtil.ensureParameterNotNull(pointInTimeType, "pointInTimeType");
264 if (pointInTimeType.equals(PointInTimeType.CURRENT_POINT_IN_TIME))
265 return currentNodeListTableModel;
266 else if (pointInTimeType.equals(PointInTimeType.REFERENCE_POINT_IN_TIME))
267 return referenceNodeListTableModel;
268
269 // should not happen
270 return null;
271 }
272
273 public DiffTableModel getRelationMemberTableModel(PointInTimeType pointInTimeType) {
274 CheckParameterUtil.ensureParameterNotNull(pointInTimeType, "pointInTimeType");
275 if (pointInTimeType.equals(PointInTimeType.CURRENT_POINT_IN_TIME))
276 return currentRelationMemberTableModel;
277 else if (pointInTimeType.equals(PointInTimeType.REFERENCE_POINT_IN_TIME))
278 return referenceRelationMemberTableModel;
279
280 // should not happen
281 return null;
282 }
283
284 /**
285 * Sets the {@link HistoryOsmPrimitive} which plays the role of a reference point
286 * in time (see {@link PointInTimeType}).
287 *
288 * @param reference the reference history primitive. Must not be null.
289 * @throws IllegalArgumentException if reference is null
290 * @throws IllegalStateException if this model isn't a assigned a history yet
291 * @throws IllegalArgumentException if reference isn't an history primitive for the history managed by this mode
292 *
293 * @see #setHistory(History)
294 * @see PointInTimeType
295 */
296 public void setReferencePointInTime(HistoryOsmPrimitive reference) {
297 CheckParameterUtil.ensureParameterNotNull(reference, "reference");
298 if (history == null)
299 throw new IllegalStateException(tr("History not initialized yet. Failed to set reference primitive."));
300 if (reference.getId() != history.getId())
301 throw new IllegalArgumentException(tr("Failed to set reference. Reference ID {0} does not match history ID {1}.", reference.getId(), history.getId()));
302 HistoryOsmPrimitive primitive = history.getByVersion(reference.getVersion());
303 if (primitive == null)
304 throw new IllegalArgumentException(tr("Failed to set reference. Reference version {0} not available in history.", reference.getVersion()));
305
306 this.reference = reference;
307 initTagTableModels();
308 initNodeListTableModels();
309 initMemberListTableModels();
310 setChanged();
311 notifyObservers();
312 }
313
314 /**
315 * Sets the {@link HistoryOsmPrimitive} which plays the role of the current point
316 * in time (see {@link PointInTimeType}).
317 *
318 * @param current the reference history primitive. Must not be {@code null}.
319 * @throws IllegalArgumentException if reference is {@code null}
320 * @throws IllegalStateException if this model isn't a assigned a history yet
321 * @throws IllegalArgumentException if reference isn't an history primitive for the history managed by this mode
322 *
323 * @see #setHistory(History)
324 * @see PointInTimeType
325 */
326 public void setCurrentPointInTime(HistoryOsmPrimitive current) {
327 CheckParameterUtil.ensureParameterNotNull(current, "current");
328 if (history == null)
329 throw new IllegalStateException(tr("History not initialized yet. Failed to set current primitive."));
330 if (current.getId() != history.getId())
331 throw new IllegalArgumentException(tr("Failed to set reference. Reference ID {0} does not match history ID {1}.", current.getId(), history.getId()));
332 HistoryOsmPrimitive primitive = history.getByVersion(current.getVersion());
333 if (primitive == null)
334 throw new IllegalArgumentException(tr("Failed to set current primitive. Current version {0} not available in history.", current.getVersion()));
335 this.current = current;
336 initTagTableModels();
337 initNodeListTableModels();
338 initMemberListTableModels();
339 setChanged();
340 notifyObservers();
341 }
342
343 /**
344 * Replies the history OSM primitive for the {@link PointInTimeType#CURRENT_POINT_IN_TIME}
345 *
346 * @return the history OSM primitive for the {@link PointInTimeType#CURRENT_POINT_IN_TIME} (may be null)
347 */
348 public HistoryOsmPrimitive getCurrentPointInTime() {
349 return getPointInTime(PointInTimeType.CURRENT_POINT_IN_TIME);
350 }
351
352 /**
353 * Replies the history OSM primitive for the {@link PointInTimeType#REFERENCE_POINT_IN_TIME}
354 *
355 * @return the history OSM primitive for the {@link PointInTimeType#REFERENCE_POINT_IN_TIME} (may be null)
356 */
357 public HistoryOsmPrimitive getReferencePointInTime() {
358 return getPointInTime(PointInTimeType.REFERENCE_POINT_IN_TIME);
359 }
360
361 /**
362 * replies the history OSM primitive for a given point in time
363 *
364 * @param type the type of the point in time (must not be null)
365 * @return the respective primitive. Can be null.
366 * @throws IllegalArgumentException if type is null
367 */
368 public HistoryOsmPrimitive getPointInTime(PointInTimeType type) {
369 CheckParameterUtil.ensureParameterNotNull(type, "type");
370 if (type.equals(PointInTimeType.CURRENT_POINT_IN_TIME))
371 return current;
372 else if (type.equals(PointInTimeType.REFERENCE_POINT_IN_TIME))
373 return reference;
374
375 // should not happen
376 return null;
377 }
378
379 /**
380 * Returns true if <code>primitive</code> is the latest primitive
381 * representing the version currently edited in the current data
382 * layer.
383 *
384 * @param primitive the primitive to check
385 * @return true if <code>primitive</code> is the latest primitive
386 */
387 public boolean isLatest(HistoryOsmPrimitive primitive) {
388 if (primitive == null) return false;
389 return primitive == latest;
390 }
391
392 /**
393 * The table model for the list of versions in the current history
394 *
395 */
396 public final class VersionTableModel extends AbstractTableModel {
397
398 private VersionTableModel() {
399 }
400
401 @Override
402 public int getRowCount() {
403 if (history == null)
404 return 0;
405 int ret = history.getNumVersions();
406 if (latest != null) {
407 ret++;
408 }
409 return ret;
410 }
411
412 @Override
413 public Object getValueAt(int row, int column) {
414 switch (column) {
415 case 0:
416 return Long.toString(getPrimitive(row).getVersion());
417 case 1:
418 return isReferencePointInTime(row);
419 case 2:
420 return isCurrentPointInTime(row);
421 case 3:
422 HistoryOsmPrimitive p3 = getPrimitive(row);
423 if (p3 != null && p3.getTimestamp() != null)
424 return DateUtils.formatDateTime(p3.getTimestamp(), DateFormat.SHORT, DateFormat.SHORT);
425 return null;
426 case 4:
427 HistoryOsmPrimitive p4 = getPrimitive(row);
428 if (p4 != null) {
429 User user = p4.getUser();
430 if (user != null)
431 return user.getName();
432 }
433 return null;
434 }
435 return null;
436 }
437
438 @Override
439 public void setValueAt(Object aValue, int row, int column) {
440 if (!((Boolean) aValue)) return;
441 switch (column) {
442 case 1:
443 setReferencePointInTime(row);
444 break;
445 case 2:
446 setCurrentPointInTime(row);
447 break;
448 default:
449 return;
450 }
451 fireTableDataChanged();
452 }
453
454 @Override
455 public boolean isCellEditable(int row, int column) {
456 return column >= 1 && column <= 2;
457 }
458
459 public void setReferencePointInTime(int row) {
460 if (history == null) return;
461 if (row == history.getNumVersions()) {
462 if (latest != null) {
463 HistoryBrowserModel.this.setReferencePointInTime(latest);
464 }
465 return;
466 }
467 if (row < 0 || row > history.getNumVersions()) return;
468 HistoryOsmPrimitive reference = history.get(row);
469 HistoryBrowserModel.this.setReferencePointInTime(reference);
470 }
471
472 public void setCurrentPointInTime(int row) {
473 if (history == null) return;
474 if (row == history.getNumVersions()) {
475 if (latest != null) {
476 HistoryBrowserModel.this.setCurrentPointInTime(latest);
477 }
478 return;
479 }
480 if (row < 0 || row > history.getNumVersions()) return;
481 HistoryOsmPrimitive current = history.get(row);
482 HistoryBrowserModel.this.setCurrentPointInTime(current);
483 }
484
485 public boolean isReferencePointInTime(int row) {
486 if (history == null) return false;
487 if (row == history.getNumVersions())
488 return latest == reference;
489 if (row < 0 || row > history.getNumVersions()) return false;
490 HistoryOsmPrimitive p = history.get(row);
491 return p == reference;
492 }
493
494 public boolean isCurrentPointInTime(int row) {
495 if (history == null) return false;
496 if (row == history.getNumVersions())
497 return latest == current;
498 if (row < 0 || row > history.getNumVersions()) return false;
499 HistoryOsmPrimitive p = history.get(row);
500 return p == current;
501 }
502
503 public HistoryOsmPrimitive getPrimitive(int row) {
504 if (history == null)
505 return null;
506 return isLatest(row) ? latest : history.get(row);
507 }
508
509 public boolean isLatest(int row) {
510 return row >= history.getNumVersions();
511 }
512
513 public OsmPrimitive getLatest() {
514 if (latest == null) return null;
515 OsmDataLayer editLayer = Main.main.getEditLayer();
516 if (editLayer == null) return null;
517 return editLayer.data.getPrimitiveById(latest.getId(), latest.getType());
518 }
519
520 @Override
521 public int getColumnCount() {
522 return 6;
523 }
524 }
525
526 /**
527 * The table model for the tags of the version at {@link PointInTimeType#REFERENCE_POINT_IN_TIME}
528 * or {@link PointInTimeType#CURRENT_POINT_IN_TIME}
529 *
530 */
531 public class TagTableModel extends AbstractTableModel {
532
533 private transient List<String> keys;
534 private PointInTimeType pointInTimeType;
535
536 protected void initKeyList() {
537 HashSet<String> keySet = new HashSet<>();
538 if (current != null) {
539 keySet.addAll(current.getTags().keySet());
540 }
541 if (reference != null) {
542 keySet.addAll(reference.getTags().keySet());
543 }
544 keys = new ArrayList<>(keySet);
545 Collections.sort(keys);
546 fireTableDataChanged();
547 }
548
549 protected TagTableModel(PointInTimeType type) {
550 pointInTimeType = type;
551 initKeyList();
552 }
553
554 @Override
555 public int getRowCount() {
556 if (keys == null) return 0;
557 return keys.size();
558 }
559
560 @Override
561 public Object getValueAt(int row, int column) {
562 return keys.get(row);
563 }
564
565 @Override
566 public boolean isCellEditable(int row, int column) {
567 return false;
568 }
569
570 public boolean hasTag(String key) {
571 HistoryOsmPrimitive primitive = getPointInTime(pointInTimeType);
572 if (primitive == null)
573 return false;
574 return primitive.hasTag(key);
575 }
576
577 public String getValue(String key) {
578 HistoryOsmPrimitive primitive = getPointInTime(pointInTimeType);
579 if (primitive == null)
580 return null;
581 return primitive.get(key);
582 }
583
584 public boolean oppositeHasTag(String key) {
585 PointInTimeType opposite = pointInTimeType.opposite();
586 HistoryOsmPrimitive primitive = getPointInTime(opposite);
587 if (primitive == null)
588 return false;
589 return primitive.hasTag(key);
590 }
591
592 public String getOppositeValue(String key) {
593 PointInTimeType opposite = pointInTimeType.opposite();
594 HistoryOsmPrimitive primitive = getPointInTime(opposite);
595 if (primitive == null)
596 return null;
597 return primitive.get(key);
598 }
599
600 public boolean hasSameValueAsOpposite(String key) {
601 String value = getValue(key);
602 String oppositeValue = getOppositeValue(key);
603 if (value == null || oppositeValue == null)
604 return false;
605 return value.equals(oppositeValue);
606 }
607
608 public PointInTimeType getPointInTimeType() {
609 return pointInTimeType;
610 }
611
612 public boolean isCurrentPointInTime() {
613 return pointInTimeType.equals(PointInTimeType.CURRENT_POINT_IN_TIME);
614 }
615
616 public boolean isReferencePointInTime() {
617 return pointInTimeType.equals(PointInTimeType.REFERENCE_POINT_IN_TIME);
618 }
619
620 @Override
621 public int getColumnCount() {
622 return 1;
623 }
624 }
625
626 protected void setLatest(HistoryOsmPrimitive latest) {
627 if (latest == null) {
628 if (this.current == this.latest) {
629 this.current = history.getLatest();
630 }
631 if (this.reference == this.latest) {
632 this.current = history.getLatest();
633 }
634 this.latest = null;
635 } else {
636 if (this.current == this.latest) {
637 this.current = latest;
638 }
639 if (this.reference == this.latest) {
640 this.reference = latest;
641 }
642 this.latest = latest;
643 }
644 fireModelChange();
645 }
646
647 /**
648 * Removes this model as listener for data change and layer change
649 * events.
650 *
651 */
652 public void unlinkAsListener() {
653 OsmDataLayer editLayer = Main.main.getEditLayer();
654 if (editLayer != null) {
655 editLayer.data.removeDataSetListener(this);
656 }
657 MapView.removeLayerChangeListener(this);
658 }
659
660 /* ---------------------------------------------------------------------- */
661 /* DataSetListener */
662 /* ---------------------------------------------------------------------- */
663 @Override
664 public void nodeMoved(NodeMovedEvent event) {
665 Node node = event.getNode();
666 if (!node.isNew() && node.getId() == history.getId()) {
667 setLatest(new HistoryPrimitiveBuilder().build(node));
668 }
669 }
670
671 @Override
672 public void primitivesAdded(PrimitivesAddedEvent event) {
673 for (OsmPrimitive p: event.getPrimitives()) {
674 if (canShowAsLatest(p)) {
675 setLatest(new HistoryPrimitiveBuilder().build(p));
676 }
677 }
678 }
679
680 @Override
681 public void primitivesRemoved(PrimitivesRemovedEvent event) {
682 for (OsmPrimitive p: event.getPrimitives()) {
683 if (!p.isNew() && p.getId() == history.getId()) {
684 setLatest(null);
685 }
686 }
687 }
688
689 @Override
690 public void relationMembersChanged(RelationMembersChangedEvent event) {
691 Relation r = event.getRelation();
692 if (!r.isNew() && r.getId() == history.getId()) {
693 setLatest(new HistoryPrimitiveBuilder().build(r));
694 }
695 }
696
697 @Override
698 public void tagsChanged(TagsChangedEvent event) {
699 OsmPrimitive prim = event.getPrimitive();
700 if (!prim.isNew() && prim.getId() == history.getId()) {
701 setLatest(new HistoryPrimitiveBuilder().build(prim));
702 }
703 }
704
705 @Override
706 public void wayNodesChanged(WayNodesChangedEvent event) {
707 Way way = event.getChangedWay();
708 if (!way.isNew() && way.getId() == history.getId()) {
709 setLatest(new HistoryPrimitiveBuilder().build(way));
710 }
711 }
712
713 @Override
714 public void dataChanged(DataChangedEvent event) {
715 OsmPrimitive primitive = event.getDataset().getPrimitiveById(history.getId(), history.getType());
716 HistoryOsmPrimitive latest;
717 if (canShowAsLatest(primitive)) {
718 latest = new HistoryPrimitiveBuilder().build(primitive);
719 } else {
720 latest = null;
721 }
722 setLatest(latest);
723 fireModelChange();
724 }
725
726 @Override
727 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
728 // Irrelevant
729 }
730
731 /* ---------------------------------------------------------------------- */
732 /* LayerChangeListener */
733 /* ---------------------------------------------------------------------- */
734 @Override
735 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
736 if (oldLayer instanceof OsmDataLayer) {
737 OsmDataLayer l = (OsmDataLayer)oldLayer;
738 l.data.removeDataSetListener(this);
739 }
740 if (!(newLayer instanceof OsmDataLayer)) {
741 latest = null;
742 fireModelChange();
743 return;
744 }
745 OsmDataLayer l = (OsmDataLayer)newLayer;
746 l.data.addDataSetListener(this);
747 OsmPrimitive primitive = l.data.getPrimitiveById(history.getId(), history.getType());
748 HistoryOsmPrimitive latest;
749 if (canShowAsLatest(primitive)) {
750 latest = new HistoryPrimitiveBuilder().build(primitive);
751 } else {
752 latest = null;
753 }
754 setLatest(latest);
755 fireModelChange();
756 }
757
758 @Override
759 public void layerAdded(Layer newLayer) {}
760 @Override
761 public void layerRemoved(Layer oldLayer) {}
762
763 /**
764 * Creates a {@link HistoryOsmPrimitive} from a {@link OsmPrimitive}
765 *
766 */
767 static class HistoryPrimitiveBuilder extends AbstractVisitor {
768 private HistoryOsmPrimitive clone;
769
770 @Override
771 public void visit(Node n) {
772 clone = new HistoryNode(n.getId(), n.getVersion(), n.isVisible(), getCurrentUser(), 0, null, n.getCoor(), false);
773 clone.setTags(n.getKeys());
774 }
775
776 @Override
777 public void visit(Relation r) {
778 clone = new HistoryRelation(r.getId(), r.getVersion(), r.isVisible(), getCurrentUser(), 0, null, false);
779 clone.setTags(r.getKeys());
780 HistoryRelation hr = (HistoryRelation)clone;
781 for (RelationMember rm : r.getMembers()) {
782 hr.addMember(new RelationMemberData(rm.getRole(), rm.getType(), rm.getUniqueId()));
783 }
784 }
785
786 @Override
787 public void visit(Way w) {
788 clone = new HistoryWay(w.getId(), w.getVersion(), w.isVisible(), getCurrentUser(), 0, null, false);
789 clone.setTags(w.getKeys());
790 for (Node n: w.getNodes()) {
791 ((HistoryWay)clone).addNode(n.getUniqueId());
792 }
793 }
794
795 private User getCurrentUser() {
796 UserInfo info = JosmUserIdentityManager.getInstance().getUserInfo();
797 return info == null ? User.getAnonymous() : User.createOsmUser(info.getId(), info.getDisplayName());
798 }
799
800 public HistoryOsmPrimitive build(OsmPrimitive primitive) {
801 primitive.accept(this);
802 return clone;
803 }
804 }
805}
Note: See TracBrowser for help on using the repository browser.