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

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

Checkstyle 6.19: enable SingleSpaceSeparator and fix violations

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