Ticket #7489: 7489_unified2_updated.diff

File 7489_unified2_updated.diff, 33.1 KB (added by bastiK, 12 years ago)

patch updated for [5471]

  • src/org/openstreetmap/josm/actions/MergeSelectionAction.java

     
    88import java.awt.event.KeyEvent;
    99import java.util.Collection;
    1010import java.util.List;
     11import org.openstreetmap.josm.Main;
     12import org.openstreetmap.josm.command.MergeCommand;
    1113
    1214import org.openstreetmap.josm.data.osm.DataSet;
    1315import org.openstreetmap.josm.data.osm.OsmPrimitive;
    14 import org.openstreetmap.josm.data.osm.visitor.MergeSourceBuildingVisitor;
    1516import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
    1617import org.openstreetmap.josm.gui.layer.Layer;
    1718import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     
    4445                return;
    4546            }
    4647        }
    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);
    4951    }
    5052
     53    @Override
    5154    public void actionPerformed(ActionEvent e) {
    5255        if (getEditLayer() == null || getEditLayer().data.getAllSelected().isEmpty())
    5356            return;
  • src/org/openstreetmap/josm/actions/UpdateSelectionAction.java

     
    1313import javax.swing.JOptionPane;
    1414
    1515import org.openstreetmap.josm.Main;
     16import org.openstreetmap.josm.command.DownloadOsmCommand;
    1617import org.openstreetmap.josm.data.osm.DataSet;
    1718import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1819import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     
    3940        reader.append(getCurrentDataSet(),id, type);
    4041        try {
    4142            DataSet ds = reader.parseOsm(NullProgressMonitor.INSTANCE);
    42             Main.map.mapView.getEditLayer().mergeFrom(ds);
     43            Main.main.undoRedo.add(new DownloadOsmCommand(tr("Update selected primitives"), Main.map.mapView.getEditLayer(), ds));
    4344        } catch(Exception e) {
    4445            ExceptionDialogUtil.explainException(e);
    4546        }
  • src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java

     
    1616
    1717import javax.swing.JOptionPane;
    1818import org.openstreetmap.josm.Main;
     19import org.openstreetmap.josm.actions.AutoScaleAction;
     20import org.openstreetmap.josm.command.DownloadOsmCommand;
    1921import org.openstreetmap.josm.data.Bounds;
    2022import org.openstreetmap.josm.data.coor.LatLon;
    2123import org.openstreetmap.josm.data.imagery.ImageryInfo;
     
    264266                if (targetLayer == null) {
    265267                    targetLayer = getFirstDataLayer();
    266268                }
    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());
    270271            }
    271272
    272273            suggestImageryLayers();
  • src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java

     
    102102
    103103    public List<TestError> validationErrors = new ArrayList<TestError>();
    104104
    105     protected void setRequiresSaveToFile(boolean newValue) {
     105    public void setRequiresSaveToFile(boolean newValue) {
    106106        boolean oldValue = requiresSaveToFile;
    107107        requiresSaveToFile = newValue;
    108108        if (oldValue != newValue) {
     
    110110        }
    111111    }
    112112
    113     protected void setRequiresUploadToServer(boolean newValue) {
     113    public void setRequiresUploadToServer(boolean newValue) {
    114114        boolean oldValue = requiresUploadToServer;
    115115        requiresUploadToServer = newValue;
    116116        if (oldValue != newValue) {
     
    302302    }
    303303
    304304    @Override public void mergeFrom(final Layer from) {
     305        // TODO: make undo-able
    305306        final PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor(tr("Merging layers"));
    306307        monitor.setCancelable(false);
    307308        if (from instanceof OsmDataLayer && ((OsmDataLayer)from).isUploadDiscouraged()) {
     
    317318     *
    318319     * @param from  the source data set
    319320     */
     321    @Deprecated
    320322    public void mergeFrom(final DataSet from) {
    321323        mergeFrom(from, null);
    322324    }
     
    327329     *
    328330     * @param from  the source data set
    329331     */
     332    @Deprecated
    330333    public void mergeFrom(final DataSet from, ProgressMonitor progressMonitor) {
    331334        final DataSetMerger visitor = new DataSetMerger(data,from);
    332335        try {
     
    378381     *
    379382     * @param numNewConflicts the number of detected conflicts
    380383     */
    381     protected void warnNumNewConflicts(int numNewConflicts) {
     384    public void warnNumNewConflicts(int numNewConflicts) {
    382385        if (numNewConflicts == 0) return;
    383386
    384387        String msg1 = trn(
  • src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationMemberTask.java

     
    1313import javax.swing.SwingUtilities;
    1414
    1515import org.openstreetmap.josm.Main;
     16import org.openstreetmap.josm.command.DownloadOsmCommand;
    1617import org.openstreetmap.josm.data.osm.DataSet;
    1718import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1819import org.openstreetmap.josm.data.osm.Relation;
     
    135136            SwingUtilities.invokeLater(
    136137                    new Runnable() {
    137138                        public void run() {
    138                             curLayer.mergeFrom(dataSet);
    139                             curLayer.onPostDownloadFromServer();
     139                            Main.main.undoRedo.add(new DownloadOsmCommand(tr("Download relation members"), curLayer, dataSet));
    140140                        }
    141141                    }
    142142            );
  • src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationTask.java

     
    99import javax.swing.SwingUtilities;
    1010
    1111import org.openstreetmap.josm.Main;
     12import org.openstreetmap.josm.command.DownloadOsmCommand;
    1213import org.openstreetmap.josm.data.osm.DataSet;
    1314import org.openstreetmap.josm.data.osm.DataSetMerger;
    1415import org.openstreetmap.josm.data.osm.Relation;
     
    9899            SwingUtilities.invokeAndWait(
    99100                    new Runnable() {
    100101                        public void run() {
    101                             layer.mergeFrom(allDownloads);
    102                             layer.onPostDownloadFromServer();
     102                            Main.main.undoRedo.add(new DownloadOsmCommand(tr("Download relation(s)"), layer, allDownloads));
    103103                            Main.map.repaint();
    104104                        }
    105105                    }
  • src/org/openstreetmap/josm/gui/io/DownloadPrimitivesTask.java

     
    88import java.util.List;
    99import java.util.Set;
    1010
     11import javax.swing.SwingUtilities;
     12
     13import org.openstreetmap.josm.Main;
    1114import org.openstreetmap.josm.actions.AutoScaleAction;
     15import org.openstreetmap.josm.command.DownloadOsmCommand;
    1216import org.openstreetmap.josm.data.osm.DataSet;
    1317import org.openstreetmap.josm.data.osm.DataSetMerger;
    1418import org.openstreetmap.josm.data.osm.Node;
     
    8185        }
    8286        GuiHelper.runInEDTAndWait(new Runnable() {
    8387            public void run() {
    84                 layer.mergeFrom(ds);
     88                Main.main.undoRedo.add(new DownloadOsmCommand(tr("Download primitives"), layer, ds));
    8589                AutoScaleAction.zoomTo(ds.allPrimitives());
    86                 layer.onPostDownloadFromServer();
    8790            }
    8891        });
    8992    }
  • src/org/openstreetmap/josm/gui/io/UpdatePrimitivesTask.java

     
    1010import java.util.Collections;
    1111
    1212import javax.swing.SwingUtilities;
     13import org.openstreetmap.josm.Main;
     14import org.openstreetmap.josm.command.DownloadOsmCommand;
    1315
    1416import org.openstreetmap.josm.data.osm.DataSet;
    1517import org.openstreetmap.josm.data.osm.DataSetMerger;
     
    8284        }
    8385        GuiHelper.runInEDTAndWait(new Runnable() {
    8486            public void run() {
    85                 layer.mergeFrom(ds);
    86                 layer.onPostDownloadFromServer();
     87                Main.main.undoRedo.add(new DownloadOsmCommand(tr("Update primitives"), layer, ds));
    8788            }
    8889        });
    8990    }
  • src/org/openstreetmap/josm/data/osm/Node.java

     
    219219     * have an assigend OSM id, the IDs have to be the same.
    220220     *
    221221     * @param other the other primitive. Must not be null.
     222     * @return true if the semantic or technical attributes were changed
    222223     * @throws IllegalArgumentException thrown if other is null.
    223224     * @throws DataIntegrityProblemException thrown if either this is new and other is not, or other is new and this is not
    224225     * @throws DataIntegrityProblemException thrown if other is new and other.getId() != this.getId()
    225226     */
    226227    @Override
    227     public void mergeFrom(OsmPrimitive other) {
     228    public boolean mergeFrom(OsmPrimitive other) {
     229        boolean changed;
    228230        boolean locked = writeLock();
    229231        try {
    230             super.mergeFrom(other);
    231             if (!other.isIncomplete()) {
     232            changed = super.mergeFrom(other);
     233            if (!other.isIncomplete() && !((Node)other).getCoor().equals((getCoor()))) {
    232234                setCoor(((Node)other).getCoor());
     235                changed = true;
    233236            }
    234237        } finally {
    235238            writeUnlock(locked);
    236239        }
     240        return changed;
    237241    }
    238242
    239243    @Override public void load(PrimitiveData data) {
  • src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

     
    973973     * Merges the technical and semantical attributes from <code>other</code> onto this.
    974974     *
    975975     * 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 assigend OSM id, the IDs have to be the same.
     976     * have an assigned OSM id, the IDs have to be the same.
    977977     *
    978978     * @param other the other primitive. Must not be null.
     979     * @return true if the semantic or technical attributes were changed
    979980     * @throws IllegalArgumentException thrown if other is null.
    980981     * @throws DataIntegrityProblemException thrown if either this is new and other is not, or other is new and this is not
    981982     * @throws DataIntegrityProblemException thrown if other isn't new and other.getId() != this.getId()
    982983     */
    983     public void mergeFrom(OsmPrimitive other) {
     984    public boolean mergeFrom(OsmPrimitive other) {
     985        if (hasEqualSemanticAttributes(other) && hasEqualTechnicalAttributes(other))
     986            return false;
     987
    984988        boolean locked = writeLock();
    985989        try {
    986990            CheckParameterUtil.ensureParameterNotNull(other, "other");
     
    9991003        } finally {
    10001004            writeUnlock(locked);
    10011005        }
     1006        return true;
    10021007    }
    10031008
    10041009    /**
  • src/org/openstreetmap/josm/data/osm/DataSetMerger.java

     
    4343     */
    4444    private final Set<PrimitiveId> objectsWithChildrenToMerge;
    4545    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;
    4654
    4755    /**
    4856     * constructor
     
    6169        mergedMap = new HashMap<PrimitiveId, PrimitiveId>();
    6270        objectsWithChildrenToMerge = new HashSet<PrimitiveId>();
    6371        objectsToDelete = new HashSet<OsmPrimitive>();
     72        changedObjectsMap = new HashMap<OsmPrimitive, PrimitiveData>();
     73        addedObjects = new HashSet<OsmPrimitive>();
     74        undoState = UndoState.INIT;
    6475    }
    6576
    6677    /**
     
    7788     * @param <P>  the type of the other primitive
    7889     * @param source  the other primitive
    7990     */
    80     protected void mergePrimitive(OsmPrimitive source, Collection<? extends OsmPrimitive> candidates) {
     91        protected void mergePrimitive(OsmPrimitive source, Collection<? extends OsmPrimitive> candidates) {
    8192        if (!source.isNew() ) {
    8293            // try to merge onto a matching primitive with the same
    8394            // defined id
     
    107118                    target.setTimestamp(source.getTimestamp());
    108119                    target.setModified(source.isModified());
    109120                    objectsWithChildrenToMerge.add(source.getPrimitiveId());
     121                    changedObjectsMap.put(target, source.save());
    110122                    return;
    111123                }
    112124            }
     
    126138        targetDataSet.addPrimitive(target);
    127139        mergedMap.put(source.getPrimitiveId(), target.getPrimitiveId());
    128140        objectsWithChildrenToMerge.add(source.getPrimitiveId());
     141        addedObjects.add(target);
    129142    }
    130143
    131144    protected OsmPrimitive getMergeTarget(OsmPrimitive mergeSource) throws IllegalStateException {
     
    188201                List<OsmPrimitive> referrers = target.getReferrers();
    189202                if (referrers.isEmpty()) {
    190203                    resetPrimitive(target);
    191                     target.mergeFrom(source);
    192204                    target.setDeleted(true);
     205                    if (target.mergeFrom(source))
     206                        changedObjectsMap.put(target, source.save());
    193207                    it.remove();
    194208                    flag = true;
    195209                } else {
     
    214228            for (OsmPrimitive osm: objectsToDelete) {
    215229                resetPrimitive(osm);
    216230            }
    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());
    220236            }
    221237        }
    222238    }
     
    306322            // target is incomplete, source completes it
    307323            // => merge source into target
    308324            //
    309             target.mergeFrom(source);
     325            if (target.mergeFrom(source))
     326                changedObjectsMap.put(target, source.save());
    310327            objectsWithChildrenToMerge.add(source.getPrimitiveId());
    311328        } else if (!target.isIncomplete() && source.isIncomplete()) {
    312329            // target is complete and source is incomplete
     
    332349                if (targetDataSet.getPrimitiveById(referrer.getPrimitiveId()) == null) {
    333350                    addConflict(new Conflict<OsmPrimitive>(target, source, true));
    334351                    target.setDeleted(false);
     352                    changedObjectsMap.put(target, source.save());
    335353                    break;
    336354                }
    337355            }
     
    343361        } else if (! target.isModified() && source.isModified()) {
    344362            // target not modified. We can assume that source is the most recent version.
    345363            // clone it into target.
    346             target.mergeFrom(source);
     364            if (target.mergeFrom(source))
     365                changedObjectsMap.put(target, source.save());
    347366            objectsWithChildrenToMerge.add(source.getPrimitiveId());
    348367        } else if (! target.isModified() && !source.isModified() && target.getVersion() == source.getVersion()) {
    349368            // both not modified. Merge nevertheless.
    350369            // This helps when updating "empty" relations, see #4295
    351             target.mergeFrom(source);
     370            if (target.mergeFrom(source))
     371                changedObjectsMap.put(target, source.save());
    352372            objectsWithChildrenToMerge.add(source.getPrimitiveId());
    353373        } else if (! target.isModified() && !source.isModified() && target.getVersion() < source.getVersion()) {
    354374            // my not modified but other is newer. clone other onto mine.
    355375            //
    356             target.mergeFrom(source);
     376            if (target.mergeFrom(source))
     377                changedObjectsMap.put(target, source.save());
    357378            objectsWithChildrenToMerge.add(source.getPrimitiveId());
    358379        } else if (target.isModified() && ! source.isModified() && target.getVersion() == source.getVersion()) {
    359380            // target is same as source but target is modified
    360381            // => keep target and reset modified flag if target and source are semantically equal
    361382            if (target.hasEqualSemanticAttributes(source)) {
    362383                target.setModified(false);
     384                changedObjectsMap.put(target, source.save());
    363385            }
    364386        } else if (source.isDeleted() != target.isDeleted()) {
    365387            // target is modified and deleted state differs.
     
    376398            // technical attributes like timestamp or user information. Semantic
    377399            // attributes should already be equal if we get here.
    378400            //
    379             target.mergeFrom(source);
     401            if (target.mergeFrom(source))
     402                changedObjectsMap.put(target, source.save());
    380403            objectsWithChildrenToMerge.add(source.getPrimitiveId());
    381404        }
    382405        return true;
     
    437460        if (progressMonitor != null) {
    438461            progressMonitor.finishTask();
    439462        }
     463       
     464        undoState = UndoState.MERGED;
    440465    }
    441466
    442467    /**
     
    456481    public ConflictCollection getConflicts() {
    457482        return conflicts;
    458483    }
     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    }
    459550}
  • src/org/openstreetmap/josm/command/DownloadOsmCommand.java

     
     1// License: GPL. Copyright 2012 by Josh Doe and others
     2package org.openstreetmap.josm.command;
     3
     4import java.util.ArrayList;
     5import java.util.Collection;
     6import javax.swing.Icon;
     7import org.openstreetmap.josm.data.osm.DataSet;
     8import org.openstreetmap.josm.data.osm.OsmPrimitive;
     9import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     10import static org.openstreetmap.josm.tools.I18n.marktr;
     11import static org.openstreetmap.josm.tools.I18n.tr;
     12import org.openstreetmap.josm.tools.ImageProvider;
     13
     14/**
     15 * A command that merges a downloaded dataset to a layer.
     16 *
     17 * @author joshdoe
     18 */
     19public 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
     2package org.openstreetmap.josm.command;
     3
     4import java.awt.geom.Area;
     5import java.util.Collection;
     6import java.util.HashSet;
     7import javax.swing.Icon;
     8import javax.swing.JOptionPane;
     9import org.openstreetmap.josm.Main;
     10import org.openstreetmap.josm.data.conflict.Conflict;
     11import org.openstreetmap.josm.data.osm.*;
     12import org.openstreetmap.josm.data.osm.visitor.MergeSourceBuildingVisitor;
     13import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     14import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
     15import org.openstreetmap.josm.tools.CheckParameterUtil;
     16import static org.openstreetmap.josm.tools.I18n.marktr;
     17import static org.openstreetmap.josm.tools.I18n.tr;
     18import org.openstreetmap.josm.tools.ImageProvider;
     19
     20/**
     21 * A command that merges objects from one layer to another.
     22 *
     23 * @author joshdoe
     24 */
     25public 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