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

Last change on this file since 4406 was 4406, checked in by simon04, 13 years ago

fix #6776 - sensible pre-selection of versions in history dialog

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