source: josm/trunk/src/org/openstreetmap/josm/gui/conflict/pair/ListMergeModel.java@ 5299

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

see #4899, see #7266, see #7333: Resolved NPE in conflict manager when copying a member created by merging two layers

  • Property svn:eol-style set to native
File size: 30.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.conflict.pair;
3
4import static org.openstreetmap.josm.gui.conflict.pair.ComparePairType.MY_WITH_MERGED;
5import static org.openstreetmap.josm.gui.conflict.pair.ComparePairType.MY_WITH_THEIR;
6import static org.openstreetmap.josm.gui.conflict.pair.ComparePairType.THEIR_WITH_MERGED;
7import static org.openstreetmap.josm.gui.conflict.pair.ListRole.MERGED_ENTRIES;
8import static org.openstreetmap.josm.gui.conflict.pair.ListRole.MY_ENTRIES;
9import static org.openstreetmap.josm.gui.conflict.pair.ListRole.THEIR_ENTRIES;
10import static org.openstreetmap.josm.tools.I18n.tr;
11
12import java.beans.PropertyChangeEvent;
13import java.beans.PropertyChangeListener;
14import java.util.ArrayList;
15import java.util.HashMap;
16import java.util.List;
17import java.util.Map;
18import java.util.Observable;
19
20import javax.swing.AbstractListModel;
21import javax.swing.ComboBoxModel;
22import javax.swing.DefaultListSelectionModel;
23import javax.swing.JOptionPane;
24import javax.swing.JTable;
25import javax.swing.ListSelectionModel;
26import javax.swing.table.DefaultTableModel;
27import javax.swing.table.TableModel;
28
29import org.openstreetmap.josm.Main;
30import org.openstreetmap.josm.data.osm.DataSet;
31import org.openstreetmap.josm.data.osm.OsmPrimitive;
32import org.openstreetmap.josm.data.osm.PrimitiveId;
33import org.openstreetmap.josm.gui.HelpAwareOptionPane;
34import org.openstreetmap.josm.gui.help.HelpUtil;
35import org.openstreetmap.josm.gui.widgets.OsmPrimitivesTableModel;
36import org.openstreetmap.josm.tools.CheckParameterUtil;
37
38/**
39 * ListMergeModel is a model for interactively comparing and merging two list of entries
40 * of type T. It maintains three lists of entries of type T:
41 * <ol>
42 * <li>the list of <em>my</em> entries</li>
43 * <li>the list of <em>their</em> entries</li>
44 * <li>the list of <em>merged</em> entries</li>
45 * </ol>
46 *
47 * A ListMergeModel is a factory for three {@link TableModel}s and three {@link ListSelectionModel}s:
48 * <ol>
49 * <li>the table model and the list selection for for a {@link JTable} which shows my entries.
50 * See {@link #getMyTableModel()}</li> and {@link ListMergeModel#getMySelectionModel()}</li>
51 * <li>dito for their entries and merged entries</li>
52 * </ol>
53 *
54 * A ListMergeModel can be ''frozen''. If it's frozen, it doesn't accept additional merge
55 * decisions. {@link PropertyChangeListener}s can register for property value changes of
56 * {@link #PROP_FROZEN}.
57 *
58 * ListMergeModel is an abstract class. Three methods have to be implemented by subclasses:
59 * <ul>
60 * <li>{@link ListMergeModel#cloneEntryForMergedList(Object)} - clones an entry of type T</li>
61 * <li>{@link ListMergeModel#isEqualEntry(Object, Object)} - checks whether two entries are equals </li>
62 * <li>{@link ListMergeModel#setValueAt(DefaultTableModel, Object, int, int)} - handles values edited in
63 * a JTable, dispatched from {@link TableModel#setValueAt(Object, int, int)} </li>
64 * </ul>
65 * A ListMergeModel is used in combination with a {@link ListMerger}.
66 *
67 * @param <T> the type of the list entries
68 * @see ListMerger
69 */
70public abstract class ListMergeModel<T extends PrimitiveId> extends Observable {
71 public static final String FROZEN_PROP = ListMergeModel.class.getName() + ".frozen";
72
73 private static final int MAX_DELETED_PRIMITIVE_IN_DIALOG = 5;
74
75 protected HashMap<ListRole, ArrayList<T>> entries;
76
77 protected EntriesTableModel myEntriesTableModel;
78 protected EntriesTableModel theirEntriesTableModel;
79 protected EntriesTableModel mergedEntriesTableModel;
80
81 protected EntriesSelectionModel myEntriesSelectionModel;
82 protected EntriesSelectionModel theirEntriesSelectionModel;
83 protected EntriesSelectionModel mergedEntriesSelectionModel;
84
85 private final List<PropertyChangeListener> listeners;
86 private boolean isFrozen = false;
87 private final ComparePairListModel comparePairListModel;
88
89 private DataSet myDataset;
90 private Map<PrimitiveId, PrimitiveId> mergedMap;
91
92 /**
93 * Creates a clone of an entry of type T suitable to be included in the
94 * list of merged entries
95 *
96 * @param entry the entry
97 * @return the cloned entry
98 */
99 protected abstract T cloneEntryForMergedList(T entry);
100
101 /**
102 * checks whether two entries are equal. This is not necessarily the same as
103 * e1.equals(e2).
104 *
105 * @param e1 the first entry
106 * @param e2 the second entry
107 * @return true, if the entries are equal, false otherwise.
108 */
109 public abstract boolean isEqualEntry(T e1, T e2);
110
111 /**
112 * Handles method dispatches from {@link TableModel#setValueAt(Object, int, int)}.
113 *
114 * @param model the table model
115 * @param value the value to be set
116 * @param row the row index
117 * @param col the column index
118 *
119 * @see TableModel#setValueAt(Object, int, int)
120 */
121 protected abstract void setValueAt(DefaultTableModel model, Object value, int row, int col);
122
123 /**
124 *
125 * @param entry
126 * @return Primitive from my dataset referenced by entry
127 */
128 protected OsmPrimitive getMyPrimitive(T entry) {
129 return getMyPrimitiveDefault(entry);
130 }
131
132 protected final OsmPrimitive getMyPrimitiveDefault(PrimitiveId entry) {
133 OsmPrimitive result = myDataset.getPrimitiveById(entry);
134 if (result == null && mergedMap != null) {
135 PrimitiveId id = mergedMap.get(entry);
136 if (id == null && entry instanceof OsmPrimitive) {
137 id = mergedMap.get(((OsmPrimitive)entry).getPrimitiveId());
138 }
139 if (id != null) {
140 result = myDataset.getPrimitiveById(id);
141 }
142 }
143 return result;
144 }
145
146 protected void buildMyEntriesTableModel() {
147 myEntriesTableModel = new EntriesTableModel(MY_ENTRIES);
148 }
149
150 protected void buildTheirEntriesTableModel() {
151 theirEntriesTableModel = new EntriesTableModel(THEIR_ENTRIES);
152 }
153
154 protected void buildMergedEntriesTableModel() {
155 mergedEntriesTableModel = new EntriesTableModel(MERGED_ENTRIES);
156 }
157
158 protected List<T> getMergedEntries() {
159 return entries.get(MERGED_ENTRIES);
160 }
161
162 protected List<T> getMyEntries() {
163 return entries.get(MY_ENTRIES);
164 }
165
166 protected List<T> getTheirEntries() {
167 return entries.get(THEIR_ENTRIES);
168 }
169
170 public int getMyEntriesSize() {
171 return getMyEntries().size();
172 }
173
174 public int getMergedEntriesSize() {
175 return getMergedEntries().size();
176 }
177
178 public int getTheirEntriesSize() {
179 return getTheirEntries().size();
180 }
181
182 public ListMergeModel() {
183 entries = new HashMap<ListRole, ArrayList<T>>();
184 for (ListRole role : ListRole.values()) {
185 entries.put(role, new ArrayList<T>());
186 }
187
188 buildMyEntriesTableModel();
189 buildTheirEntriesTableModel();
190 buildMergedEntriesTableModel();
191
192 myEntriesSelectionModel = new EntriesSelectionModel(entries.get(MY_ENTRIES));
193 theirEntriesSelectionModel = new EntriesSelectionModel(entries.get(THEIR_ENTRIES));
194 mergedEntriesSelectionModel = new EntriesSelectionModel(entries.get(MERGED_ENTRIES));
195
196 listeners = new ArrayList<PropertyChangeListener>();
197 comparePairListModel = new ComparePairListModel();
198
199 setFrozen(true);
200 }
201
202 public void addPropertyChangeListener(PropertyChangeListener listener) {
203 synchronized(listeners) {
204 if (listener != null && ! listeners.contains(listener)) {
205 listeners.add(listener);
206 }
207 }
208 }
209
210 public void removePropertyChangeListener(PropertyChangeListener listener) {
211 synchronized(listeners) {
212 if (listener != null && listeners.contains(listener)) {
213 listeners.remove(listener);
214 }
215 }
216 }
217
218 protected void fireFrozenChanged(boolean oldValue, boolean newValue) {
219 synchronized(listeners) {
220 PropertyChangeEvent evt = new PropertyChangeEvent(this, FROZEN_PROP, oldValue, newValue);
221 for (PropertyChangeListener listener: listeners) {
222 listener.propertyChange(evt);
223 }
224 }
225 }
226
227 public void setFrozen(boolean isFrozen) {
228 boolean oldValue = this.isFrozen;
229 this.isFrozen = isFrozen;
230 fireFrozenChanged(oldValue, this.isFrozen);
231 }
232
233 public boolean isFrozen() {
234 return isFrozen;
235 }
236
237 public OsmPrimitivesTableModel getMyTableModel() {
238 return myEntriesTableModel;
239 }
240
241 public OsmPrimitivesTableModel getTheirTableModel() {
242 return theirEntriesTableModel;
243 }
244
245 public OsmPrimitivesTableModel getMergedTableModel() {
246 return mergedEntriesTableModel;
247 }
248
249 public EntriesSelectionModel getMySelectionModel() {
250 return myEntriesSelectionModel;
251 }
252
253 public EntriesSelectionModel getTheirSelectionModel() {
254 return theirEntriesSelectionModel;
255 }
256
257 public EntriesSelectionModel getMergedSelectionModel() {
258 return mergedEntriesSelectionModel;
259 }
260
261 protected void fireModelDataChanged() {
262 myEntriesTableModel.fireTableDataChanged();
263 theirEntriesTableModel.fireTableDataChanged();
264 mergedEntriesTableModel.fireTableDataChanged();
265 setChanged();
266 notifyObservers();
267 }
268
269 protected void copyToTop(ListRole role, int []rows) {
270 copy(role, rows, 0);
271 mergedEntriesSelectionModel.setSelectionInterval(0, rows.length -1);
272 }
273
274 /**
275 * Copies the nodes given by indices in rows from the list of my nodes to the
276 * list of merged nodes. Inserts the nodes at the top of the list of merged
277 * nodes.
278 *
279 * @param rows the indices
280 */
281 public void copyMyToTop(int [] rows) {
282 copyToTop(MY_ENTRIES, rows);
283 }
284
285 /**
286 * Copies the nodes given by indices in rows from the list of their nodes to the
287 * list of merged nodes. Inserts the nodes at the top of the list of merged
288 * nodes.
289 *
290 * @param rows the indices
291 */
292 public void copyTheirToTop(int [] rows) {
293 copyToTop(THEIR_ENTRIES, rows);
294 }
295
296 /**
297 * Copies the nodes given by indices in rows from the list of nodes in source to the
298 * list of merged nodes. Inserts the nodes at the end of the list of merged
299 * nodes.
300 *
301 * @param source the list of nodes to copy from
302 * @param rows the indices
303 */
304
305 public void copyToEnd(ListRole source, int [] rows) {
306 copy(source, rows, getMergedEntriesSize());
307 mergedEntriesSelectionModel.setSelectionInterval(getMergedEntriesSize()-rows.length, getMergedEntriesSize() -1);
308
309 }
310
311 /**
312 * Copies the nodes given by indices in rows from the list of my nodes to the
313 * list of merged nodes. Inserts the nodes at the end of the list of merged
314 * nodes.
315 *
316 * @param rows the indices
317 */
318 public void copyMyToEnd(int [] rows) {
319 copyToEnd(MY_ENTRIES, rows);
320 }
321
322 /**
323 * Copies the nodes given by indices in rows from the list of their nodes to the
324 * list of merged nodes. Inserts the nodes at the end of the list of merged
325 * nodes.
326 *
327 * @param rows the indices
328 */
329 public void copyTheirToEnd(int [] rows) {
330 copyToEnd(THEIR_ENTRIES, rows);
331 }
332
333 public void clearMerged() {
334 getMergedEntries().clear();
335 fireModelDataChanged();
336 }
337
338 protected final void initPopulate(OsmPrimitive my, OsmPrimitive their, Map<PrimitiveId, PrimitiveId> mergedMap) {
339 CheckParameterUtil.ensureParameterNotNull(my, "my");
340 CheckParameterUtil.ensureParameterNotNull(their, "their");
341 this.myDataset = my.getDataSet();
342 this.mergedMap = mergedMap;
343 getMergedEntries().clear();
344 getMyEntries().clear();
345 getTheirEntries().clear();
346 }
347
348 protected void alertCopyFailedForDeletedPrimitives(List<PrimitiveId> deletedIds) {
349 List<String> items = new ArrayList<String>();
350 for (int i=0; i<Math.min(MAX_DELETED_PRIMITIVE_IN_DIALOG, deletedIds.size()); i++) {
351 items.add(deletedIds.get(i).toString());
352 }
353 if (deletedIds.size() > MAX_DELETED_PRIMITIVE_IN_DIALOG) {
354 items.add(tr("{0} more...", deletedIds.size() - MAX_DELETED_PRIMITIVE_IN_DIALOG));
355 }
356 StringBuffer sb = new StringBuffer();
357 sb.append("<html>");
358 sb.append(tr("The following objects could not be copied to the target object<br>because they are deleted in the target dataset:"));
359 sb.append("<ul>");
360 for (String item: items) {
361 sb.append("<li>").append(item).append("</li>");
362 }
363 sb.append("</ul>");
364 sb.append("</html>");
365 HelpAwareOptionPane.showOptionDialog(
366 Main.parent,
367 sb.toString(),
368 tr("Merging deleted objects failed"),
369 JOptionPane.WARNING_MESSAGE,
370 HelpUtil.ht("/Dialog/Conflict#MergingDeletedPrimitivesFailed")
371 );
372 }
373
374 private void copy(ListRole sourceRole, int[] rows, int position) {
375 if (position < 0 || position > getMergedEntriesSize())
376 throw new IllegalArgumentException();
377 List<T> newItems = new ArrayList<T>(rows.length);
378 List<T> source = entries.get(sourceRole);
379 List<PrimitiveId> deletedIds = new ArrayList<PrimitiveId>();
380 for (int row: rows) {
381 T entry = source.get(row);
382 OsmPrimitive primitive = getMyPrimitive(entry);
383 if (!primitive.isDeleted()) {
384 T clone = cloneEntryForMergedList(entry);
385 newItems.add(clone);
386 } else {
387 deletedIds.add(primitive.getPrimitiveId());
388 }
389 }
390 getMergedEntries().addAll(position, newItems);
391 fireModelDataChanged();
392 if (!deletedIds.isEmpty()) {
393 alertCopyFailedForDeletedPrimitives(deletedIds);
394 }
395 }
396
397 public void copyAll(ListRole source) {
398 getMergedEntries().clear();
399
400 int[] rows = new int[entries.get(source).size()];
401 for (int i=0; i<rows.length; i++) {
402 rows[i] = i;
403 }
404 copy(source, rows, 0);
405 }
406
407 /**
408 * Copies the nodes given by indices in rows from the list of nodes <code>source</code> to the
409 * list of merged nodes. Inserts the nodes before row given by current.
410 *
411 * @param source the list of nodes to copy from
412 * @param rows the indices
413 * @param current the row index before which the nodes are inserted
414 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
415 *
416 */
417 protected void copyBeforeCurrent(ListRole source, int [] rows, int current) {
418 copy(source, rows, current);
419 mergedEntriesSelectionModel.setSelectionInterval(current, current + rows.length-1);
420 }
421
422 /**
423 * Copies the nodes given by indices in rows from the list of my nodes to the
424 * list of merged nodes. Inserts the nodes before row given by current.
425 *
426 * @param rows the indices
427 * @param current the row index before which the nodes are inserted
428 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
429 *
430 */
431 public void copyMyBeforeCurrent(int [] rows, int current) {
432 copyBeforeCurrent(MY_ENTRIES,rows,current);
433 }
434
435 /**
436 * Copies the nodes given by indices in rows from the list of their nodes to the
437 * list of merged nodes. Inserts the nodes before row given by current.
438 *
439 * @param rows the indices
440 * @param current the row index before which the nodes are inserted
441 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
442 *
443 */
444 public void copyTheirBeforeCurrent(int [] rows, int current) {
445 copyBeforeCurrent(THEIR_ENTRIES,rows,current);
446 }
447
448 /**
449 * Copies the nodes given by indices in rows from the list of nodes <code>source</code> to the
450 * list of merged nodes. Inserts the nodes after the row given by current.
451 *
452 * @param source the list of nodes to copy from
453 * @param rows the indices
454 * @param current the row index after which the nodes are inserted
455 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
456 *
457 */
458 protected void copyAfterCurrent(ListRole source, int [] rows, int current) {
459 copy(source, rows, current + 1);
460 mergedEntriesSelectionModel.setSelectionInterval(current+1, current + rows.length-1);
461 notifyObservers();
462 }
463
464 /**
465 * Copies the nodes given by indices in rows from the list of my nodes to the
466 * list of merged nodes. Inserts the nodes after the row given by current.
467 *
468 * @param rows the indices
469 * @param current the row index after which the nodes are inserted
470 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
471 *
472 */
473 public void copyMyAfterCurrent(int [] rows, int current) {
474 copyAfterCurrent(MY_ENTRIES, rows, current);
475 }
476
477 /**
478 * Copies the nodes given by indices in rows from the list of my nodes to the
479 * list of merged nodes. Inserts the nodes after the row given by current.
480 *
481 * @param rows the indices
482 * @param current the row index after which the nodes are inserted
483 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
484 *
485 */
486 public void copyTheirAfterCurrent(int [] rows, int current) {
487 copyAfterCurrent(THEIR_ENTRIES, rows, current);
488 }
489
490 /**
491 * Moves the nodes given by indices in rows up by one position in the list
492 * of merged nodes.
493 *
494 * @param rows the indices
495 *
496 */
497 public void moveUpMerged(int [] rows) {
498 if (rows == null || rows.length == 0)
499 return;
500 if (rows[0] == 0)
501 // can't move up
502 return;
503 List<T> mergedEntries = getMergedEntries();
504 for (int row: rows) {
505 T n = mergedEntries.get(row);
506 mergedEntries.remove(row);
507 mergedEntries.add(row -1, n);
508 }
509 fireModelDataChanged();
510 notifyObservers();
511 mergedEntriesSelectionModel.clearSelection();
512 for (int row: rows) {
513 mergedEntriesSelectionModel.addSelectionInterval(row-1, row-1);
514 }
515 }
516
517 /**
518 * Moves the nodes given by indices in rows down by one position in the list
519 * of merged nodes.
520 *
521 * @param rows the indices
522 */
523 public void moveDownMerged(int [] rows) {
524 if (rows == null || rows.length == 0)
525 return;
526 List<T> mergedEntries = getMergedEntries();
527 if (rows[rows.length -1] == mergedEntries.size() -1)
528 // can't move down
529 return;
530 for (int i = rows.length-1; i>=0;i--) {
531 int row = rows[i];
532 T n = mergedEntries.get(row);
533 mergedEntries.remove(row);
534 mergedEntries.add(row +1, n);
535 }
536 fireModelDataChanged();
537 notifyObservers();
538 mergedEntriesSelectionModel.clearSelection();
539 for (int row: rows) {
540 mergedEntriesSelectionModel.addSelectionInterval(row+1, row+1);
541 }
542 }
543
544 /**
545 * Removes the nodes given by indices in rows from the list
546 * of merged nodes.
547 *
548 * @param rows the indices
549 */
550 public void removeMerged(int [] rows) {
551 if (rows == null || rows.length == 0)
552 return;
553
554 List<T> mergedEntries = getMergedEntries();
555
556 for (int i = rows.length-1; i>=0;i--) {
557 mergedEntries.remove(rows[i]);
558 }
559 fireModelDataChanged();
560 notifyObservers();
561 mergedEntriesSelectionModel.clearSelection();
562 }
563
564 /**
565 * Replies true if the list of my entries and the list of their
566 * entries are equal
567 *
568 * @return true, if the lists are equal; false otherwise
569 */
570 protected boolean myAndTheirEntriesEqual() {
571
572 if (getMyEntriesSize() != getTheirEntriesSize())
573 return false;
574 for (int i=0; i < getMyEntriesSize(); i++) {
575 if (! isEqualEntry(getMyEntries().get(i), getTheirEntries().get(i)))
576 return false;
577 }
578 return true;
579 }
580
581 /**
582 * This an adapter between a {@link JTable} and one of the three entry lists
583 * in the role {@link ListRole} managed by the {@link ListMergeModel}.
584 *
585 * From the point of view of the {@link JTable} it is a {@link TableModel}.
586 *
587 * @param <T>
588 * @see ListMergeModel#getMyTableModel()
589 * @see ListMergeModel#getTheirTableModel()
590 * @see ListMergeModel#getMergedTableModel()
591 */
592 public class EntriesTableModel extends DefaultTableModel implements OsmPrimitivesTableModel {
593 private final ListRole role;
594
595 /**
596 *
597 * @param role the role
598 */
599 public EntriesTableModel(ListRole role) {
600 this.role = role;
601 }
602
603 @Override
604 public int getRowCount() {
605 int count = Math.max(getMyEntries().size(), getMergedEntries().size());
606 count = Math.max(count, getTheirEntries().size());
607 return count;
608 }
609
610 @Override
611 public Object getValueAt(int row, int column) {
612 if (row < entries.get(role).size())
613 return entries.get(role).get(row);
614 return null;
615 }
616
617 @Override
618 public boolean isCellEditable(int row, int column) {
619 return false;
620 }
621
622 @Override
623 public void setValueAt(Object value, int row, int col) {
624 ListMergeModel.this.setValueAt(this, value,row,col);
625 }
626
627 public ListMergeModel<T> getListMergeModel() {
628 return ListMergeModel.this;
629 }
630
631 /**
632 * replies true if the {@link ListRole} of this {@link EntriesTableModel}
633 * participates in the current {@link ComparePairType}
634 *
635 * @return true, if the if the {@link ListRole} of this {@link EntriesTableModel}
636 * participates in the current {@link ComparePairType}
637 *
638 * @see ComparePairListModel#getSelectedComparePair()
639 */
640 public boolean isParticipatingInCurrentComparePair() {
641 return getComparePairListModel()
642 .getSelectedComparePair()
643 .isParticipatingIn(role);
644 }
645
646 /**
647 * replies true if the entry at <code>row</code> is equal to the entry at the
648 * same position in the opposite list of the current {@link ComparePairType}.
649 *
650 * @param row the row number
651 * @return true if the entry at <code>row</code> is equal to the entry at the
652 * same position in the opposite list of the current {@link ComparePairType}
653 * @exception IllegalStateException thrown, if this model is not participating in the
654 * current {@link ComparePairType}
655 * @see ComparePairType#getOppositeRole(ListRole)
656 * @see #getRole()
657 * @see #getOppositeEntries()
658 */
659 public boolean isSamePositionInOppositeList(int row) {
660 if (!isParticipatingInCurrentComparePair())
661 throw new IllegalStateException(tr("List in role {0} is currently not participating in a compare pair.", role.toString()));
662 if (row >= getEntries().size()) return false;
663 if (row >= getOppositeEntries().size()) return false;
664
665 T e1 = getEntries().get(row);
666 T e2 = getOppositeEntries().get(row);
667 return isEqualEntry(e1, e2);
668 }
669
670 /**
671 * replies true if the entry at the current position is present in the opposite list
672 * of the current {@link ComparePairType}.
673 *
674 * @param row the current row
675 * @return true if the entry at the current position is present in the opposite list
676 * of the current {@link ComparePairType}.
677 * @exception IllegalStateException thrown, if this model is not participating in the
678 * current {@link ComparePairType}
679 * @see ComparePairType#getOppositeRole(ListRole)
680 * @see #getRole()
681 * @see #getOppositeEntries()
682 */
683 public boolean isIncludedInOppositeList(int row) {
684 if (!isParticipatingInCurrentComparePair())
685 throw new IllegalStateException(tr("List in role {0} is currently not participating in a compare pair.", role.toString()));
686
687 if (row >= getEntries().size()) return false;
688 T e1 = getEntries().get(row);
689 for (T e2: getOppositeEntries()) {
690 if (isEqualEntry(e1, e2)) return true;
691 }
692 return false;
693 }
694
695 protected ArrayList<T> getEntries() {
696 return entries.get(role);
697 }
698
699 /**
700 * replies the opposite list of entries with respect to the current {@link ComparePairType}
701 *
702 * @return the opposite list of entries
703 */
704 protected ArrayList<T> getOppositeEntries() {
705 ListRole opposite = getComparePairListModel().getSelectedComparePair().getOppositeRole(role);
706 return entries.get(opposite);
707 }
708
709 public ListRole getRole() {
710 return role;
711 }
712
713 @Override
714 public OsmPrimitive getReferredPrimitive(int idx) {
715 return (OsmPrimitive) getValueAt(idx, 1);
716 }
717 }
718
719 /**
720 * This is the selection model to be used in a {@link JTable} which displays
721 * an entry list managed by {@link ListMergeModel}.
722 *
723 * The model ensures that only rows displaying an entry in the entry list
724 * can be selected. "Empty" rows can't be selected.
725 *
726 * @see ListMergeModel#getMySelectionModel()
727 * @see ListMergeModel#getMergedSelectionModel()
728 * @see ListMergeModel#getTheirSelectionModel()
729 *
730 */
731 protected class EntriesSelectionModel extends DefaultListSelectionModel {
732 private final ArrayList<T> entries;
733
734 public EntriesSelectionModel(ArrayList<T> nodes) {
735 this.entries = nodes;
736 }
737
738 @Override
739 public void addSelectionInterval(int index0, int index1) {
740 if (entries.isEmpty()) return;
741 if (index0 > entries.size() - 1) return;
742 index0 = Math.min(entries.size()-1, index0);
743 index1 = Math.min(entries.size()-1, index1);
744 super.addSelectionInterval(index0, index1);
745 }
746
747 @Override
748 public void insertIndexInterval(int index, int length, boolean before) {
749 if (entries.isEmpty()) return;
750 if (before) {
751 int newindex = Math.min(entries.size()-1, index);
752 if (newindex < index - length) return;
753 length = length - (index - newindex);
754 super.insertIndexInterval(newindex, length, before);
755 } else {
756 if (index > entries.size() -1) return;
757 length = Math.min(entries.size()-1 - index, length);
758 super.insertIndexInterval(index, length, before);
759 }
760 }
761
762 @Override
763 public void moveLeadSelectionIndex(int leadIndex) {
764 if (entries.isEmpty()) return;
765 leadIndex = Math.max(0, leadIndex);
766 leadIndex = Math.min(entries.size() - 1, leadIndex);
767 super.moveLeadSelectionIndex(leadIndex);
768 }
769
770 @Override
771 public void removeIndexInterval(int index0, int index1) {
772 if (entries.isEmpty()) return;
773 index0 = Math.max(0, index0);
774 index0 = Math.min(entries.size() - 1, index0);
775
776 index1 = Math.max(0, index1);
777 index1 = Math.min(entries.size() - 1, index1);
778 super.removeIndexInterval(index0, index1);
779 }
780
781 @Override
782 public void removeSelectionInterval(int index0, int index1) {
783 if (entries.isEmpty()) return;
784 index0 = Math.max(0, index0);
785 index0 = Math.min(entries.size() - 1, index0);
786
787 index1 = Math.max(0, index1);
788 index1 = Math.min(entries.size() - 1, index1);
789 super.removeSelectionInterval(index0, index1);
790 }
791
792 @Override
793 public void setAnchorSelectionIndex(int anchorIndex) {
794 if (entries.isEmpty()) return;
795 anchorIndex = Math.min(entries.size() - 1, anchorIndex);
796 super.setAnchorSelectionIndex(anchorIndex);
797 }
798
799 @Override
800 public void setLeadSelectionIndex(int leadIndex) {
801 if (entries.isEmpty()) return;
802 leadIndex = Math.min(entries.size() - 1, leadIndex);
803 super.setLeadSelectionIndex(leadIndex);
804 }
805
806 @Override
807 public void setSelectionInterval(int index0, int index1) {
808 if (entries.isEmpty()) return;
809 index0 = Math.max(0, index0);
810 index0 = Math.min(entries.size() - 1, index0);
811
812 index1 = Math.max(0, index1);
813 index1 = Math.min(entries.size() - 1, index1);
814
815 super.setSelectionInterval(index0, index1);
816 }
817 }
818
819 public ComparePairListModel getComparePairListModel() {
820 return this.comparePairListModel;
821 }
822
823 public class ComparePairListModel extends AbstractListModel implements ComboBoxModel {
824
825 private int selectedIdx;
826 private final ArrayList<ComparePairType> compareModes;
827
828 public ComparePairListModel() {
829 this.compareModes = new ArrayList<ComparePairType>();
830 compareModes.add(MY_WITH_THEIR);
831 compareModes.add(MY_WITH_MERGED);
832 compareModes.add(THEIR_WITH_MERGED);
833 selectedIdx = 0;
834 }
835
836 public Object getElementAt(int index) {
837 if (index < compareModes.size())
838 return compareModes.get(index);
839 throw new IllegalArgumentException(tr("Unexpected value of parameter ''index''. Got {0}.", index));
840 }
841
842 public int getSize() {
843 return compareModes.size();
844 }
845
846 public Object getSelectedItem() {
847 return compareModes.get(selectedIdx);
848 }
849
850 public void setSelectedItem(Object anItem) {
851 int i = compareModes.indexOf(anItem);
852 if (i < 0)
853 throw new IllegalStateException(tr("Item {0} not found in list.", anItem));
854 selectedIdx = i;
855 fireModelDataChanged();
856 }
857
858 public ComparePairType getSelectedComparePair() {
859 return compareModes.get(selectedIdx);
860 }
861 }
862}
Note: See TracBrowser for help on using the repository browser.