Ticket #7489: add_merge_command.patch
File add_merge_command.patch, 17.1 KB (added by , 12 years ago) |
---|
-
src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
372 372 * 373 373 * @param numNewConflicts the number of detected conflicts 374 374 */ 375 p rotectedvoid warnNumNewConflicts(int numNewConflicts) {375 public void warnNumNewConflicts(int numNewConflicts) { 376 376 if (numNewConflicts == 0) return; 377 377 378 378 String msg1 = trn( -
src/org/openstreetmap/josm/actions/MergeSelectionAction.java
8 8 import java.awt.event.KeyEvent; 9 9 import java.util.Collection; 10 10 import java.util.List; 11 import org.openstreetmap.josm.Main; 12 import org.openstreetmap.josm.command.MergeCommand; 11 13 12 14 import org.openstreetmap.josm.data.osm.DataSet; 13 15 import org.openstreetmap.josm.data.osm.OsmPrimitive; 14 import org.openstreetmap.josm.data.osm.visitor.MergeSourceBuildingVisitor;15 16 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 16 17 import org.openstreetmap.josm.gui.layer.Layer; 17 18 import org.openstreetmap.josm.gui.layer.OsmDataLayer; … … 36 37 Layer targetLayer = askTargetLayer(targetLayers); 37 38 if (targetLayer == null) 38 39 return; 39 MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(getEditLayer().data); 40 ((OsmDataLayer)targetLayer).mergeFrom(builder.build()); 40 41 MergeCommand cmd = new MergeCommand((OsmDataLayer)targetLayer, getEditLayer().data, true); 42 Main.main.undoRedo.add(cmd); 41 43 } 42 44 45 @Override 43 46 public void actionPerformed(ActionEvent e) { 44 47 if (getEditLayer() == null || getEditLayer().data.getSelected().isEmpty()) 45 48 return; -
src/org/openstreetmap/josm/data/osm/DataSetMerger.java
43 43 */ 44 44 private final Set<PrimitiveId> objectsWithChildrenToMerge; 45 45 private final Set<OsmPrimitive> objectsToDelete; 46 47 private final Map<OsmPrimitive, PrimitiveData> changedObjectsMap; 48 private final Set<OsmPrimitive> addedObjects; 49 50 private enum UndoState { 51 INIT, MERGED, UNDONE, REDONE 52 } 53 private UndoState undoState; 46 54 47 55 /** 48 56 * constructor … … 61 69 mergedMap = new HashMap<PrimitiveId, PrimitiveId>(); 62 70 objectsWithChildrenToMerge = new HashSet<PrimitiveId>(); 63 71 objectsToDelete = new HashSet<OsmPrimitive>(); 72 changedObjectsMap = new HashMap<OsmPrimitive, PrimitiveData>(); 73 addedObjects = new HashSet<OsmPrimitive>(); 74 undoState = UndoState.INIT; 64 75 } 65 76 66 77 /** … … 77 88 * @param <P> the type of the other primitive 78 89 * @param source the other primitive 79 90 */ 80 protected void mergePrimitive(OsmPrimitive source, Collection<? extends OsmPrimitive> candidates) {91 protected void mergePrimitive(OsmPrimitive source, Collection<? extends OsmPrimitive> candidates) { 81 92 if (!source.isNew() ) { 82 93 // try to merge onto a matching primitive with the same 83 94 // defined id … … 107 118 target.setTimestamp(source.getTimestamp()); 108 119 target.setModified(source.isModified()); 109 120 objectsWithChildrenToMerge.add(source.getPrimitiveId()); 121 changedObjectsMap.put(target, source.save()); 110 122 return; 111 123 } 112 124 } … … 126 138 targetDataSet.addPrimitive(target); 127 139 mergedMap.put(source.getPrimitiveId(), target.getPrimitiveId()); 128 140 objectsWithChildrenToMerge.add(source.getPrimitiveId()); 141 addedObjects.add(target); 129 142 } 130 143 131 144 protected OsmPrimitive getMergeTarget(OsmPrimitive mergeSource) throws IllegalStateException { … … 180 193 if (referrers.isEmpty()) { 181 194 target.setDeleted(true); 182 195 target.mergeFrom(source); 196 changedObjectsMap.put(target, source.save()); 183 197 it.remove(); 184 198 flag = true; 185 199 } else { … … 208 222 ((Relation) osm).setMembers(null); 209 223 } 210 224 } 211 for (OsmPrimitive osm: objectsToDelete) { 212 osm.setDeleted(true); 213 osm.mergeFrom(sourceDataSet.getPrimitiveById(osm.getPrimitiveId())); 225 for (OsmPrimitive target: objectsToDelete) { 226 OsmPrimitive source = sourceDataSet.getPrimitiveById(target.getPrimitiveId()); 227 target.setDeleted(true); 228 target.mergeFrom(source); 229 changedObjectsMap.put(target, source.save()); 214 230 } 215 231 } 216 232 } … … 293 309 // => merge source into target 294 310 // 295 311 target.mergeFrom(source); 312 changedObjectsMap.put(target, source.save()); 296 313 objectsWithChildrenToMerge.add(source.getPrimitiveId()); 297 314 } else if (!target.isIncomplete() && source.isIncomplete()) { 298 315 // target is complete and source is incomplete … … 318 335 if (targetDataSet.getPrimitiveById(referrer.getPrimitiveId()) == null) { 319 336 conflicts.add(new Conflict<OsmPrimitive>(target, source, true)); 320 337 target.setDeleted(false); 338 changedObjectsMap.put(target, source.save()); 321 339 break; 322 340 } 323 341 } … … 330 348 // target not modified. We can assume that source is the most recent version. 331 349 // clone it into target. 332 350 target.mergeFrom(source); 351 changedObjectsMap.put(target, source.save()); 333 352 objectsWithChildrenToMerge.add(source.getPrimitiveId()); 334 353 } else if (! target.isModified() && !source.isModified() && target.getVersion() == source.getVersion()) { 335 354 // both not modified. Merge nevertheless. 336 355 // This helps when updating "empty" relations, see #4295 337 356 target.mergeFrom(source); 357 changedObjectsMap.put(target, source.save()); 338 358 objectsWithChildrenToMerge.add(source.getPrimitiveId()); 339 359 } else if (! target.isModified() && !source.isModified() && target.getVersion() < source.getVersion()) { 340 360 // my not modified but other is newer. clone other onto mine. 341 361 // 342 362 target.mergeFrom(source); 363 changedObjectsMap.put(target, source.save()); 343 364 objectsWithChildrenToMerge.add(source.getPrimitiveId()); 344 365 } else if (target.isModified() && ! source.isModified() && target.getVersion() == source.getVersion()) { 345 366 // target is same as source but target is modified 346 367 // => keep target and reset modified flag if target and source are semantically equal 347 368 if (target.hasEqualSemanticAttributes(source)) { 348 369 target.setModified(false); 370 changedObjectsMap.put(target, source.save()); 349 371 } 350 372 } else if (source.isDeleted() != target.isDeleted()) { 351 373 // target is modified and deleted state differs. … … 363 385 // attributes should already be equal if we get here. 364 386 // 365 387 target.mergeFrom(source); 388 changedObjectsMap.put(target, source.save()); 366 389 objectsWithChildrenToMerge.add(source.getPrimitiveId()); 367 390 } 368 391 return true; … … 423 446 if (progressMonitor != null) { 424 447 progressMonitor.finishTask(); 425 448 } 449 450 undoState = UndoState.MERGED; 426 451 } 427 452 428 453 /** … … 442 467 public ConflictCollection getConflicts() { 443 468 return conflicts; 444 469 } 470 471 /** 472 * Undos the merge operation. 473 */ 474 public void unmerge() { 475 if (undoState != UndoState.MERGED && undoState != UndoState.REDONE) { 476 throw new AssertionError(); 477 } 478 479 targetDataSet.beginUpdate(); 480 481 for (PrimitiveId osm : addedObjects) { 482 targetDataSet.removePrimitive(osm); 483 } 484 485 for (Map.Entry<OsmPrimitive, PrimitiveData> e : changedObjectsMap.entrySet()) { 486 // restore previous state and save current state for opposite undo action 487 PrimitiveData old = e.getKey().save(); 488 e.getKey().load(e.getValue()); 489 e.setValue(old); 490 } 491 492 targetDataSet.endUpdate(); 493 undoState = UndoState.UNDONE; 494 } 495 496 public void remerge() { 497 if (undoState != UndoState.UNDONE) { 498 throw new AssertionError(); 499 } 500 501 targetDataSet.beginUpdate(); 502 503 for (OsmPrimitive osm : addedObjects) { 504 targetDataSet.addPrimitive(osm); 505 } 506 507 for (Map.Entry<OsmPrimitive, PrimitiveData> e : changedObjectsMap.entrySet()) { 508 // restore previous state and save current state for opposite undo action 509 PrimitiveData old = e.getKey().save(); 510 e.getKey().load(e.getValue()); 511 e.setValue(old); 512 } 513 514 targetDataSet.endUpdate(); 515 undoState = UndoState.REDONE; 516 } 517 518 public Map<OsmPrimitive, PrimitiveData> getChangedObjectsMap() { 519 return changedObjectsMap; 520 } 521 522 public Set<OsmPrimitive> getAddedObjects() { 523 return addedObjects; 524 } 445 525 } -
src/org/openstreetmap/josm/command/MergeCommand.java
1 // License: GPL. Copyright 2012 by Josh Doe and others 2 package org.openstreetmap.josm.command; 3 4 import java.awt.geom.Area; 5 import java.util.Collection; 6 import java.util.HashSet; 7 import javax.swing.Icon; 8 import javax.swing.JOptionPane; 9 import org.openstreetmap.josm.Main; 10 import org.openstreetmap.josm.data.conflict.Conflict; 11 import org.openstreetmap.josm.data.osm.*; 12 import org.openstreetmap.josm.data.osm.visitor.MergeSourceBuildingVisitor; 13 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 14 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor; 15 import org.openstreetmap.josm.tools.CheckParameterUtil; 16 import static org.openstreetmap.josm.tools.I18n.marktr; 17 import static org.openstreetmap.josm.tools.I18n.tr; 18 import org.openstreetmap.josm.tools.ImageProvider; 19 20 /** 21 * A command that merges objects from one layer to another. 22 * 23 * @author joshdoe 24 */ 25 public class MergeCommand extends Command { 26 27 private DataSetMerger merger; 28 private DataSet sourceDataSet; 29 private DataSet targetDataSet; 30 private OsmDataLayer targetLayer; 31 private Collection<DataSource> addedDataSources; 32 private String otherVersion; 33 private Collection<Conflict> addedConflicts; 34 35 /** 36 * Create command to merge all or only currently selected objects from 37 * sourceDataSet to targetLayer. 38 * 39 * @param targetLayer 40 * @param sourceDataSet 41 * @param onlySelected true to only merge objects selected in the 42 * sourceDataSet 43 */ 44 public MergeCommand(OsmDataLayer targetLayer, DataSet sourceDataSet, boolean onlySelected) { 45 this(targetLayer, sourceDataSet, onlySelected ? sourceDataSet.getSelected() : null); 46 } 47 48 /** 49 * Create command to merge the selection from the sourceDataSet to the 50 * targetLayer. 51 * 52 * @param targetLayer 53 * @param sourceDataSet 54 * @param selection 55 */ 56 public MergeCommand(OsmDataLayer targetLayer, DataSet sourceDataSet, Collection<OsmPrimitive> selection) { 57 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 58 CheckParameterUtil.ensureParameterNotNull(sourceDataSet, "sourceDataSet"); 59 this.targetLayer = targetLayer; 60 this.targetDataSet = targetLayer.data; 61 62 // if selection present, create new dataset with just selected objects 63 // and their "hull" (otherwise use entire dataset) 64 if (selection != null && !selection.isEmpty()) { 65 Collection<OsmPrimitive> origSelection = sourceDataSet.getSelected(); 66 sourceDataSet.setSelected(selection); 67 MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(sourceDataSet); 68 this.sourceDataSet = builder.build(); 69 sourceDataSet.setSelected(origSelection); 70 } 71 72 addedConflicts = new HashSet<Conflict>(); 73 addedDataSources = new HashSet<DataSource>(); 74 } 75 76 @Override 77 public boolean executeCommand() { 78 PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor(tr("Merging data")); 79 monitor.setCancelable(false); 80 if (merger == null) { 81 //first time command is executed 82 merger = new DataSetMerger(targetDataSet, sourceDataSet); 83 try { 84 merger.merge(monitor); 85 } catch (DataIntegrityProblemException e) { 86 JOptionPane.showMessageDialog( 87 Main.parent, 88 e.getHtmlMessage() != null ? e.getHtmlMessage() : e.getMessage(), 89 tr("Error"), 90 JOptionPane.ERROR_MESSAGE); 91 return false; 92 93 } 94 95 Area a = targetDataSet.getDataSourceArea(); 96 97 // copy the merged layer's data source info; 98 // only add source rectangles if they are not contained in the 99 // layer already. 100 for (DataSource src : sourceDataSet.dataSources) { 101 if (a == null || !a.contains(src.bounds.asRect())) { 102 targetDataSet.dataSources.add(src); 103 addedDataSources.add(src); 104 } 105 } 106 107 otherVersion = targetDataSet.getVersion(); 108 // copy the merged layer's API version, downgrade if required 109 if (targetDataSet.getVersion() == null) { 110 targetDataSet.setVersion(sourceDataSet.getVersion()); 111 } else if ("0.5".equals(targetDataSet.getVersion()) ^ "0.5".equals(sourceDataSet.getVersion())) { 112 System.err.println(tr("Warning: mixing 0.6 and 0.5 data results in version 0.5")); 113 targetDataSet.setVersion("0.5"); 114 } 115 116 117 // FIXME: allow conflicts to be retrieved rather than added to layer? 118 if (targetLayer != null) { 119 for (Conflict<?> c : merger.getConflicts()) { 120 if (!targetLayer.getConflicts().hasConflict(c)) { 121 targetLayer.getConflicts().add(c); 122 addedConflicts.add(c); 123 } 124 } 125 } 126 } else { 127 // command is being "redone" 128 129 merger.remerge(); 130 targetDataSet.dataSources.addAll(addedDataSources); 131 132 String version = otherVersion; 133 otherVersion = targetDataSet.getVersion(); 134 targetDataSet.setVersion(version); 135 136 for (Conflict c : addedConflicts) { 137 targetLayer.getConflicts().add(c); 138 } 139 } 140 141 if (addedConflicts.size() > 0) { 142 targetLayer.warnNumNewConflicts(addedConflicts.size()); 143 } 144 145 // repaint to make sure new data is displayed properly. 146 Main.map.mapView.repaint(); 147 148 monitor.close(); 149 150 return true; 151 } 152 153 @Override 154 public void undoCommand() { 155 merger.unmerge(); 156 157 // restore data source area 158 targetDataSet.dataSources.removeAll(addedDataSources); 159 160 String version = otherVersion; 161 otherVersion = targetDataSet.getVersion(); 162 targetDataSet.setVersion(version); 163 164 for (Conflict c : addedConflicts) { 165 targetLayer.getConflicts().remove(c); 166 } 167 168 Main.map.mapView.repaint(); 169 } 170 171 @Override 172 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 173 throw new UnsupportedOperationException("Not supported yet."); 174 } 175 176 @Override 177 public String getDescriptionText() { 178 return tr(marktr("Merge objects, {0} added, {1} modified"), 179 merger.getAddedObjects().size(), 180 merger.getChangedObjectsMap().size()); 181 } 182 183 @Override 184 public Icon getDescriptionIcon() { 185 return ImageProvider.get("dialogs", "mergedown"); 186 } 187 } 188 No newline at end of file