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

Last change on this file since 2254 was 2243, checked in by Gubaer, 15 years ago

fixed #3650: Double-click on items in history dialog should open history
fixed #3649: History dialog does not show moved nodes
fixed #3383: changeset tags in history dialog (partial fix, there's now a link to the server page for browsing changesets)

File size: 22.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.util.ArrayList;
7import java.util.Collections;
8import java.util.HashSet;
9import java.util.Observable;
10import java.util.logging.Logger;
11
12import javax.swing.table.DefaultTableModel;
13
14import org.openstreetmap.josm.data.coor.CoordinateFormat;
15import org.openstreetmap.josm.data.coor.LatLon;
16import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
17import org.openstreetmap.josm.data.osm.history.History;
18import org.openstreetmap.josm.data.osm.history.HistoryNode;
19import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
20import org.openstreetmap.josm.data.osm.history.HistoryRelation;
21import org.openstreetmap.josm.data.osm.history.HistoryWay;
22
23/**
24 * This is the model used by the history browser.
25 *
26 * The state this model manages consists of the following elements:
27 * <ul>
28 * <li>the {@see History} of a specific {@see OsmPrimitive}</li>
29 * <li>a dedicated version in this {@see History} called the {@see PointInTimeType#REFERENCE_POINT_IN_TIME}</li>
30 * <li>another version in this {@see History} called the {@see PointInTimeType#CURRENT_POINT_IN_TIME}</li>
31 * <ul>
32 * {@see HistoryBrowser} always compares the {@see PointInTimeType#REFERENCE_POINT_IN_TIME} with the
33 * {@see PointInTimeType#CURRENT_POINT_IN_TIME}.
34
35 * This model provides various {@see TableModel}s for {@see JTable}s used in {@see HistoryBrowser}, for
36 * instance:
37 * <ul>
38 * <li>{@see #getTagTableModel(PointInTimeType)} replies a {@see TableModel} for the tags of either of
39 * the two selected versions</li>
40 * <li>{@see #getNodeListTableModel(PointInTimeType)} replies a {@see TableModel} for the list of nodes of
41 * the two selected versions (if the current history provides information about a {@see Way}</li>
42 * <li> {@see #getRelationMemberTableModel(PointInTimeType)} replies a {@see TableModel} for the list of relation
43 * members of the two selected versions (if the current history provides information about a {@see Relation}</li>
44 * </ul>
45 *
46 * @see HistoryBrowser
47 */
48public class HistoryBrowserModel extends Observable {
49
50 private static Logger logger = Logger.getLogger(HistoryBrowserModel.class.getName());
51
52 /** the history of an OsmPrimitive */
53 private History history;
54 private HistoryOsmPrimitive reference;
55 private HistoryOsmPrimitive current;
56
57 private VersionTableModel versionTableModel;
58 private TagTableModel currentTagTableModel;
59 private TagTableModel referenceTagTableModel;
60 private NodeListTableModel currentNodeListTableModel;
61 private NodeListTableModel referenceNodeListTableModel;
62 private RelationMemberTableModel currentRelationMemberTableModel;
63 private RelationMemberTableModel referenceRelationMemberTableModel;
64
65 public HistoryBrowserModel() {
66 versionTableModel = new VersionTableModel();
67 currentTagTableModel = new TagTableModel(PointInTimeType.CURRENT_POINT_IN_TIME);
68 referenceTagTableModel = new TagTableModel(PointInTimeType.REFERENCE_POINT_IN_TIME);
69 currentNodeListTableModel = new NodeListTableModel(PointInTimeType.CURRENT_POINT_IN_TIME);
70 referenceNodeListTableModel = new NodeListTableModel(PointInTimeType.REFERENCE_POINT_IN_TIME);
71 currentRelationMemberTableModel = new RelationMemberTableModel(PointInTimeType.CURRENT_POINT_IN_TIME);
72 referenceRelationMemberTableModel = new RelationMemberTableModel(PointInTimeType.REFERENCE_POINT_IN_TIME);
73 }
74
75 public HistoryBrowserModel(History history) {
76 this();
77 setHistory(history);
78 }
79
80 /**
81 * replies the history managed by this model
82 * @return the history
83 */
84 public History getHistory() {
85 return history;
86 }
87
88 /**
89 * sets the history to be managed by this model
90 *
91 * @param history the history
92 *
93 */
94 public void setHistory(History history) {
95 this.history = history;
96 if (history.getNumVersions() > 0) {
97 current = history.getEarliest();
98 reference = history.getEarliest();
99 }
100 initTagTableModels();
101 fireModelChange();
102 }
103
104
105 protected void fireModelChange() {
106 setChanged();
107 notifyObservers();
108 versionTableModel.fireTableDataChanged();
109 }
110
111 /**
112 * Replies the table model to be used in a {@see JTable} which
113 * shows the list of versions in this history.
114 *
115 * @return the table model
116 */
117 public VersionTableModel getVersionTableModel() {
118 return versionTableModel;
119 }
120
121 protected void initTagTableModels() {
122 currentTagTableModel.initKeyList();
123 referenceTagTableModel.initKeyList();
124 }
125
126 protected void initNodeListTabeModels() {
127 currentNodeListTableModel.fireTableDataChanged();
128 referenceNodeListTableModel.fireTableDataChanged();
129 }
130
131 protected void initMemberListTableModels() {
132 currentRelationMemberTableModel.fireTableDataChanged();
133 referenceRelationMemberTableModel.fireTableDataChanged();
134 }
135
136 /**
137 * replies the tag table model for the respective point in time
138 *
139 * @param pointInTimeType the type of the point in time (must not be null)
140 * @return the tag table model
141 * @exception IllegalArgumentException thrown, if pointInTimeType is null
142 */
143 public TagTableModel getTagTableModel(PointInTimeType pointInTimeType) throws IllegalArgumentException {
144 if (pointInTimeType == null)
145 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "pointInTimeType"));
146 if (pointInTimeType.equals(PointInTimeType.CURRENT_POINT_IN_TIME))
147 return currentTagTableModel;
148 else if (pointInTimeType.equals(PointInTimeType.REFERENCE_POINT_IN_TIME))
149 return referenceTagTableModel;
150
151 // should not happen
152 return null;
153 }
154
155 public NodeListTableModel getNodeListTableModel(PointInTimeType pointInTimeType) throws IllegalArgumentException {
156 if (pointInTimeType == null)
157 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "pointInTimeType"));
158 if (pointInTimeType.equals(PointInTimeType.CURRENT_POINT_IN_TIME))
159 return currentNodeListTableModel;
160 else if (pointInTimeType.equals(PointInTimeType.REFERENCE_POINT_IN_TIME))
161 return referenceNodeListTableModel;
162
163 // should not happen
164 return null;
165 }
166
167 public RelationMemberTableModel getRelationMemberTableModel(PointInTimeType pointInTimeType) throws IllegalArgumentException {
168 if (pointInTimeType == null)
169 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "pointInTimeType"));
170 if (pointInTimeType.equals(PointInTimeType.CURRENT_POINT_IN_TIME))
171 return currentRelationMemberTableModel;
172 else if (pointInTimeType.equals(PointInTimeType.REFERENCE_POINT_IN_TIME))
173 return referenceRelationMemberTableModel;
174
175 // should not happen
176 return null;
177 }
178
179 public void setReferencePointInTime(HistoryOsmPrimitive reference) throws IllegalArgumentException, IllegalStateException{
180 if (reference == null)
181 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "reference"));
182 if (history == null)
183 throw new IllegalStateException(tr("History not initialized yet. Failed to set reference primitive."));
184 if (reference.getId() != history.getId())
185 throw new IllegalArgumentException(tr("Failed to set reference. Reference ID {0} does not match history ID {1}.", reference.getId(), history.getId()));
186 HistoryOsmPrimitive primitive = history.getByVersion(reference.getVersion());
187 if (primitive == null)
188 throw new IllegalArgumentException(tr("Failed to set reference. Reference version {0} not available in history.", reference.getVersion()));
189
190 this.reference = reference;
191 initTagTableModels();
192 initNodeListTabeModels();
193 initMemberListTableModels();
194 setChanged();
195 notifyObservers();
196 }
197
198 public void setCurrentPointInTime(HistoryOsmPrimitive current) throws IllegalArgumentException, IllegalStateException{
199 if (current == null)
200 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "current"));
201 if (history == null)
202 throw new IllegalStateException(tr("History not initialized yet. Failed to set current primitive."));
203 if (current.getId() != history.getId())
204 throw new IllegalArgumentException(tr("Failed to set reference. Reference ID {0} does not match history ID {1}.", current.getId(), history.getId()));
205 HistoryOsmPrimitive primitive = history.getByVersion(current.getVersion());
206 if (primitive == null)
207 throw new IllegalArgumentException(tr("Failed to set current primitive. Current version {0} not available in history.", current.getVersion()));
208 this.current = current;
209 initTagTableModels();
210 initNodeListTabeModels();
211 initMemberListTableModels();
212 setChanged();
213 notifyObservers();
214 }
215
216 /**
217 * Replies the history OSM primitive for the {@see PointInTimeType#CURRENT_POINT_IN_TIME}
218 *
219 * @return the history OSM primitive for the {@see PointInTimeType#CURRENT_POINT_IN_TIME} (may be null)
220 */
221 public HistoryOsmPrimitive getCurrentPointInTime() {
222 return getPointInTime(PointInTimeType.CURRENT_POINT_IN_TIME);
223 }
224
225 /**
226 * Replies the history OSM primitive for the {@see PointInTimeType#REFERENCE_POINT_IN_TIME}
227 *
228 * @return the history OSM primitive for the {@see PointInTimeType#REFERENCE_POINT_IN_TIME} (may be null)
229 */
230 public HistoryOsmPrimitive getReferencePointInTime() {
231 return getPointInTime(PointInTimeType.REFERENCE_POINT_IN_TIME);
232 }
233
234 /**
235 * replies the history OSM primitive for a given point in time
236 *
237 * @param type the type of the point in time (must not be null)
238 * @return the respective primitive. Can be null.
239 * @exception IllegalArgumentException thrown, if type is null
240 */
241 public HistoryOsmPrimitive getPointInTime(PointInTimeType type) throws IllegalArgumentException {
242 if (type == null)
243 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "type"));
244 if (type.equals(PointInTimeType.CURRENT_POINT_IN_TIME))
245 return current;
246 else if (type.equals(PointInTimeType.REFERENCE_POINT_IN_TIME))
247 return reference;
248
249 // should not happen
250 return null;
251 }
252
253 /**
254 * The table model for the list of versions in the current history
255 *
256 */
257 public class VersionTableModel extends DefaultTableModel {
258
259 private VersionTableModel() {
260 }
261
262 @Override
263 public int getRowCount() {
264 if (history == null)
265 return 0;
266 return history.getNumVersions();
267 }
268
269 @Override
270 public Object getValueAt(int row, int column) {
271 if(history == null)
272 return null;
273 return history.get(row);
274 }
275
276 @Override
277 public boolean isCellEditable(int row, int column) {
278 return false;
279 }
280
281 public void setReferencePointInTime(int row) {
282 if (history == null) return;
283 if (row < 0 || row > history.getNumVersions()) return;
284 HistoryOsmPrimitive reference = history.get(row);
285 HistoryBrowserModel.this.setReferencePointInTime(reference);
286 }
287
288 public void setCurrentPointInTime(int row) {
289 if (history == null) return;
290 if (row < 0 || row > history.getNumVersions()) return;
291 HistoryOsmPrimitive current = history.get(row);
292 HistoryBrowserModel.this.setCurrentPointInTime(current);
293 }
294
295 public boolean isReferencePointInTime(int row) {
296 if (history == null) return false;
297 if (row < 0 || row > history.getNumVersions()) return false;
298 HistoryOsmPrimitive p = history.get(row);
299 return p.equals(reference);
300 }
301
302 public HistoryOsmPrimitive getPrimitive(int row) {
303 return history.get(row);
304 }
305 }
306
307
308 /**
309 * The table model for the tags of the version at {@see PointInTimeType#REFERENCE_POINT_IN_TIME}
310 * or {@see PointInTimeType#CURRENT_POINT_IN_TIME}
311 *
312 */
313 public class TagTableModel extends DefaultTableModel {
314
315 private ArrayList<String> keys;
316 private PointInTimeType pointInTimeType;
317
318 protected void initKeyList() {
319 HashSet<String> keySet = new HashSet<String>();
320 if (current != null) {
321 keySet.addAll(current.getTags().keySet());
322 }
323 if (reference != null) {
324 keySet.addAll(reference.getTags().keySet());
325 }
326 keys = new ArrayList<String>(keySet);
327 Collections.sort(keys);
328 fireTableDataChanged();
329 }
330
331 protected TagTableModel(PointInTimeType type) {
332 pointInTimeType = type;
333 initKeyList();
334 }
335
336 @Override
337 public int getRowCount() {
338 if (keys == null) return 0;
339 return keys.size();
340 }
341
342 @Override
343 public Object getValueAt(int row, int column) {
344 return keys.get(row);
345 }
346
347 @Override
348 public boolean isCellEditable(int row, int column) {
349 return false;
350 }
351
352 public boolean hasTag(String key) {
353 HistoryOsmPrimitive primitive = getPointInTime(pointInTimeType);
354 if (primitive == null)
355 return false;
356 return primitive.hasTag(key);
357 }
358
359 public String getValue(String key) {
360 HistoryOsmPrimitive primitive = getPointInTime(pointInTimeType);
361 if (primitive == null)
362 return null;
363 return primitive.get(key);
364 }
365
366 public boolean oppositeHasTag(String key) {
367 PointInTimeType opposite = pointInTimeType.opposite();
368 HistoryOsmPrimitive primitive = getPointInTime(opposite);
369 if (primitive == null)
370 return false;
371 return primitive.hasTag(key);
372 }
373
374 public String getOppositeValue(String key) {
375 PointInTimeType opposite = pointInTimeType.opposite();
376 HistoryOsmPrimitive primitive = getPointInTime(opposite);
377 if (primitive == null)
378 return null;
379 return primitive.get(key);
380 }
381
382 public boolean hasSameValueAsOpposite(String key) {
383 String value = getValue(key);
384 String oppositeValue = getOppositeValue(key);
385 if (value == null || oppositeValue == null)
386 return false;
387 return value.equals(oppositeValue);
388 }
389
390 public PointInTimeType getPointInTimeType() {
391 return pointInTimeType;
392 }
393
394 public boolean isCurrentPointInTime() {
395 return pointInTimeType.equals(PointInTimeType.CURRENT_POINT_IN_TIME);
396 }
397
398 public boolean isReferencePointInTime() {
399 return pointInTimeType.equals(PointInTimeType.REFERENCE_POINT_IN_TIME);
400 }
401 }
402
403 /**
404 * The table model for the nodes of the version at {@see PointInTimeType#REFERENCE_POINT_IN_TIME}
405 * or {@see PointInTimeType#CURRENT_POINT_IN_TIME}
406 *
407 */
408 public class NodeListTableModel extends DefaultTableModel {
409
410 private PointInTimeType pointInTimeType;
411
412 private NodeListTableModel(PointInTimeType pointInTimeType) {
413 this.pointInTimeType = pointInTimeType;
414 }
415
416 @Override
417 public int getRowCount() {
418 int n = 0;
419 if (current != null && current.getType().equals(OsmPrimitiveType.WAY)) {
420 n = ((HistoryWay)current).getNumNodes();
421 }
422 if (reference != null && reference.getType().equals(OsmPrimitiveType.WAY)) {
423 n = Math.max(n,((HistoryWay)reference).getNumNodes());
424 }
425 return n;
426 }
427
428 protected HistoryWay getWay() {
429 if (pointInTimeType.equals(PointInTimeType.CURRENT_POINT_IN_TIME)) {
430 if (! current.getType().equals(OsmPrimitiveType.WAY))
431 return null;
432 return (HistoryWay)current;
433 }
434 if (pointInTimeType.equals(PointInTimeType.REFERENCE_POINT_IN_TIME)) {
435 if (! reference.getType().equals(OsmPrimitiveType.WAY))
436 return null;
437 return (HistoryWay)reference;
438 }
439
440 // should not happen
441 return null;
442 }
443
444 protected HistoryWay getOppositeWay() {
445 PointInTimeType opposite = pointInTimeType.opposite();
446 if (opposite.equals(PointInTimeType.CURRENT_POINT_IN_TIME)) {
447 if (! current.getType().equals(OsmPrimitiveType.WAY))
448 return null;
449 return (HistoryWay)current;
450 }
451 if (opposite.equals(PointInTimeType.REFERENCE_POINT_IN_TIME)) {
452 if (! reference.getType().equals(OsmPrimitiveType.WAY))
453 return null;
454 return (HistoryWay)reference;
455 }
456
457 // should not happen
458 return null;
459 }
460
461 @Override
462 public Object getValueAt(int row, int column) {
463 HistoryWay way = getWay();
464 if (way == null)
465 return null;
466 if (row >= way.getNumNodes())
467 return null;
468 return way.getNodes().get(row);
469 }
470
471 @Override
472 public boolean isCellEditable(int row, int column) {
473 return false;
474 }
475
476 public boolean isSameInOppositeWay(int row) {
477 HistoryWay thisWay = getWay();
478 HistoryWay oppositeWay = getOppositeWay();
479 if (thisWay == null || oppositeWay == null)
480 return false;
481 if (row >= oppositeWay.getNumNodes())
482 return false;
483 return thisWay.getNodeId(row) == oppositeWay.getNodeId(row);
484 }
485
486 public boolean isInOppositeWay(int row) {
487 HistoryWay thisWay = getWay();
488 HistoryWay oppositeWay = getOppositeWay();
489 if (thisWay == null || oppositeWay == null)
490 return false;
491 return oppositeWay.getNodes().contains(thisWay.getNodeId(row));
492 }
493 }
494
495 /**
496 * The table model for the relation members of the version at {@see PointInTimeType#REFERENCE_POINT_IN_TIME}
497 * or {@see PointInTimeType#CURRENT_POINT_IN_TIME}
498 *
499 */
500
501 public class RelationMemberTableModel extends DefaultTableModel {
502
503 private PointInTimeType pointInTimeType;
504
505 private RelationMemberTableModel(PointInTimeType pointInTimeType) {
506 this.pointInTimeType = pointInTimeType;
507 }
508
509 @Override
510 public int getRowCount() {
511 int n = 0;
512 if (current != null && current.getType().equals(OsmPrimitiveType.RELATION)) {
513 n = ((HistoryRelation)current).getNumMembers();
514 }
515 if (reference != null && reference.getType().equals(OsmPrimitiveType.RELATION)) {
516 n = Math.max(n,((HistoryRelation)reference).getNumMembers());
517 }
518 return n;
519 }
520
521 protected HistoryRelation getRelation() {
522 if (pointInTimeType.equals(PointInTimeType.CURRENT_POINT_IN_TIME)) {
523 if (! current.getType().equals(OsmPrimitiveType.RELATION))
524 return null;
525 return (HistoryRelation)current;
526 }
527 if (pointInTimeType.equals(PointInTimeType.REFERENCE_POINT_IN_TIME)) {
528 if (! reference.getType().equals(OsmPrimitiveType.RELATION))
529 return null;
530 return (HistoryRelation)reference;
531 }
532
533 // should not happen
534 return null;
535 }
536
537 protected HistoryRelation getOppositeRelation() {
538 PointInTimeType opposite = pointInTimeType.opposite();
539 if (opposite.equals(PointInTimeType.CURRENT_POINT_IN_TIME)) {
540 if (! current.getType().equals(OsmPrimitiveType.RELATION))
541 return null;
542 return (HistoryRelation)current;
543 }
544 if (opposite.equals(PointInTimeType.REFERENCE_POINT_IN_TIME)) {
545 if (! reference.getType().equals(OsmPrimitiveType.RELATION))
546 return null;
547 return (HistoryRelation)reference;
548 }
549
550 // should not happen
551 return null;
552 }
553
554 @Override
555 public Object getValueAt(int row, int column) {
556 HistoryRelation relation = getRelation();
557 if (relation == null)
558 return null;
559 if (row >= relation.getNumMembers())
560 return null;
561 return relation.getMembers().get(row);
562 }
563
564 @Override
565 public boolean isCellEditable(int row, int column) {
566 return false;
567 }
568
569 public boolean isSameInOppositeWay(int row) {
570 HistoryRelation thisRelation = getRelation();
571 HistoryRelation oppositeRelation = getOppositeRelation();
572 if (thisRelation == null || oppositeRelation == null)
573 return false;
574 if (row >= oppositeRelation.getNumMembers())
575 return false;
576 return
577 thisRelation.getMembers().get(row).getPrimitiveId() == oppositeRelation.getMembers().get(row).getPrimitiveId()
578 && thisRelation.getMembers().get(row).getRole().equals(oppositeRelation.getMembers().get(row).getRole());
579 }
580
581 public boolean isInOppositeWay(int row) {
582 HistoryRelation thisRelation = getRelation();
583 HistoryRelation oppositeRelation = getOppositeRelation();
584 if (thisRelation == null || oppositeRelation == null)
585 return false;
586 return oppositeRelation.getMembers().contains(thisRelation.getMembers().get(row));
587 }
588 }
589}
Note: See TracBrowser for help on using the repository browser.