Ticket #7489: 7489_unified2_updated.diff
File 7489_unified2_updated.diff, 33.1 KB (added by , 12 years ago) |
---|
-
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; … … 44 45 return; 45 46 } 46 47 } 47 MergeSourceBuildingVisitor builder = new MergeSourceBuildingVisitor(getEditLayer().data); 48 ((OsmDataLayer)targetLayer).mergeFrom(builder.build()); 48 49 MergeCommand cmd = new MergeCommand((OsmDataLayer)targetLayer, getEditLayer().data, true); 50 Main.main.undoRedo.add(cmd); 49 51 } 50 52 53 @Override 51 54 public void actionPerformed(ActionEvent e) { 52 55 if (getEditLayer() == null || getEditLayer().data.getAllSelected().isEmpty()) 53 56 return; -
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
16 16 17 17 import javax.swing.JOptionPane; 18 18 import org.openstreetmap.josm.Main; 19 import org.openstreetmap.josm.actions.AutoScaleAction; 20 import org.openstreetmap.josm.command.DownloadOsmCommand; 19 21 import org.openstreetmap.josm.data.Bounds; 20 22 import org.openstreetmap.josm.data.coor.LatLon; 21 23 import org.openstreetmap.josm.data.imagery.ImageryInfo; … … 264 266 if (targetLayer == null) { 265 267 targetLayer = getFirstDataLayer(); 266 268 } 267 targetLayer.mergeFrom(dataSet); 268 computeBboxAndCenterScale(); 269 targetLayer.onPostDownloadFromServer(); 269 Main.main.undoRedo.add(new DownloadOsmCommand(tr("Download primitives from bounding box"), targetLayer, dataSet)); 270 AutoScaleAction.zoomTo(dataSet.allPrimitives()); 270 271 } 271 272 272 273 suggestImageryLayers(); -
src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
102 102 103 103 public List<TestError> validationErrors = new ArrayList<TestError>(); 104 104 105 p rotectedvoid setRequiresSaveToFile(boolean newValue) {105 public void setRequiresSaveToFile(boolean newValue) { 106 106 boolean oldValue = requiresSaveToFile; 107 107 requiresSaveToFile = newValue; 108 108 if (oldValue != newValue) { … … 110 110 } 111 111 } 112 112 113 p rotectedvoid setRequiresUploadToServer(boolean newValue) {113 public void setRequiresUploadToServer(boolean newValue) { 114 114 boolean oldValue = requiresUploadToServer; 115 115 requiresUploadToServer = newValue; 116 116 if (oldValue != newValue) { … … 302 302 } 303 303 304 304 @Override public void mergeFrom(final Layer from) { 305 // TODO: make undo-able 305 306 final PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor(tr("Merging layers")); 306 307 monitor.setCancelable(false); 307 308 if (from instanceof OsmDataLayer && ((OsmDataLayer)from).isUploadDiscouraged()) { … … 317 318 * 318 319 * @param from the source data set 319 320 */ 321 @Deprecated 320 322 public void mergeFrom(final DataSet from) { 321 323 mergeFrom(from, null); 322 324 } … … 327 329 * 328 330 * @param from the source data set 329 331 */ 332 @Deprecated 330 333 public void mergeFrom(final DataSet from, ProgressMonitor progressMonitor) { 331 334 final DataSetMerger visitor = new DataSetMerger(data,from); 332 335 try { … … 378 381 * 379 382 * @param numNewConflicts the number of detected conflicts 380 383 */ 381 p rotectedvoid warnNumNewConflicts(int numNewConflicts) {384 public void warnNumNewConflicts(int numNewConflicts) { 382 385 if (numNewConflicts == 0) return; 383 386 384 387 String msg1 = trn( -
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/io/DownloadPrimitivesTask.java
8 8 import java.util.List; 9 9 import java.util.Set; 10 10 11 import javax.swing.SwingUtilities; 12 13 import org.openstreetmap.josm.Main; 11 14 import org.openstreetmap.josm.actions.AutoScaleAction; 15 import org.openstreetmap.josm.command.DownloadOsmCommand; 12 16 import org.openstreetmap.josm.data.osm.DataSet; 13 17 import org.openstreetmap.josm.data.osm.DataSetMerger; 14 18 import org.openstreetmap.josm.data.osm.Node; … … 81 85 } 82 86 GuiHelper.runInEDTAndWait(new Runnable() { 83 87 public void run() { 84 layer.mergeFrom(ds);88 Main.main.undoRedo.add(new DownloadOsmCommand(tr("Download primitives"), layer, ds)); 85 89 AutoScaleAction.zoomTo(ds.allPrimitives()); 86 layer.onPostDownloadFromServer();87 90 } 88 91 }); 89 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; … … 82 84 } 83 85 GuiHelper.runInEDTAndWait(new Runnable() { 84 86 public void run() { 85 layer.mergeFrom(ds); 86 layer.onPostDownloadFromServer(); 87 Main.main.undoRedo.add(new DownloadOsmCommand(tr("Update primitives"), layer, ds)); 87 88 } 88 89 }); 89 90 } -
src/org/openstreetmap/josm/data/osm/Node.java
219 219 * have an assigend OSM id, the IDs have to be the same. 220 220 * 221 221 * @param other the other primitive. Must not be null. 222 * @return true if the semantic or technical attributes were changed 222 223 * @throws IllegalArgumentException thrown if other is null. 223 224 * @throws DataIntegrityProblemException thrown if either this is new and other is not, or other is new and this is not 224 225 * @throws DataIntegrityProblemException thrown if other is new and other.getId() != this.getId() 225 226 */ 226 227 @Override 227 public void mergeFrom(OsmPrimitive other) { 228 public boolean mergeFrom(OsmPrimitive other) { 229 boolean changed; 228 230 boolean locked = writeLock(); 229 231 try { 230 super.mergeFrom(other);231 if (!other.isIncomplete() ) {232 changed = super.mergeFrom(other); 233 if (!other.isIncomplete() && !((Node)other).getCoor().equals((getCoor()))) { 232 234 setCoor(((Node)other).getCoor()); 235 changed = true; 233 236 } 234 237 } finally { 235 238 writeUnlock(locked); 236 239 } 240 return changed; 237 241 } 238 242 239 243 @Override public void load(PrimitiveData data) { -
src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
973 973 * Merges the technical and semantical attributes from <code>other</code> onto this. 974 974 * 975 975 * Both this and other must be new, or both must be assigned an OSM ID. If both this and <code>other</code> 976 * have an assig end OSM id, the IDs have to be the same.976 * have an assigned OSM id, the IDs have to be the same. 977 977 * 978 978 * @param other the other primitive. Must not be null. 979 * @return true if the semantic or technical attributes were changed 979 980 * @throws IllegalArgumentException thrown if other is null. 980 981 * @throws DataIntegrityProblemException thrown if either this is new and other is not, or other is new and this is not 981 982 * @throws DataIntegrityProblemException thrown if other isn't new and other.getId() != this.getId() 982 983 */ 983 public void mergeFrom(OsmPrimitive other) { 984 public boolean mergeFrom(OsmPrimitive other) { 985 if (hasEqualSemanticAttributes(other) && hasEqualTechnicalAttributes(other)) 986 return false; 987 984 988 boolean locked = writeLock(); 985 989 try { 986 990 CheckParameterUtil.ensureParameterNotNull(other, "other"); … … 999 1003 } finally { 1000 1004 writeUnlock(locked); 1001 1005 } 1006 return true; 1002 1007 } 1003 1008 1004 1009 /** -
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 { … … 188 201 List<OsmPrimitive> referrers = target.getReferrers(); 189 202 if (referrers.isEmpty()) { 190 203 resetPrimitive(target); 191 target.mergeFrom(source);192 204 target.setDeleted(true); 205 if (target.mergeFrom(source)) 206 changedObjectsMap.put(target, source.save()); 193 207 it.remove(); 194 208 flag = true; 195 209 } else { … … 214 228 for (OsmPrimitive osm: objectsToDelete) { 215 229 resetPrimitive(osm); 216 230 } 217 for (OsmPrimitive osm: objectsToDelete) { 218 osm.setDeleted(true); 219 osm.mergeFrom(sourceDataSet.getPrimitiveById(osm.getPrimitiveId())); 231 for (OsmPrimitive target: objectsToDelete) { 232 OsmPrimitive source = sourceDataSet.getPrimitiveById(target.getPrimitiveId()); 233 target.setDeleted(true); 234 if (target.mergeFrom(source)) 235 changedObjectsMap.put(target, source.save()); 220 236 } 221 237 } 222 238 } … … 306 322 // target is incomplete, source completes it 307 323 // => merge source into target 308 324 // 309 target.mergeFrom(source); 325 if (target.mergeFrom(source)) 326 changedObjectsMap.put(target, source.save()); 310 327 objectsWithChildrenToMerge.add(source.getPrimitiveId()); 311 328 } else if (!target.isIncomplete() && source.isIncomplete()) { 312 329 // target is complete and source is incomplete … … 332 349 if (targetDataSet.getPrimitiveById(referrer.getPrimitiveId()) == null) { 333 350 addConflict(new Conflict<OsmPrimitive>(target, source, true)); 334 351 target.setDeleted(false); 352 changedObjectsMap.put(target, source.save()); 335 353 break; 336 354 } 337 355 } … … 343 361 } else if (! target.isModified() && source.isModified()) { 344 362 // target not modified. We can assume that source is the most recent version. 345 363 // clone it into target. 346 target.mergeFrom(source); 364 if (target.mergeFrom(source)) 365 changedObjectsMap.put(target, source.save()); 347 366 objectsWithChildrenToMerge.add(source.getPrimitiveId()); 348 367 } else if (! target.isModified() && !source.isModified() && target.getVersion() == source.getVersion()) { 349 368 // both not modified. Merge nevertheless. 350 369 // This helps when updating "empty" relations, see #4295 351 target.mergeFrom(source); 370 if (target.mergeFrom(source)) 371 changedObjectsMap.put(target, source.save()); 352 372 objectsWithChildrenToMerge.add(source.getPrimitiveId()); 353 373 } else if (! target.isModified() && !source.isModified() && target.getVersion() < source.getVersion()) { 354 374 // my not modified but other is newer. clone other onto mine. 355 375 // 356 target.mergeFrom(source); 376 if (target.mergeFrom(source)) 377 changedObjectsMap.put(target, source.save()); 357 378 objectsWithChildrenToMerge.add(source.getPrimitiveId()); 358 379 } else if (target.isModified() && ! source.isModified() && target.getVersion() == source.getVersion()) { 359 380 // target is same as source but target is modified 360 381 // => keep target and reset modified flag if target and source are semantically equal 361 382 if (target.hasEqualSemanticAttributes(source)) { 362 383 target.setModified(false); 384 changedObjectsMap.put(target, source.save()); 363 385 } 364 386 } else if (source.isDeleted() != target.isDeleted()) { 365 387 // target is modified and deleted state differs. … … 376 398 // technical attributes like timestamp or user information. Semantic 377 399 // attributes should already be equal if we get here. 378 400 // 379 target.mergeFrom(source); 401 if (target.mergeFrom(source)) 402 changedObjectsMap.put(target, source.save()); 380 403 objectsWithChildrenToMerge.add(source.getPrimitiveId()); 381 404 } 382 405 return true; … … 437 460 if (progressMonitor != null) { 438 461 progressMonitor.finishTask(); 439 462 } 463 464 undoState = UndoState.MERGED; 440 465 } 441 466 442 467 /** … … 456 481 public ConflictCollection getConflicts() { 457 482 return conflicts; 458 483 } 484 485 /** 486 * Undos the merge operation. 487 */ 488 public void unmerge() { 489 if (undoState != UndoState.MERGED && undoState != UndoState.REDONE) { 490 throw new AssertionError(); 491 } 492 493 targetDataSet.beginUpdate(); 494 495 for (PrimitiveId osm : addedObjects) { 496 targetDataSet.removePrimitive(osm); 497 } 498 499 for (Map.Entry<OsmPrimitive, PrimitiveData> e : changedObjectsMap.entrySet()) { 500 // restore previous state and save current state for opposite undo action 501 PrimitiveData old = e.getKey().save(); 502 e.getKey().load(e.getValue()); 503 e.setValue(old); 504 } 505 506 targetDataSet.endUpdate(); 507 undoState = UndoState.UNDONE; 508 } 509 510 /** 511 * Re-merge objects with dataset. Identical results as {@see #merge()}, but performed 512 * after {@see #unmerge()} has been called. 513 */ 514 public void remerge() { 515 if (undoState != UndoState.UNDONE) { 516 throw new AssertionError(); 517 } 518 519 targetDataSet.beginUpdate(); 520 521 // add back objects in order that they can be referenced by other objects 522 for (OsmPrimitive osm : OsmPrimitive.getFilteredList(addedObjects, Node.class)) { 523 targetDataSet.addPrimitive(osm); 524 } 525 for (OsmPrimitive osm : OsmPrimitive.getFilteredList(addedObjects, Way.class)) { 526 targetDataSet.addPrimitive(osm); 527 } 528 for (OsmPrimitive osm : OsmPrimitive.getFilteredList(addedObjects, Relation.class)) { 529 targetDataSet.addPrimitive(osm); 530 } 531 532 for (Map.Entry<OsmPrimitive, PrimitiveData> e : changedObjectsMap.entrySet()) { 533 // restore previous state and save current state for opposite undo action 534 PrimitiveData old = e.getKey().save(); 535 e.getKey().load(e.getValue()); 536 e.setValue(old); 537 } 538 539 targetDataSet.endUpdate(); 540 undoState = UndoState.REDONE; 541 } 542 543 public Map<OsmPrimitive, PrimitiveData> getChangedObjectsMap() { 544 return changedObjectsMap; 545 } 546 547 public Set<OsmPrimitive> getAddedObjects() { 548 return addedObjects; 549 } 459 550 } -
src/org/openstreetmap/josm/command/DownloadOsmCommand.java
1 // License: GPL. Copyright 2012 by Josh Doe and others 2 package org.openstreetmap.josm.command; 3 4 import java.util.ArrayList; 5 import java.util.Collection; 6 import javax.swing.Icon; 7 import org.openstreetmap.josm.data.osm.DataSet; 8 import org.openstreetmap.josm.data.osm.OsmPrimitive; 9 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 10 import static org.openstreetmap.josm.tools.I18n.marktr; 11 import static org.openstreetmap.josm.tools.I18n.tr; 12 import org.openstreetmap.josm.tools.ImageProvider; 13 14 /** 15 * A command that merges a downloaded dataset to a layer. 16 * 17 * @author joshdoe 18 */ 19 public class DownloadOsmCommand extends Command { 20 private OsmDataLayer targetLayer; 21 private DataSet sourceDataSet; 22 private MergeCommand mergeCommand; 23 private boolean requiresSaveToFile; 24 private boolean requiresUploadToServer; 25 private String commandSummary; 26 27 public DownloadOsmCommand(String commandSummary, OsmDataLayer targetLayer, DataSet sourceDataSet) { 28 super(targetLayer); 29 this.commandSummary = commandSummary; 30 this.targetLayer = targetLayer; 31 this.sourceDataSet = sourceDataSet; 32 mergeCommand = new MergeCommand(targetLayer, sourceDataSet, false); 33 } 34 35 @Override 36 public boolean executeCommand() { 37 if (!mergeCommand.executeCommand()) { 38 return false; 39 } 40 requiresSaveToFile = targetLayer.requiresSaveToFile(); 41 requiresUploadToServer = targetLayer.requiresUploadToServer(); 42 targetLayer.setRequiresSaveToFile(true); 43 targetLayer.setRequiresUploadToServer(sourceDataSet.isModified()); 44 return true; 45 } 46 47 @Override 48 public void undoCommand() { 49 mergeCommand.undoCommand(); 50 boolean oldValue; 51 oldValue = targetLayer.requiresSaveToFile(); 52 targetLayer.setRequiresSaveToFile(requiresSaveToFile); 53 requiresSaveToFile = oldValue; 54 oldValue = targetLayer.requiresUploadToServer(); 55 targetLayer.setRequiresUploadToServer(requiresUploadToServer); 56 requiresUploadToServer = oldValue; 57 } 58 59 @Override 60 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 61 throw new UnsupportedOperationException("Not supported yet."); 62 } 63 64 @Override 65 public String getDescriptionText() { 66 return commandSummary; 67 } 68 69 @Override 70 public Icon getDescriptionIcon() { 71 return ImageProvider.get("dialogs", "down"); 72 } 73 74 @Override 75 public Collection<? extends OsmPrimitive> getParticipatingPrimitives() { 76 return sourceDataSet.allPrimitives(); 77 } 78 79 private class DownloadPseudoCommand extends PseudoCommand { 80 81 @Override 82 public String getDescriptionText() { 83 return tr(marktr("Download {0} nodes, {1} ways, {2} relations"), 84 sourceDataSet.getNodes().size(), 85 sourceDataSet.getWays().size(), 86 sourceDataSet.getRelations().size()); 87 } 88 89 @Override 90 public Collection<? extends OsmPrimitive> getParticipatingPrimitives() { 91 return sourceDataSet.allPrimitives(); 92 } 93 94 @Override 95 public Icon getDescriptionIcon() { 96 return ImageProvider.get("dialogs", "down"); 97 } 98 99 } 100 @Override 101 public Collection<PseudoCommand> getChildren() { 102 ArrayList<PseudoCommand> children = new ArrayList<PseudoCommand>(); 103 children.add(new DownloadPseudoCommand()); 104 children.add(mergeCommand); 105 return children; 106 } 107 108 } -
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