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

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

see #7505 - Hide redacted version in history browser when loading an .osm file containg primitives that have been redacted meanwhile

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