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

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

Sonar/FindBugs - Loose coupling

  • 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 #FROZEN_PROP}.
58 *
59 * ListMergeModel is an abstract class. Three methods have to be implemented by subclasses:
60 * <ul>
61 * <li>{@link ListMergeModel#cloneEntryForMergedList} - clones an entry of type T</li>
62 * <li>{@link ListMergeModel#isEqualEntry} - 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 Map<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 * @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 List<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 List<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 Object value = getValueAt(idx, 1);
716 if (value instanceof OsmPrimitive) {
717 return (OsmPrimitive) value;
718 } else if (value instanceof RelationMember) {
719 return ((RelationMember)value).getMember();
720 } else {
721 Main.error("Unknown object type: "+value);
722 return null;
723 }
724 }
725 }
726
727 /**
728 * This is the selection model to be used in a {@link JTable} which displays
729 * an entry list managed by {@link ListMergeModel}.
730 *
731 * The model ensures that only rows displaying an entry in the entry list
732 * can be selected. "Empty" rows can't be selected.
733 *
734 * @see ListMergeModel#getMySelectionModel()
735 * @see ListMergeModel#getMergedSelectionModel()
736 * @see ListMergeModel#getTheirSelectionModel()
737 *
738 */
739 protected class EntriesSelectionModel extends DefaultListSelectionModel {
740 private final List<T> entries;
741
742 public EntriesSelectionModel(ArrayList<T> nodes) {
743 this.entries = nodes;
744 }
745
746 @Override
747 public void addSelectionInterval(int index0, int index1) {
748 if (entries.isEmpty()) return;
749 if (index0 > entries.size() - 1) return;
750 index0 = Math.min(entries.size()-1, index0);
751 index1 = Math.min(entries.size()-1, index1);
752 super.addSelectionInterval(index0, index1);
753 }
754
755 @Override
756 public void insertIndexInterval(int index, int length, boolean before) {
757 if (entries.isEmpty()) return;
758 if (before) {
759 int newindex = Math.min(entries.size()-1, index);
760 if (newindex < index - length) return;
761 length = length - (index - newindex);
762 super.insertIndexInterval(newindex, length, before);
763 } else {
764 if (index > entries.size() -1) return;
765 length = Math.min(entries.size()-1 - index, length);
766 super.insertIndexInterval(index, length, before);
767 }
768 }
769
770 @Override
771 public void moveLeadSelectionIndex(int leadIndex) {
772 if (entries.isEmpty()) return;
773 leadIndex = Math.max(0, leadIndex);
774 leadIndex = Math.min(entries.size() - 1, leadIndex);
775 super.moveLeadSelectionIndex(leadIndex);
776 }
777
778 @Override
779 public void removeIndexInterval(int index0, int index1) {
780 if (entries.isEmpty()) return;
781 index0 = Math.max(0, index0);
782 index0 = Math.min(entries.size() - 1, index0);
783
784 index1 = Math.max(0, index1);
785 index1 = Math.min(entries.size() - 1, index1);
786 super.removeIndexInterval(index0, index1);
787 }
788
789 @Override
790 public void removeSelectionInterval(int index0, int index1) {
791 if (entries.isEmpty()) return;
792 index0 = Math.max(0, index0);
793 index0 = Math.min(entries.size() - 1, index0);
794
795 index1 = Math.max(0, index1);
796 index1 = Math.min(entries.size() - 1, index1);
797 super.removeSelectionInterval(index0, index1);
798 }
799
800 @Override
801 public void setAnchorSelectionIndex(int anchorIndex) {
802 if (entries.isEmpty()) return;
803 anchorIndex = Math.min(entries.size() - 1, anchorIndex);
804 super.setAnchorSelectionIndex(anchorIndex);
805 }
806
807 @Override
808 public void setLeadSelectionIndex(int leadIndex) {
809 if (entries.isEmpty()) return;
810 leadIndex = Math.min(entries.size() - 1, leadIndex);
811 super.setLeadSelectionIndex(leadIndex);
812 }
813
814 @Override
815 public void setSelectionInterval(int index0, int index1) {
816 if (entries.isEmpty()) return;
817 index0 = Math.max(0, index0);
818 index0 = Math.min(entries.size() - 1, index0);
819
820 index1 = Math.max(0, index1);
821 index1 = Math.min(entries.size() - 1, index1);
822
823 super.setSelectionInterval(index0, index1);
824 }
825 }
826
827 public ComparePairListModel getComparePairListModel() {
828 return this.comparePairListModel;
829 }
830
831 public class ComparePairListModel extends AbstractListModel implements ComboBoxModel {
832
833 private int selectedIdx;
834 private final ArrayList<ComparePairType> compareModes;
835
836 public ComparePairListModel() {
837 this.compareModes = new ArrayList<ComparePairType>();
838 compareModes.add(MY_WITH_THEIR);
839 compareModes.add(MY_WITH_MERGED);
840 compareModes.add(THEIR_WITH_MERGED);
841 selectedIdx = 0;
842 }
843
844 @Override
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 @Override
852 public int getSize() {
853 return compareModes.size();
854 }
855
856 @Override
857 public Object getSelectedItem() {
858 return compareModes.get(selectedIdx);
859 }
860
861 @Override
862 public void setSelectedItem(Object anItem) {
863 int i = compareModes.indexOf(anItem);
864 if (i < 0)
865 throw new IllegalStateException(tr("Item {0} not found in list.", anItem));
866 selectedIdx = i;
867 fireModelDataChanged();
868 }
869
870 public ComparePairType getSelectedComparePair() {
871 return compareModes.get(selectedIdx);
872 }
873 }
874}
Note: See TracBrowser for help on using the repository browser.