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

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

sonar - squid:S2259 - Null pointers should not be dereferenced

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