Ticket #7489: add_merge_command2.patch
| File add_merge_command2.patch, 25.9 KB (added by , 14 years ago) |
|---|
-
src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationMemberTask.java
13 13 import javax.swing.SwingUtilities; 14 14 15 15 import org.openstreetmap.josm.Main; 16 import org.openstreetmap.josm.command.DownloadOsmCommand; 16 17 import org.openstreetmap.josm.data.osm.DataSet; 17 18 import org.openstreetmap.josm.data.osm.OsmPrimitive; 18 19 import org.openstreetmap.josm.data.osm.Relation; … … 135 136 SwingUtilities.invokeLater( 136 137 new Runnable() { 137 138 public void run() { 138 curLayer.mergeFrom(dataSet); 139 curLayer.onPostDownloadFromServer(); 139 Main.main.undoRedo.add(new DownloadOsmCommand(tr("Download relation members"), curLayer, dataSet)); 140 140 } 141 141 } 142 142 ); -
src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationTask.java
9 9 import javax.swing.SwingUtilities; 10 10 11 11 import org.openstreetmap.josm.Main; 12 import org.openstreetmap.josm.command.DownloadOsmCommand; 12 13 import org.openstreetmap.josm.data.osm.DataSet; 13 14 import org.openstreetmap.josm.data.osm.DataSetMerger; 14 15 import org.openstreetmap.josm.data.osm.Relation; … … 98 99 SwingUtilities.invokeAndWait( 99 100 new Runnable() { 100 101 public void run() { 101 layer.mergeFrom(allDownloads); 102 layer.onPostDownloadFromServer(); 102 Main.main.undoRedo.add(new DownloadOsmCommand(tr("Download relation(s)"), layer, allDownloads)); 103 103 Main.map.repaint(); 104 104 } 105 105 } -
src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
99 99 100 100 public List<TestError> validationErrors = new ArrayList<TestError>(); 101 101 102 p rotectedvoid setRequiresSaveToFile(boolean newValue) {102 public void setRequiresSaveToFile(boolean newValue) { 103 103 boolean oldValue = requiresSaveToFile; 104 104 requiresSaveToFile = newValue; 105 105 if (oldValue != newValue) { … … 107 107 } 108 108 } 109 109 110 p rotectedvoid setRequiresUploadToServer(boolean newValue) {110 public void setRequiresUploadToServer(boolean newValue) { 111 111 boolean oldValue = requiresUploadToServer; 112 112 requiresUploadToServer = newValue; 113 113 if (oldValue != newValue) { … … 299 299 } 300 300 301 301 @Override public void mergeFrom(final Layer from) { 302 // TODO: make undo-able 302 303 final PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor(tr("Merging layers")); 303 304 monitor.setCancelable(false); 304 305 mergeFrom(((OsmDataLayer)from).data, monitor); … … 311 312 * 312 313 * @param from the source data set 313 314 */ 315 @Deprecated 314 316 public void mergeFrom(final DataSet from) { 315 317 mergeFrom(from, null); 316 318 } … … 321 323 * 322 324 * @param from the source data set 323 325 */ 326 @Deprecated 324 327 public void mergeFrom(final DataSet from, ProgressMonitor progressMonitor) { 325 328 final DataSetMerger visitor = new DataSetMerger(data,from); 326 329 try { … … 372 375 * 373 376 * @param numNewConflicts the number of detected conflicts 374 377 */ 375 p rotectedvoid warnNumNewConflicts(int numNewConflicts) {378 public void warnNumNewConflicts(int numNewConflicts) { 376 379 if (numNewConflicts == 0) return; 377 380 378 381 String msg1 = trn( -
src/org/openstreetmap/josm/gui/io/DownloadPrimitivesTask.java
10 10 import java.util.Set; 11 11 12 12 import javax.swing.SwingUtilities; 13 import org.openstreetmap.josm.Main; 13 14 14 15 import org.openstreetmap.josm.actions.AutoScaleAction; 16 import org.openstreetmap.josm.command.DownloadOsmCommand; 15 17 import org.openstreetmap.josm.data.osm.DataSet; 16 18 import org.openstreetmap.josm.data.osm.DataSetMerger; 17 19 import org.openstreetmap.josm.data.osm.Node; … … 83 85 } 84 86 Runnable r = new Runnable() { 85 87 public void run() { 86 layer.mergeFrom(ds);88 Main.main.undoRedo.add(new DownloadOsmCommand(tr("Download primitives"), layer, ds)); 87 89 AutoScaleAction.zoomTo(ds.allPrimitives()); 88 layer.onPostDownloadFromServer();89 90 } 90 91 }; 91 92 -
src/org/openstreetmap/josm/gui/io/UpdatePrimitivesTask.java
10 10 import java.util.Collections; 11 11 12 12 import javax.swing.SwingUtilities; 13 import org.openstreetmap.josm.Main; 14 import org.openstreetmap.josm.command.DownloadOsmCommand; 13 15 14 16 import org.openstreetmap.josm.data.osm.DataSet; 15 17 import org.openstreetmap.josm.data.osm.DataSetMerger; … … 81 83 } 82 84 Runnable r = new Runnable() { 83 85 public void run() { 84 layer.mergeFrom(ds); 85 layer.onPostDownloadFromServer(); 86 Main.main.undoRedo.add(new DownloadOsmCommand(tr("Update primitives"), layer, ds)); 86 87 } 87 88 }; 88 89 -
src/org/openstreetmap/josm/actions/UpdateSelectionAction.java
13 13 import javax.swing.JOptionPane; 14 14 15 15 import org.openstreetmap.josm.Main; 16 import org.openstreetmap.josm.command.DownloadOsmCommand; 16 17 import org.openstreetmap.josm.data.osm.DataSet; 17 18 import org.openstreetmap.josm.data.osm.OsmPrimitive; 18 19 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; … … 39 40 reader.append(getCurrentDataSet(),id, type); 40 41 try { 41 42 DataSet ds = reader.parseOsm(NullProgressMonitor.INSTANCE); 42 Main.ma p.mapView.getEditLayer().mergeFrom(ds);43 Main.main.undoRedo.add(new DownloadOsmCommand(tr("Update selected primitives"), Main.map.mapView.getEditLayer(), ds)); 43 44 } catch(Exception e) { 44 45 ExceptionDialogUtil.explainException(e); 45 46 } -
src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java
10 10 import java.util.regex.Pattern; 11 11 12 12 import org.openstreetmap.josm.Main; 13 import org.openstreetmap.josm.actions.AutoScaleAction; 14 import org.openstreetmap.josm.command.DownloadOsmCommand; 13 15 import org.openstreetmap.josm.data.Bounds; 14 16 import org.openstreetmap.josm.data.coor.LatLon; 15 17 import org.openstreetmap.josm.data.osm.DataSet; … … 207 209 if (targetLayer == null) { 208 210 targetLayer = getFirstDataLayer(); 209 211 } 210 targetLayer.mergeFrom(dataSet); 211 computeBboxAndCenterScale(); 212 targetLayer.onPostDownloadFromServer(); 212 Main.main.undoRedo.add(new DownloadOsmCommand(tr("Download primitives from bounding box"), targetLayer, dataSet)); 213 AutoScaleAction.zoomTo(dataSet.allPrimitives()); 213 214 } 214 215 } 215 216 -
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 /** 497 * Re-merge objects with dataset. Identical results as {@see #merge()}, but performed 498 * after {@see #unmerge()} has been called. 499 */ 500 public void remerge() { 501 if (undoState != UndoState.UNDONE) { 502 throw new AssertionError(); 503 } 504 505 targetDataSet.beginUpdate(); 506 507 // add back objects in order that they can be referenced by other objects 508 for (OsmPrimitive osm : OsmPrimitive.getFilteredList(addedObjects, Node.class)) { 509 targetDataSet.addPrimitive(osm); 510 } 511 for (OsmPrimitive osm : OsmPrimitive.getFilteredList(addedObjects, Way.class)) { 512 targetDataSet.addPrimitive(osm); 513 } 514 for (OsmPrimitive osm : OsmPrimitive.getFilteredList(addedObjects, Relation.class)) { 515 targetDataSet.addPrimitive(osm); 516 } 517 518 for (Map.Entry<OsmPrimitive, PrimitiveData> e : changedObjectsMap.entrySet()) { 519 // restore previous state and save current state for opposite undo action 520 PrimitiveData old = e.getKey().save(); 521 e.getKey().load(e.getValue()); 522 e.setValue(old); 523 } 524 525 targetDataSet.endUpdate(); 526 undoState = UndoState.REDONE; 527 } 528 529 public Map<OsmPrimitive, PrimitiveData> getChangedObjectsMap() { 530 return changedObjectsMap; 531 } 532 533 public Set<OsmPrimitive> getAddedObjects() { 534 return addedObjects; 535 } 445 536 } -
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 super(targetLayer); 58 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 59 CheckParameterUtil.ensureParameterNotNull(sourceDataSet, "sourceDataSet"); 60 this.targetLayer = targetLayer; 61 this.targetDataSet = targetLayer.data; 62 63 // if selection present, create new dataset with just selected objects 64 // and their "hull" (otherwise use entire dataset) 65 if (selection != null && !selection.isEmpty()) { 66 Collection<OsmPrimitive> origSelection = sourceDataSet.getSelected(); 67 sourceDataSet.setSelected(selection); 68 MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(sourceDataSet); 69 this.sourceDataSet = builder.build(); 70 sourceDataSet.setSelected(origSelection); 71 } else { 72 this.sourceDataSet = sourceDataSet; 73 } 74 75 76 addedConflicts = new HashSet<Conflict>(); 77 addedDataSources = new HashSet<DataSource>(); 78 } 79 80 @Override 81 public boolean executeCommand() { 82 PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor(tr("Merging data")); 83 monitor.setCancelable(false); 84 if (merger == null) { 85 //first time command is executed 86 merger = new DataSetMerger(targetDataSet, sourceDataSet); 87 try { 88 merger.merge(monitor); 89 } catch (DataIntegrityProblemException e) { 90 JOptionPane.showMessageDialog( 91 Main.parent, 92 e.getHtmlMessage() != null ? e.getHtmlMessage() : e.getMessage(), 93 tr("Error"), 94 JOptionPane.ERROR_MESSAGE); 95 return false; 96 97 } 98 99 Area a = targetDataSet.getDataSourceArea(); 100 101 // copy the merged layer's data source info; 102 // only add source rectangles if they are not contained in the 103 // layer already. 104 for (DataSource src : sourceDataSet.dataSources) { 105 if (a == null || !a.contains(src.bounds.asRect())) { 106 targetDataSet.dataSources.add(src); 107 addedDataSources.add(src); 108 } 109 } 110 111 otherVersion = targetDataSet.getVersion(); 112 // copy the merged layer's API version, downgrade if required 113 if (targetDataSet.getVersion() == null) { 114 targetDataSet.setVersion(sourceDataSet.getVersion()); 115 } else if ("0.5".equals(targetDataSet.getVersion()) ^ "0.5".equals(sourceDataSet.getVersion())) { 116 System.err.println(tr("Warning: mixing 0.6 and 0.5 data results in version 0.5")); 117 targetDataSet.setVersion("0.5"); 118 } 119 120 121 // FIXME: allow conflicts to be retrieved rather than added to layer? 122 if (targetLayer != null) { 123 for (Conflict<?> c : merger.getConflicts()) { 124 if (!targetLayer.getConflicts().hasConflict(c)) { 125 targetLayer.getConflicts().add(c); 126 addedConflicts.add(c); 127 } 128 } 129 } 130 } else { 131 // command is being "redone" 132 133 merger.remerge(); 134 targetDataSet.dataSources.addAll(addedDataSources); 135 136 String version = otherVersion; 137 otherVersion = targetDataSet.getVersion(); 138 targetDataSet.setVersion(version); 139 140 for (Conflict c : addedConflicts) { 141 targetLayer.getConflicts().add(c); 142 } 143 } 144 145 if (addedConflicts.size() > 0) { 146 targetLayer.warnNumNewConflicts(addedConflicts.size()); 147 } 148 149 // repaint to make sure new data is displayed properly. 150 Main.map.mapView.repaint(); 151 152 monitor.close(); 153 154 return true; 155 } 156 157 @Override 158 public void undoCommand() { 159 merger.unmerge(); 160 161 // restore data source area 162 targetDataSet.dataSources.removeAll(addedDataSources); 163 164 String version = otherVersion; 165 otherVersion = targetDataSet.getVersion(); 166 targetDataSet.setVersion(version); 167 168 for (Conflict c : addedConflicts) { 169 targetLayer.getConflicts().remove(c); 170 } 171 172 Main.map.mapView.repaint(); 173 } 174 175 @Override 176 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 177 throw new UnsupportedOperationException("Not supported yet."); 178 } 179 180 @Override 181 public String getDescriptionText() { 182 return tr(marktr("Merged objects: {0} added, {1} modified"), 183 merger.getAddedObjects().size(), 184 merger.getChangedObjectsMap().size()); 185 } 186 187 @Override 188 public Icon getDescriptionIcon() { 189 return ImageProvider.get("dialogs", "mergedown"); 190 } 191 192 @Override 193 public Collection<? extends OsmPrimitive> getParticipatingPrimitives() { 194 HashSet<OsmPrimitive> prims = new HashSet<OsmPrimitive>(); 195 prims.addAll(merger.getAddedObjects()); 196 prims.addAll(merger.getChangedObjectsMap().keySet()); 197 return prims; 198 } 199 } 200 No newline at end of file
