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

Last change on this file since 5266 was 5266, checked in by bastiK, 12 years ago

fixed majority of javadoc warnings by replacing "{@see" by "{@link"

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