source: josm/trunk/src/org/openstreetmap/josm/gui/conflict/nodes/NodeListMergeModel.java@ 1622

Last change on this file since 1622 was 1622, checked in by Gubaer, 15 years ago

first version of extended conflict resolution dialog.

File size: 16.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.conflict.nodes;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.beans.PropertyChangeEvent;
7import java.beans.PropertyChangeListener;
8import java.util.ArrayList;
9import java.util.List;
10import java.util.logging.Logger;
11
12import javax.swing.DefaultListSelectionModel;
13import javax.swing.ListSelectionModel;
14import javax.swing.table.DefaultTableModel;
15import javax.swing.table.TableModel;
16
17import org.openstreetmap.josm.command.WayNodesConflictResolverCommand;
18import org.openstreetmap.josm.data.osm.Node;
19import org.openstreetmap.josm.data.osm.Way;
20
21public class NodeListMergeModel {
22 private static final Logger logger = Logger.getLogger(NodeListMergeModel.class.getName());
23
24 public static final String PROP_FROZEN = NodeListMergeModel.class.getName() + ".frozen";
25
26
27 private ArrayList<Node> myNodes;
28 private ArrayList<Node> theirNodes;
29 private ArrayList<Node> mergedNodes;
30
31
32 private DefaultTableModel myNodesTableModel;
33 private DefaultTableModel theirNodesTableModel;
34 private DefaultTableModel mergedNodesTableModel;
35
36 private DefaultListSelectionModel myNodesSelectionModel;
37 private DefaultListSelectionModel theirNodesSelectionModel;
38 private DefaultListSelectionModel mergedNodesSelectionModel;
39
40 private ArrayList<PropertyChangeListener> listeners;
41 private boolean isFrozen = false;
42
43
44 public NodeListMergeModel() {
45 myNodes = new ArrayList<Node>();
46 theirNodes = new ArrayList<Node>();
47 mergedNodes = new ArrayList<Node>();
48
49 myNodesTableModel = new NodeListTableModel(myNodes);
50 theirNodesTableModel = new NodeListTableModel(theirNodes);
51 mergedNodesTableModel = new NodeListTableModel(mergedNodes);
52
53 myNodesSelectionModel = new DefaultListSelectionModel();
54 theirNodesSelectionModel = new DefaultListSelectionModel();
55 mergedNodesSelectionModel = new DefaultListSelectionModel();
56
57 listeners = new ArrayList<PropertyChangeListener>();
58
59 setFrozen(true);
60 }
61
62
63 public void addPropertyChangeListener(PropertyChangeListener listener) {
64 synchronized(listeners) {
65 if (listener != null && ! listeners.contains(listener)) {
66 listeners.add(listener);
67 }
68 }
69 }
70
71 public void removePropertyChangeListener(PropertyChangeListener listener) {
72 synchronized(listeners) {
73 if (listener != null && listeners.contains(listener)) {
74 listeners.remove(listener);
75 }
76 }
77 }
78
79 protected void fireFrozenChanged(boolean oldValue, boolean newValue) {
80 synchronized(listeners) {
81 PropertyChangeEvent evt = new PropertyChangeEvent(this, PROP_FROZEN, oldValue, newValue);
82 for (PropertyChangeListener listener: listeners) {
83 listener.propertyChange(evt);
84 }
85 }
86 }
87
88 public void setFrozen(boolean isFrozen) {
89 boolean oldValue = this.isFrozen;
90 this.isFrozen = isFrozen;
91 fireFrozenChanged(oldValue, this.isFrozen);
92 }
93
94 public boolean isFrozen() {
95 return isFrozen;
96 }
97
98 public TableModel getMyNodesTableModel() {
99 return myNodesTableModel;
100 }
101
102 public TableModel getTheirNodesTableModel() {
103 return theirNodesTableModel;
104 }
105
106 public TableModel getMergedNodesTableModel() {
107 return mergedNodesTableModel;
108 }
109
110 public ListSelectionModel getMyNodesSelectionModel() {
111 return myNodesSelectionModel;
112 }
113
114 public ListSelectionModel getTheirNodesSelectionModel() {
115 return theirNodesSelectionModel;
116 }
117
118 public ListSelectionModel getMergedNodesSelectionModel() {
119 return mergedNodesSelectionModel;
120 }
121
122
123 protected void fireModelDataChanged() {
124 myNodesTableModel.fireTableDataChanged();
125 theirNodesTableModel.fireTableDataChanged();
126 mergedNodesTableModel.fireTableDataChanged();
127 }
128
129 protected void copyNodesToTop(List<Node> source, int []rows) {
130 if (rows == null || rows.length == 0) {
131 return;
132 }
133 for (int i = rows.length - 1; i >= 0; i--) {
134 int row = rows[i];
135 Node n = source.get(row);
136 mergedNodes.add(0, n);
137 }
138 fireModelDataChanged();
139 mergedNodesSelectionModel.setSelectionInterval(0, rows.length -1);
140 }
141
142 /**
143 * Copies the nodes given by indices in rows from the list of my nodes to the
144 * list of merged nodes. Inserts the nodes at the top of the list of merged
145 * nodes.
146 *
147 * @param rows the indices
148 */
149 public void copyMyNodesToTop(int [] rows) {
150 copyNodesToTop(myNodes, rows);
151 }
152
153 /**
154 * Copies the nodes given by indices in rows from the list of their nodes to the
155 * list of merged nodes. Inserts the nodes at the top of the list of merged
156 * nodes.
157 *
158 * @param rows the indices
159 */
160 public void copyTheirNodesToTop(int [] rows) {
161 copyNodesToTop(theirNodes, rows);
162 }
163
164 /**
165 * Copies the nodes given by indices in rows from the list of nodes in source to the
166 * list of merged nodes. Inserts the nodes at the end of the list of merged
167 * nodes.
168 *
169 * @param source the list of nodes to copy from
170 * @param rows the indices
171 */
172
173 public void copyNodesToEnd(List<Node> source, int [] rows) {
174 if (rows == null || rows.length == 0) {
175 return;
176 }
177 for (int row : rows) {
178 Node n = source.get(row);
179 mergedNodes.add(n);
180 }
181 fireModelDataChanged();
182 mergedNodesSelectionModel.setSelectionInterval(mergedNodes.size()-rows.length, mergedNodes.size() -1);
183
184 }
185
186 /**
187 * Copies the nodes given by indices in rows from the list of my nodes to the
188 * list of merged nodes. Inserts the nodes at the end of the list of merged
189 * nodes.
190 *
191 * @param rows the indices
192 */
193 public void copyMyNodesToEnd(int [] rows) {
194 copyNodesToEnd(myNodes, rows);
195 }
196
197 /**
198 * Copies the nodes given by indices in rows from the list of their nodes to the
199 * list of merged nodes. Inserts the nodes at the end of the list of merged
200 * nodes.
201 *
202 * @param rows the indices
203 */
204 public void copyTheirNodesToEnd(int [] rows) {
205 copyNodesToEnd(theirNodes, rows);
206 }
207
208 /**
209 * Copies the nodes given by indices in rows from the list of nodes <code>source</code> to the
210 * list of merged nodes. Inserts the nodes before row given by current.
211 *
212 * @param source the list of nodes to copy from
213 * @param rows the indices
214 * @param current the row index before which the nodes are inserted
215 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
216 *
217 */
218 protected void copyNodesBeforeCurrent(List<Node> source, int [] rows, int current) {
219 if (rows == null || rows.length == 0) {
220 return;
221 }
222 if (current < 0 || current >= mergedNodes.size()) {
223 throw new IllegalArgumentException(tr("parameter current out of range: got {0}", current));
224 }
225 for (int i=rows.length -1; i>=0; i--) {
226 int row = rows[i];
227 Node n = source.get(row);
228 mergedNodes.add(current, n);
229 }
230 fireModelDataChanged();
231 mergedNodesSelectionModel.setSelectionInterval(current, current + rows.length-1);
232 }
233
234 /**
235 * Copies the nodes given by indices in rows from the list of my nodes to the
236 * list of merged nodes. Inserts the nodes before row given by current.
237 *
238 * @param rows the indices
239 * @param current the row index before which the nodes are inserted
240 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
241 *
242 */
243 public void copyMyNodesBeforeCurrent(int [] rows, int current) {
244 copyNodesBeforeCurrent(myNodes,rows,current);
245 }
246
247 /**
248 * Copies the nodes given by indices in rows from the list of their nodes to the
249 * list of merged nodes. Inserts the nodes before row given by current.
250 *
251 * @param rows the indices
252 * @param current the row index before which the nodes are inserted
253 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
254 *
255 */
256 public void copyTheirNodesBeforeCurrent(int [] rows, int current) {
257 copyNodesBeforeCurrent(theirNodes,rows,current);
258 }
259
260 /**
261 * Copies the nodes given by indices in rows from the list of nodes <code>source</code> to the
262 * list of merged nodes. Inserts the nodes after the row given by current.
263 *
264 * @param source the list of nodes to copy from
265 * @param rows the indices
266 * @param current the row index after which the nodes are inserted
267 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
268 *
269 */
270 protected void copyNodesAfterCurrent(List<Node> source, int [] rows, int current) {
271 if (rows == null || rows.length == 0) {
272 return;
273 }
274 if (current < 0 || current >= mergedNodes.size()) {
275 throw new IllegalArgumentException(tr("parameter current out of range: got {0}", current));
276 }
277 if (current == mergedNodes.size() -1) {
278 copyMyNodesToEnd(rows);
279 } else {
280 for (int i=rows.length -1; i>=0; i--) {
281 int row = rows[i];
282 Node n = source.get(row);
283 mergedNodes.add(current+1, n);
284 }
285 }
286 fireModelDataChanged();
287 mergedNodesSelectionModel.setSelectionInterval(current+1, current + rows.length-1);
288 }
289
290 /**
291 * Copies the nodes given by indices in rows from the list of my nodes to the
292 * list of merged nodes. Inserts the nodes after the row given by current.
293 *
294 * @param rows the indices
295 * @param current the row index after which the nodes are inserted
296 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
297 *
298 */
299 public void copyMyNodesAfterCurrent(int [] rows, int current) {
300 copyNodesAfterCurrent(myNodes, rows, current);
301 }
302
303 /**
304 * Copies the nodes given by indices in rows from the list of my nodes to the
305 * list of merged nodes. Inserts the nodes after the row given by current.
306 *
307 * @param rows the indices
308 * @param current the row index after which the nodes are inserted
309 * @exception IllegalArgumentException thrown, if current < 0 or >= #nodes in list of merged nodes
310 *
311 */
312 public void copyTheirNodesAfterCurrent(int [] rows, int current) {
313 copyNodesAfterCurrent(theirNodes, rows, current);
314 }
315
316 /**
317 * Moves the nodes given by indices in rows up by one position in the list
318 * of merged nodes.
319 *
320 * @param rows the indices
321 *
322 */
323 protected void moveUpMergedNodes(int [] rows) {
324 if (rows == null || rows.length == 0) {
325 return;
326 }
327 if (rows[0] == 0) {
328 // can't move up
329 return;
330 }
331 for (int row: rows) {
332 Node n = mergedNodes.get(row);
333 mergedNodes.remove(row);
334 mergedNodes.add(row -1, n);
335 }
336 fireModelDataChanged();
337 mergedNodesSelectionModel.clearSelection();
338 for (int row: rows) {
339 mergedNodesSelectionModel.addSelectionInterval(row-1, row-1);
340 }
341 }
342
343 /**
344 * Moves the nodes given by indices in rows down by one position in the list
345 * of merged nodes.
346 *
347 * @param rows the indices
348 */
349 protected void moveDownMergedNodes(int [] rows) {
350 if (rows == null || rows.length == 0) {
351 return;
352 }
353 if (rows[rows.length -1] == mergedNodes.size() -1) {
354 // can't move down
355 return;
356 }
357 for (int i = rows.length-1; i>=0;i--) {
358 int row = rows[i];
359 Node n = mergedNodes.get(row);
360 mergedNodes.remove(row);
361 mergedNodes.add(row +1, n);
362 }
363 fireModelDataChanged();
364 mergedNodesSelectionModel.clearSelection();
365 for (int row: rows) {
366 mergedNodesSelectionModel.addSelectionInterval(row+1, row+1);
367 }
368 }
369
370 /**
371 * Removes the nodes given by indices in rows from the list
372 * of merged nodes.
373 *
374 * @param rows the indices
375 */
376 protected void removeMergedNodes(int [] rows) {
377 if (rows == null || rows.length == 0) {
378 return;
379 }
380 for (int i = rows.length-1; i>=0;i--) {
381 mergedNodes.remove(rows[i]);
382 }
383 fireModelDataChanged();
384 mergedNodesSelectionModel.clearSelection();
385 }
386
387
388 /**
389 * Replies true if the list of my nodes and the list of their
390 * nodes are equal, i.e. if they consists of a list of nodes with
391 * identical ids in the same order.
392 *
393 * @return true, if the lists are equal; false otherwise
394 */
395 protected boolean myAndTheirNodesEqual() {
396 if (myNodes.size() != theirNodes.size()) {
397 return false;
398 }
399 for (int i=0; i < myNodes.size(); i++) {
400 if (myNodes.get(i).id != theirNodes.get(i).id) {
401 return false;
402 }
403 }
404 return true;
405 }
406
407 /**
408 * Populates the model with the nodes in the two {@see Way}s <code>my</code> and
409 * <code>their</code>.
410 *
411 * @param my my way (i.e. the way in the local dataset)
412 * @param their their way (i.e. the way in the server dataset)
413 * @exception IllegalArgumentException thrown, if my is null
414 * @exception IllegalArgumentException thrown, if their is null
415 */
416 public void populate(Way my, Way their) {
417 if (my == null)
418 throw new IllegalArgumentException("parameter 'way' must not be null");
419 if (their == null)
420 throw new IllegalArgumentException("parameter 'their' must not be null");
421 mergedNodes.clear();
422 myNodes.clear();
423 theirNodes.clear();
424 for (Node n : my.nodes) {
425 myNodes.add(n);
426 }
427 for (Node n : their.nodes) {
428 theirNodes.add(n);
429 }
430 if (myAndTheirNodesEqual()) {
431 mergedNodes = new ArrayList<Node>(myNodes);
432 setFrozen(true);
433 } else {
434 setFrozen(false);
435 }
436
437 fireModelDataChanged();
438 }
439
440 /**
441 * Builds the command to resolve conflicts in the node list of a way
442 *
443 * @param my my way. Must not be null.
444 * @param their their way. Must not be null
445 * @return the command
446 * @exception IllegalArgumentException thrown, if my is null or not a {@see Way}
447 * @exception IllegalArgumentException thrown, if their is null or not a {@see Way}
448 * @exception IllegalStateException thrown, if the merge is not yet frozen
449 */
450 public WayNodesConflictResolverCommand buildResolveCommand(Way my, Way their) {
451 if (my == null) {
452 throw new IllegalArgumentException("parameter my most not be null");
453 }
454 if (their == null) {
455 throw new IllegalArgumentException("parameter my most not be null");
456 }
457 if (! isFrozen()) {
458 throw new IllegalArgumentException("merged nodes not frozen yet. Can't build resolution command");
459 }
460 return new WayNodesConflictResolverCommand(my, their, mergedNodes);
461 }
462
463 class NodeListTableModel extends DefaultTableModel {
464 private ArrayList<Node> nodes;
465
466 public NodeListTableModel(ArrayList<Node> nodes) {
467 this.nodes = nodes;
468 }
469
470 @Override
471 public int getRowCount() {
472 return nodes == null ? 0 : nodes.size();
473 }
474
475 @Override
476 public Object getValueAt(int row, int column) {
477 return nodes.get(row);
478 }
479
480 @Override
481 public boolean isCellEditable(int row, int column) {
482 return false;
483 }
484 }
485
486
487}
Note: See TracBrowser for help on using the repository browser.