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

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

global use of Utils.joinAsHtmlUnorderedList()

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