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

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

fix #7855 - conflict dialog: CCE selecting a way in members tab

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