Index: /trunk/src/org/openstreetmap/josm/data/osm/DataSetMerger.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/DataSetMerger.java	(revision 2442)
+++ /trunk/src/org/openstreetmap/josm/data/osm/DataSetMerger.java	(revision 2443)
@@ -26,7 +26,7 @@
 
     /** the target dataset for merging */
-    private final DataSet myDataSet;
+    private final DataSet targetDataSet;
     /** the source dataset where primitives are merged from */
-    private final DataSet theirDataSet;
+    private final DataSet sourceDataSet;
 
     /**
@@ -38,5 +38,7 @@
      * to relation members) after the first phase of merging
      */
-    private Set<Long> fixReferences;
+    private Set<Long> childrenToMerge;
+
+    private Set<OsmPrimitive> deletedObjectsToUnlink;
 
     /**
@@ -45,16 +47,17 @@
      * The visitor will merge <code>theirDataSet</code> onto <code>myDataSet</code>
      *
-     * @param myDataSet  dataset with my primitives. Must not be null.
-     * @param theirDataSet dataset with their primitives. Ignored, if null.
+     * @param targetDataSet  dataset with my primitives. Must not be null.
+     * @param sourceDataSet dataset with their primitives. Ignored, if null.
      * @throws IllegalArgumentException thrown if myDataSet is null
      */
-    public DataSetMerger(DataSet myDataSet, DataSet theirDataSet) throws IllegalArgumentException {
-        if (myDataSet == null)
-            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null"));
-        this.myDataSet = myDataSet;
-        this.theirDataSet = theirDataSet;
+    public DataSetMerger(DataSet targetDataSet, DataSet sourceDataSet) throws IllegalArgumentException {
+        if (targetDataSet == null)
+            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "targetDataSet"));
+        this.targetDataSet = targetDataSet;
+        this.sourceDataSet = sourceDataSet;
         conflicts = new ConflictCollection();
         mergedMap = new HashMap<Long, Long>();
-        fixReferences = new HashSet<Long>();
+        childrenToMerge = new HashSet<Long>();
+        deletedObjectsToUnlink = new HashSet<OsmPrimitive>();
     }
 
@@ -71,14 +74,14 @@
      *
      * @param <P>  the type of the other primitive
-     * @param other  the other primitive
-     */
-    protected <P extends OsmPrimitive> void mergePrimitive(P other) {
-        if (!other.isNew() ) {
+     * @param source  the other primitive
+     */
+    protected <P extends OsmPrimitive> void mergePrimitive(P source) {
+        if (!source.isNew() ) {
             // try to merge onto a matching primitive with the same
             // defined id
             //
-            if (mergeById(other))
+            if (mergeById(source))
                 return;
-            if (!other.isVisible())
+            if (!source.isVisible())
                 // ignore it
                 return;
@@ -88,27 +91,27 @@
             //
             Collection<? extends OsmPrimitive> candidates = null;
-            switch(other.getType()) {
-            case NODE: candidates = myDataSet.getNodes(); break;
-            case WAY: candidates  =myDataSet.getWays(); break;
-            case RELATION: candidates = myDataSet.getRelations(); break;
-            }
-            for (OsmPrimitive my : candidates) {
-                if (!my.isNew()) {
+            switch(source.getType()) {
+            case NODE: candidates = targetDataSet.getNodes(); break;
+            case WAY: candidates  =targetDataSet.getWays(); break;
+            case RELATION: candidates = targetDataSet.getRelations(); break;
+            }
+            for (OsmPrimitive target : candidates) {
+                if (!target.isNew()) {
                     continue;
                 }
-                if (my.hasEqualSemanticAttributes(other)) {
-                    mergedMap.put(other.getUniqueId(), my.getUniqueId());
-                    if (my.isDeleted() != other.isDeleted()) {
+                if (target.hasEqualSemanticAttributes(source)) {
+                    mergedMap.put(source.getUniqueId(), target.getUniqueId());
+                    if (target.isDeleted() != source.isDeleted()) {
                         // differences in deleted state have to be merged manually
                         //
-                        conflicts.add(my, other);
+                        conflicts.add(target, source);
                     } else {
                         // copy the technical attributes from other
                         // version
-                        my.setVisible(other.isVisible());
-                        my.setUser(other.getUser());
-                        my.setTimestamp(other.getTimestamp());
-                        my.setModified(other.isModified());
-                        fixReferences.add(other.getUniqueId());
+                        target.setVisible(source.isVisible());
+                        target.setUser(source.getUser());
+                        target.setTimestamp(source.getTimestamp());
+                        target.setModified(source.isModified());
+                        childrenToMerge.add(source.getUniqueId());
                     }
                     return;
@@ -118,16 +121,16 @@
 
         // If we get here we didn't find a suitable primitive in
-        // my dataset. Create a clone and add it to my dataset.
+        // the target dataset. Create a clone and add it to the target dataset.
         //
-        OsmPrimitive my = null;
-        switch(other.getType()) {
-        case NODE: my = other.isNew() ? new Node() : new Node(other.getId()); break;
-        case WAY: my = other.isNew() ? new Way() : new Way(other.getId()); break;
-        case RELATION: my = other.isNew() ? new Relation() : new Relation(other.getId()); break;
-        }
-        my.mergeFrom(other);
-        myDataSet.addPrimitive(my);
-        mergedMap.put(other.getUniqueId(), my.getUniqueId());
-        fixReferences.add(other.getUniqueId());
+        OsmPrimitive target = null;
+        switch(source.getType()) {
+        case NODE: target = source.isNew() ? new Node() : new Node(source.getId()); break;
+        case WAY: target = source.isNew() ? new Way() : new Way(source.getId()); break;
+        case RELATION: target = source.isNew() ? new Relation() : new Relation(source.getId()); break;
+        }
+        target.mergeFrom(source);
+        targetDataSet.addPrimitive(target);
+        mergedMap.put(source.getUniqueId(), target.getUniqueId());
+        childrenToMerge.add(source.getUniqueId());
     }
 
@@ -136,5 +139,5 @@
         if (targetId == null)
             throw new RuntimeException(tr("Missing merge target for way with id {0}", mergeSource.getUniqueId()));
-        return myDataSet.getPrimitiveById(targetId, mergeSource.getType());
+        return targetDataSet.getPrimitiveById(targetId, mergeSource.getType());
     }
 
@@ -156,175 +159,158 @@
      */
     public void fixReferences() {
-        for (Way w : theirDataSet.getWays()) {
-            if (!conflicts.hasConflictForTheir(w) && fixReferences.contains(w.getUniqueId())) {
+        for (Way w : sourceDataSet.getWays()) {
+            if (!conflicts.hasConflictForTheir(w) && childrenToMerge.contains(w.getUniqueId())) {
                 mergeNodeList(w);
                 fixIncomplete(w);
             }
         }
-        for (Relation r : theirDataSet.getRelations()) {
-            if (!conflicts.hasConflictForTheir(r) && fixReferences.contains(r.getUniqueId())) {
+        for (Relation r : sourceDataSet.getRelations()) {
+            if (!conflicts.hasConflictForTheir(r) && childrenToMerge.contains(r.getUniqueId())) {
                 mergeRelationMembers(r);
             }
         }
-    }
-
-    private void mergeNodeList(Way other) {
-        Way myWay = (Way)getMergeTarget(other);
-        if (myWay == null)
-            throw new RuntimeException(tr("Missing merge target for way with id {0}", other.getUniqueId()));
-
-        List<Node> myNodes = new LinkedList<Node>();
-        for (Node otherNode : other.getNodes()) {
-            Node myNode = (Node)getMergeTarget(otherNode);
-            if (myNode != null) {
-                if (!myNode.isDeleted()) {
-                    myNodes.add(myNode);
+        for (OsmPrimitive source: deletedObjectsToUnlink) {
+            OsmPrimitive target = getMergeTarget(source);
+            if (target == null)
+                throw new RuntimeException(tr("Missing merge target for object with id {0}", source.getUniqueId()));
+            targetDataSet.unlinkReferencesToPrimitive(target);
+        }
+    }
+
+    /**
+     * Merges the node list of a source way onto its target way.
+     * 
+     * @param source the source way
+     * @throws IllegalStateException thrown if no target way can be found for the source way
+     * @throws IllegalStateException thrown if there isn't a target node for one of the nodes in the source way
+     * 
+     */
+    private void mergeNodeList(Way source) throws IllegalStateException {
+        Way target = (Way)getMergeTarget(source);
+        if (target == null)
+            throw new IllegalStateException(tr("Missing merge target for way with id {0}", source.getUniqueId()));
+
+        List<Node> newNodes = new LinkedList<Node>();
+        for (Node sourceNode : source.getNodes()) {
+            Node targetNode = (Node)getMergeTarget(sourceNode);
+            if (targetNode != null) {
+                if (!targetNode.isDeleted() && targetNode.isVisible()) {
+                    newNodes.add(targetNode);
+                } else {
+                    target.setModified(true);
                 }
             } else
-                throw new RuntimeException(tr("Missing merge target for node with id {0}", otherNode.getUniqueId()));
-        }
-
-        // check whether the node list has changed. If so, set the modified flag on the way
-        //
-        if (myWay.getNodes().size() != myNodes.size()) {
-            myWay.setModified(true);
-        } else {
-            for (int i=0; i< myWay.getNodesCount();i++) {
-                Node n1 = myWay.getNode(i);
-                Node n2 = myNodes.get(i);
-                if (n1.isNew() ^ n2.isNew()) {
-                    myWay.setModified(true);
-                    break;
-                } else if (n1.isNew() && n1 != n2) {
-                    myWay.setModified(true);
-                    break;
-                } else if (! n1.isNew() && n1.getId() != n2.getId()) {
-                    myWay.setModified(true);
-                    break;
-                }
-            }
-        }
-        myWay.setNodes(myNodes);
-    }
-
-    private void mergeRelationMembers(Relation other) {
-        Relation myRelation = (Relation) getMergeTarget(other);
-        if (myRelation == null)
-            throw new RuntimeException(tr("Missing merge target for relation with id {0}", other.getUniqueId()));
+                throw new IllegalStateException(tr("Missing merge target for node with id {0}", sourceNode.getUniqueId()));
+        }
+        target.setNodes(newNodes);
+    }
+
+
+    /**
+     * Merges the relation members of a source relation onto the corresponding target relation.
+     * @param source the source relation
+     * @throws IllegalStateException thrown if there is no corresponding target relation
+     * @throws IllegalStateException thrown if there isn't a corresponding target object for one of the relation
+     * members in source
+     */
+    private void mergeRelationMembers(Relation source) throws IllegalStateException {
+        Relation target = (Relation) getMergeTarget(source);
+        if (target == null)
+            throw new IllegalStateException(tr("Missing merge target for relation with id {0}", source.getUniqueId()));
         LinkedList<RelationMember> newMembers = new LinkedList<RelationMember>();
-        for (RelationMember otherMember : other.getMembers()) {
-            OsmPrimitive mergedMember = getMergeTarget(otherMember.getMember());
-            if (mergedMember == null)
-                throw new RuntimeException(tr("Missing merge target of type {0} with id {1}", mergedMember.getType(), mergedMember.getUniqueId()));
-            if (! mergedMember.isDeleted()) {
-                RelationMember newMember = new RelationMember(otherMember.getRole(), mergedMember);
+        for (RelationMember sourceMember : source.getMembers()) {
+            OsmPrimitive targetMember = getMergeTarget(sourceMember.getMember());
+            if (targetMember == null)
+                throw new IllegalStateException(tr("Missing merge target of type {0} with id {1}", targetMember.getType(), targetMember.getUniqueId()));
+            if (! targetMember.isDeleted() && targetMember.isVisible()) {
+                RelationMember newMember = new RelationMember(sourceMember.getRole(), targetMember);
                 newMembers.add(newMember);
-            }
-        }
-
-        // check whether the list of relation members has changed
-        //
-        if (other.getMembersCount() != newMembers.size()) {
-            myRelation.setModified(true);
-        } else {
-            for (int i=0; i<other.getMembersCount();i++) {
-                RelationMember rm1 = other.getMember(i);
-                RelationMember rm2 = newMembers.get(i);
-                if (!rm1.getRole().equals(rm2.getRole())) {
-                    myRelation.setModified(true);
-                    break;
-                } else if (rm1.getMember().isNew() ^ rm2.getMember().isNew()) {
-                    myRelation.setModified(true);
-                    break;
-                } else if (rm1.getMember().isNew() && rm1.getMember() != rm2.getMember()) {
-                    myRelation.setModified(true);
-                    break;
-                } else if (! rm1.getMember().isNew() && rm1.getMember().getId() != rm2.getMember().getId()) {
-                    myRelation.setModified(true);
-                    break;
-                }
-            }
-        }
-        myRelation.setMembers(newMembers);
-    }
-
-    /**
-     * Tries to merge a primitive <code>other</code> into an existing primitive with the same id.
-     *
-     * @param other  the other primitive which is to be merged onto a primitive in my primitives
-     * @return true, if this method was able to merge <code>other</code> with an existing node; false, otherwise
-     */
-    private <P extends OsmPrimitive> boolean mergeById(P other) {
-        OsmPrimitive my = myDataSet.getPrimitiveById(other.getId(), other.getType());
+            } else {
+                target.setModified(true);
+            }
+        }
+        target.setMembers(newMembers);
+    }
+
+    /**
+     * Tries to merge a primitive <code>source</code> into an existing primitive with the same id.
+     *
+     * @param source  the other primitive which is to be merged onto a primitive in my primitives
+     * @return true, if this method was able to merge <code>source</code> into a target object; false, otherwise
+     */
+    private boolean mergeById(OsmPrimitive source) {
+        OsmPrimitive target = targetDataSet.getPrimitiveById(source.getId(), source.getType());
         // merge other into an existing primitive with the same id, if possible
         //
-        if (my == null)
+        if (target == null)
             return false;
-        mergedMap.put(other.getUniqueId(), my.getUniqueId());
-        if (my.getVersion() > other.getVersion())
-            // my.version > other.version => keep my version
+        // found a corresponding target, remember it
+        mergedMap.put(source.getUniqueId(), target.getUniqueId());
+
+        if (target.getVersion() > source.getVersion())
+            // target.version > source.version => keep target version
             return true;
-        if (! my.isVisible() && other.isVisible()) {
+        if (! target.isVisible() && source.isVisible()) {
             // should not happen
             //
-            logger.warning(tr("My primitive with id {0} and version {1} is visible although "
-                    + "their primitive with lower version {2} is not visible. "
-                    + "Can't deal with this inconsistency. Keeping my primitive. ",
-                    Long.toString(my.getId()),Long.toString(my.getVersion()), Long.toString(other.getVersion())
+            logger.warning(tr("Target object with id {0} and version {1} is visible although "
+                    + "source object with lower version {2} is not visible. "
+                    + "Can''t deal with this inconsistency. Keeping target object. ",
+                    Long.toString(target.getId()),Long.toString(target.getVersion()), Long.toString(source.getVersion())
             ));
-        } else if (my.isVisible() && ! other.isVisible()) {
+        } else if (target.isVisible() && ! source.isVisible()) {
             // this is always a conflict because the user has to decide whether
-            // he wants to create a clone of its local primitive or whether he
-            // wants to purge my from the local dataset. He can't keep it unchanged
+            // he wants to create a clone of its target primitive or whether he
+            // wants to purge the target from the local dataset. He can't keep it unchanged
             // because it was deleted on the server.
             //
-            conflicts.add(my,other);
-        } else if (my.incomplete && !other.incomplete) {
-            // my is incomplete, other completes it
-            // => merge other onto my
-            //
-            my.mergeFrom(other);
-            fixReferences.add(other.getUniqueId());
-        } else if (!my.incomplete && other.incomplete) {
-            // my is complete and the other is incomplete
-            // => keep mine, we have more information already
-            //
-        } else if (my.incomplete && other.incomplete) {
-            // my and other are incomplete. Doesn't matter which one to
-            // take. We take mine.
-            //
-        } else if (my.isDeleted() && ! other.isDeleted() && my.getVersion() == other.getVersion()) {
-            // same version, but my is deleted. Assume mine takes precedence
+            conflicts.add(target,source);
+        } else if (target.incomplete && !source.incomplete) {
+            // target is incomplete, source completes it
+            // => merge source into target
+            //
+            target.mergeFrom(source);
+            childrenToMerge.add(source.getUniqueId());
+        } else if (!target.incomplete && source.incomplete) {
+            // target is complete and source is incomplete
+            // => keep target, it has more information already
+            //
+        } else if (target.incomplete && source.incomplete) {
+            // target and source are incomplete. Doesn't matter which one to
+            // take. We take target.
+            //
+        } else if (target.isDeleted() && ! source.isDeleted() && target.getVersion() == source.getVersion()) {
+            // same version, but target is deleted. Assume target takes precedence
             // otherwise too many conflicts when refreshing from the server
-        } else if (my.isDeleted() != other.isDeleted()) {
+        } else if (target.isDeleted() != source.isDeleted()) {
             // differences in deleted state have to be resolved manually
             //
-            conflicts.add(my,other);
-        } else if (! my.isModified() && other.isModified()) {
-            // my not modified. We can assume that other is the most recent version.
-            // clone it onto my. But check first, whether other is deleted. if so,
-            // make sure that my is not references anymore in myDataSet.
-            //
-            if (other.isDeleted()) {
-                myDataSet.unlinkReferencesToPrimitive(my);
-            }
-            my.mergeFrom(other);
-            fixReferences.add(other.getUniqueId());
-        } else if (! my.isModified() && !other.isModified() && my.getVersion() == other.getVersion()) {
+            conflicts.add(target,source);
+        } else if (! target.isModified() && source.isModified()) {
+            // target not modified. We can assume that source is the most recent version.
+            // clone it into target. But check first, whether source is deleted. if so,
+            // make sure that target is not referenced anymore in myDataSet.
+            //
+            if (source.isDeleted()) {
+                deletedObjectsToUnlink.add(source);
+            }
+            target.mergeFrom(source);
+            childrenToMerge.add(source.getUniqueId());
+        } else if (! target.isModified() && !source.isModified() && target.getVersion() == source.getVersion()) {
             // both not modified. Keep mine
             //
-        } else if (! my.isModified() && !other.isModified() && my.getVersion() < other.getVersion()) {
+        } else if (! target.isModified() && !source.isModified() && target.getVersion() < source.getVersion()) {
             // my not modified but other is newer. clone other onto mine.
             //
-            my.mergeFrom(other);
-            fixReferences.add(other.getUniqueId());
-        } else if (my.isModified() && ! other.isModified() && my.getVersion() == other.getVersion()) {
+            target.mergeFrom(source);
+            childrenToMerge.add(source.getUniqueId());
+        } else if (target.isModified() && ! source.isModified() && target.getVersion() == source.getVersion()) {
             // my is same as other but mine is modified
             // => keep mine
-        } else if (! my.hasEqualSemanticAttributes(other)) {
+        } else if (! target.hasEqualSemanticAttributes(source)) {
             // my is modified and is not semantically equal with other. Can't automatically
             // resolve the differences
             // =>  create a conflict
-            conflicts.add(my,other);
+            conflicts.add(target,source);
         } else {
             // clone from other, but keep the modified flag. mergeFrom will mainly copy
@@ -332,7 +318,7 @@
             // attributes should already be equal if we get here.
             //
-            my.mergeFrom(other);
-            my.setModified(true);
-            fixReferences.add(other.getUniqueId());
+            target.mergeFrom(source);
+            target.setModified(true);
+            childrenToMerge.add(source.getUniqueId());
         }
         return true;
@@ -346,13 +332,13 @@
      */
     public void merge() {
-        if (theirDataSet == null)
+        if (sourceDataSet == null)
             return;
-        for (Node node: theirDataSet.getNodes()) {
+        for (Node node: sourceDataSet.getNodes()) {
             mergePrimitive(node);
         }
-        for (Way way: theirDataSet.getWays()) {
+        for (Way way: sourceDataSet.getWays()) {
             mergePrimitive(way);
         }
-        for (Relation relation: theirDataSet.getRelations()) {
+        for (Relation relation: sourceDataSet.getRelations()) {
             mergePrimitive(relation);
         }
@@ -365,6 +351,6 @@
      * @return
      */
-    public DataSet getMyDataSet() {
-        return myDataSet;
+    public DataSet getTargetDataSet() {
+        return targetDataSet;
     }
 
Index: /trunk/src/org/openstreetmap/josm/io/OsmServerBackreferenceReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OsmServerBackreferenceReader.java	(revision 2442)
+++ /trunk/src/org/openstreetmap/josm/io/OsmServerBackreferenceReader.java	(revision 2443)
@@ -269,10 +269,10 @@
                 DataSetMerger visitor = new DataSetMerger(ret,ds);
                 visitor.merge();
-                ret = visitor.getMyDataSet();
+                ret = visitor.getTargetDataSet();
             }
             DataSet ds = getReferringRelations(progressMonitor.createSubTaskMonitor(1, false));
             DataSetMerger visitor = new DataSetMerger(ret,ds);
             visitor.merge();
-            ret = visitor.getMyDataSet();
+            ret = visitor.getTargetDataSet();
             readIncompletePrimitives(ret, progressMonitor.createSubTaskMonitor(1, false));
             return ret;
Index: /trunk/test/unit/org/openstreetmap/josm/data/osm/DataSetMergerTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/osm/DataSetMergerTest.java	(revision 2443)
+++ /trunk/test/unit/org/openstreetmap/josm/data/osm/DataSetMergerTest.java	(revision 2443)
@@ -0,0 +1,1083 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.projection.Mercator;
+
+public class DataSetMergerTest {
+    private static Logger logger = Logger.getLogger(DataSetMergerTest.class.getName());
+
+    static Properties testProperties;
+
+    @BeforeClass
+    static public void init() {
+
+        if(System.getProperty("josm.home") == null){
+            testProperties = new Properties();
+
+            // load properties
+            //
+            try {
+                testProperties.load(DataSetMergerTest.class.getResourceAsStream("/test-unit-env.properties"));
+            } catch(Exception e){
+                logger.log(Level.SEVERE, MessageFormat.format("failed to load property file ''{0}''", "/test-unit-env.properties"));
+                fail(MessageFormat.format("failed to load property file ''{0}''", "/test-unit-env.properties"));
+            }
+
+            // check josm.home
+            //
+            String josmHome = testProperties.getProperty("josm.home");
+            if (josmHome == null) {
+                fail(MessageFormat.format("property ''{0}'' not set in test environment", "josm.home"));
+            } else {
+                File f = new File(josmHome);
+                if (! f.exists() || ! f.canRead()) {
+                    fail(MessageFormat.format("property ''{0}'' points to ''{1}'' which is either not existing or not readable", "josm.home", josmHome));
+                }
+            }
+            System.setProperty("josm.home", josmHome);
+        }
+        Main.pref.init(false);
+
+        // init projection
+        Main.proj = new Mercator();
+    }
+
+    /**
+     * two identical nodes, even in id and version. No confict expected.
+     *
+     * Can happen if data is loaded in two layers and then merged from one layer
+     * on the other.
+     */
+    @Test
+    public void nodeSimple_IdenticalNoConflict() {
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+        Node n = new Node(new LatLon(0,0));
+        n.setOsmId(1,1);
+        n.setModified(false);
+        n.put("key1", "value1");
+        my.addPrimitive(n);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+        Node n1 = new Node(new LatLon(0,0));
+        n1.setOsmId(1,1);
+        n1.setModified(false);
+        n1.put("key1", "value1");
+        their.addPrimitive(n1);
+
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        Node n2 = (Node)my.getPrimitiveById(1, OsmPrimitiveType.NODE);
+        assertTrue(visitor.getConflicts().isEmpty());
+        assertTrue(n1 != n2); // make sure we have a clone
+        assertEquals(1, n2.getId());
+        assertEquals(1, n2.getVersion());
+        assertEquals(false, n2.isModified());
+        assertEquals("value1", n2.get("key1"));
+
+        // merge target not modified after merging
+        assertTrue(!n2.isModified());
+    }
+
+    /**
+     * two  nodes, my is unmodified, their is updated and has a higher version
+     * => their version is going to be the merged version
+     *
+     */
+    @Test
+    public void nodeSimple_locallyUnmodifiedNoConflict() {
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+        Node n = new Node(new LatLon(0,0));
+        n.setOsmId(1,1);
+        n.setModified(false);
+        n.put("key1", "value1");
+        my.addPrimitive(n);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+        Node n1 = new Node(new LatLon(0,0));
+        n1.setOsmId(1,2);
+        n1.setModified(false);
+        n1.put("key1", "value1-new");
+        n1.put("key2", "value2");
+        their.addPrimitive(n1);
+
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        Node n2 = (Node)my.getPrimitiveById(1, OsmPrimitiveType.NODE);
+        assertTrue(visitor.getConflicts().isEmpty());
+        assertTrue(n == n2); // make sure the merged node is still the original node
+        assertTrue(n2.getDataSet() == my);
+        assertEquals(1, n2.getId());
+        assertEquals(2, n2.getVersion());
+        assertEquals(false, n2.isModified());
+        assertEquals("value1-new", n2.get("key1"));
+        assertEquals("value2", n2.get("key2"));
+
+        // the merge target should not be modified
+        assertTrue(!n2.isModified());
+    }
+
+    /**
+     * Node with same id, my is modified, their has a higher version
+     * => results in a conflict
+     *
+     * Use case: node which is modified locally and updated by another mapper on
+     * the server
+     */
+    @Test
+    public void nodeSimple_TagConflict() {
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+        Node n = new Node(new LatLon(0,0));
+        n.setOsmId(1,1);
+        n.setModified(true);
+        n.put("key1", "value1");
+        n.put("key2", "value2");
+        my.addPrimitive(n);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+        Node n1 = new Node(new LatLon(0,0));
+        n1.setOsmId(1,2);
+        n1.setModified(false);
+        n1.put("key1", "value1-new");
+
+        their.addPrimitive(n1);
+
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        Node n2 = (Node)my.getPrimitiveById(1, OsmPrimitiveType.NODE);
+        assertEquals(1,visitor.getConflicts().size());
+        assertTrue(n == n2);
+        assertTrue(n1 != n2);
+        assertTrue(n1.getDataSet() == their);
+    }
+
+    /**
+     * node with same id, my is deleted, their has a higher version
+     * => results in a conflict
+     *
+     * Use case: node which is deleted locally and updated by another mapper on
+     * the server
+     */
+    @Test
+    public void nodeSimple_DeleteConflict() {
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+        Node n = new Node(1);
+        n.setCoor(new LatLon(0,0));
+        n.incomplete = false;
+        n.setDeleted(true);
+        n.put("key1", "value1");
+        my.addPrimitive(n);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+        Node n1 = new Node(new LatLon(0,0));
+        n1.setOsmId(1,1);
+        n1.setModified(false);
+        n1.put("key1", "value1-new");
+        n1.put("key2", "value2");
+        their.addPrimitive(n1);
+
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        Node n2 = (Node)my.getPrimitiveById(1, OsmPrimitiveType.NODE);
+        assertEquals(1,visitor.getConflicts().size());
+        assertTrue(n == n2);
+        assertTrue(n1 != n2);
+        assertTrue(n1.getDataSet() == their);
+    }
+
+    /**
+     * My node is visible, their version has a higher version and is not visible
+     * => create a conflict
+     *
+     */
+    @Test
+    public void nodeSimple_VisibleConflict() {
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+        Node n = new Node(new LatLon(0,0));
+        n.setOsmId(1,1);
+        n.setModified(false);
+        n.setVisible(true);
+        my.addPrimitive(n);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+        Node n1 = new Node(new LatLon(0,0));
+        n1.setOsmId(1,2);
+
+        n1.setModified(false);
+        n1.setVisible(false);
+        their.addPrimitive(n1);
+
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        Node n2 = (Node)my.getPrimitiveById(1,OsmPrimitiveType.NODE);
+        assertEquals(1,visitor.getConflicts().size());
+        assertEquals(true, n2.isVisible());
+        assertTrue(n == n2);
+        assertTrue(n1 != n2);
+        assertTrue(n1.getDataSet() == their);
+    }
+
+    /**
+     * My node is deleted, their node has the same id and version and is not deleted.
+     * => mine has precedence
+     *
+     */
+    @Test
+    public void nodeSimple_DeleteConflict_2() {
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+        Node n = new Node(new LatLon(0,0));
+        n.setOsmId(1,1);
+        n.setDeleted(true);
+        my.addPrimitive(n);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+        Node n1 = new Node(new LatLon(0,0));
+        n1.setOsmId(1,1);
+        their.addPrimitive(n1);
+
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        Node n2 = (Node)my.getPrimitiveById(1, OsmPrimitiveType.NODE);
+        assertEquals(0,visitor.getConflicts().size());
+        assertEquals(true, n2.isVisible());
+        assertTrue(n == n2);
+        assertTrue(n.getDataSet() == my);
+        assertTrue(n1.getDataSet() == their);
+    }
+
+    /**
+     * My and their node are new but semantically equal. My node is deleted.
+     *
+     * => create a conflict
+     *
+     */
+    @Test
+    public void nodeSimple_DeleteConflict_3() {
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+        Node n = new Node(new LatLon(1,1));
+        n.setDeleted(true);
+        my.addPrimitive(n);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+        Node n1 = new Node(new LatLon(1,1));
+        their.addPrimitive(n1);
+
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        assertEquals(1,visitor.getConflicts().size());
+        assertTrue(n.getDataSet() == my);
+        assertTrue(n1.getDataSet() == their);
+    }
+
+    /**
+     * My and their node are new but semantically equal. Both are deleted.
+     *
+     * => take mine
+     *
+     */
+    @Test
+    public void nodeSimple_DeleteConflict_4() {
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+        Node n = new Node(new LatLon(1,1));
+        n.setDeleted(true);
+        my.addPrimitive(n);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+        Node n1 = new Node(new LatLon(1,1));
+        n1.setDeleted(true);
+        their.addPrimitive(n1);
+
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        assertEquals(0,visitor.getConflicts().size());
+        Node n2 = (Node)my.getNodes().toArray()[0];
+        assertTrue(n2 == n);
+        assertTrue(n2.isDeleted());
+    }
+
+    /**
+     * their node is not visible and doesn't exist in my data set
+     * => ignore their node
+     *
+     */
+    @Test
+    public void nodeSimple_InvisibleNodeInTheirDataset() {
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+        Node n = new Node(new LatLon(0,0));
+        n.setOsmId(1,1);
+        n.setDeleted(true);
+        my.addPrimitive(n);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+        Node n1 = new Node(new LatLon(0,0));
+        n1.setOsmId(2,1);
+        n1.setVisible(false);
+        their.addPrimitive(n1);
+
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        Node n2 = (Node)my.getPrimitiveById(1,OsmPrimitiveType.NODE);
+        assertEquals(0,visitor.getConflicts().size());
+        assertEquals(1, my.getNodes().size());
+        assertEquals(n,n2);
+    }
+
+    /**
+     * their node has no assigned id (id == 0) and is semantically equal to one of my
+     * nodes with id == 0
+     *
+     * => merge it onto my node.
+     *
+     */
+    @Test
+    public void nodeSimple_NoIdSemanticallyEqual() {
+
+        Calendar cal = GregorianCalendar.getInstance();
+        User myUser = User.createOsmUser(1111, "my");
+
+        User theirUser = User.createOsmUser(222, "their");
+
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+        Node n = new Node();
+        n.setCoor(new LatLon(0,0));
+        n.put("key1", "value1");
+        n.setUser(myUser);
+        n.setTimestamp(cal.getTime());
+
+        my.addPrimitive(n);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+        Node n1 = new Node();
+        n1.setCoor(new LatLon(0,0));
+        n1.put("key1", "value1");
+        cal.add(Calendar.HOUR, 1);
+        Date timestamp = cal.getTime();
+        n1.setTimestamp(timestamp);
+        n1.setUser(theirUser);
+        their.addPrimitive(n1);
+
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        Node n2 = my.getNodes().iterator().next();
+        assertEquals(0,visitor.getConflicts().size());
+        assertEquals("value1",n2.get("key1"));
+        assertEquals(true, n1.getTimestamp().equals(n2.getTimestamp()));
+        assertEquals(theirUser,n2.getUser());
+        assertTrue(n2 == n);
+        assertTrue(n2 != n1);
+        assertTrue(n2.getDataSet() == my);
+    }
+
+    /**
+     * my node is incomplete, their node is complete
+     *
+     * => merge it onto my node. My node becomes complete
+     *
+     */
+    @Test
+    public void nodeSimple_IncompleteNode() {
+
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+        Node n = new Node();
+        n.setCoor(new LatLon(0,0));
+        n.setOsmId(1,1);
+        n.incomplete = true;
+        my.addPrimitive(n);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+        Node n1 = new Node();
+        n1.setCoor(new LatLon(0,0));
+        n1.setOsmId(1,1);
+        n1.put("key1", "value1");
+        Date timestamp = new Date();
+        n1.setTimestamp(timestamp);
+        their.addPrimitive(n1);
+
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        Node n2 = my.getNodes().iterator().next();
+        assertEquals(0,visitor.getConflicts().size());
+        assertEquals("value1",n2.get("key1"));
+        assertEquals(true, n1.getTimestamp().equals(n2.getTimestamp()));
+        assertEquals(false, n2.incomplete);
+        assertTrue(n2 == n);
+    }
+
+    /**
+     * their way has a higher version and different tags. the nodes are the same. My
+     * way is not modified. Merge is possible. No conflict.
+     *
+     * => merge it onto my way.
+     *
+     */
+    @Test
+    public void waySimple_IdenicalNodesDifferentTags() {
+
+        // -- the target dataset
+        DataSet target = new DataSet();
+        target.setVersion("0.6");
+
+        Node n1 = new Node();
+        n1.setCoor(new LatLon(0,0));
+        n1.setOsmId(1,1);
+        target.addPrimitive(n1);
+
+        Node n2 = new Node();
+        n2.setCoor(new LatLon(0,0));
+        n2.setOsmId(2,1);
+
+        target.addPrimitive(n2);
+
+        Way myWay = new Way();
+        myWay.setOsmId(3,1);
+        myWay.put("key1", "value1");
+        myWay.addNode(n1);
+        myWay.addNode(n2);
+        target.addPrimitive(myWay);
+
+        // -- the source data set
+        DataSet source = new DataSet();
+        source.setVersion("0.6");
+
+        Node n3 = new Node(new LatLon(0,0));
+        n3.setOsmId(1,1);
+        source.addPrimitive(n3);
+
+        Node n4 = new Node(new LatLon(1,1));
+        n4.setOsmId(2,1);
+        source.addPrimitive(n4);
+
+        Way theirWay = new Way();
+        theirWay.setOsmId(3,2);
+        theirWay.put("key1", "value1");
+        theirWay.put("key2", "value2");
+        theirWay.addNode(n3);
+        theirWay.addNode(n4);
+        source.addPrimitive(theirWay);
+
+
+        DataSetMerger visitor = new DataSetMerger(target,source);
+        visitor.merge();
+
+        // -- tests
+        Way merged = (Way)target.getPrimitiveById(3, OsmPrimitiveType.WAY);
+        assertEquals(0,visitor.getConflicts().size());
+        assertEquals("value1",merged.get("key1"));
+        assertEquals("value2",merged.get("key2"));
+        assertEquals(3,merged.getId());
+        assertEquals(2,merged.getVersion());
+        assertEquals(2,merged.getNodesCount());
+        assertEquals(1,merged.getNode(0).getId());
+        assertEquals(2,merged.getNode(1).getId());
+        assertTrue(merged == myWay);
+        assertTrue(merged.getDataSet() == target);
+
+        Node mergedNode = (Node)target.getPrimitiveById(1, OsmPrimitiveType.NODE);
+        assertTrue(mergedNode == n1);
+        mergedNode = (Node)target.getPrimitiveById(2, OsmPrimitiveType.NODE);
+        assertTrue(mergedNode == n2);
+
+        assertTrue(!merged.isModified());
+    }
+
+    /**
+     * their way has a higher version and different tags. And it has more nodes. Two
+     * of the existing nodes are modified.
+     *
+     * => merge it onto my way, no conflict
+     *
+     */
+    @Test
+    public void waySimple_AdditionalNodesAndChangedNodes() {
+
+        // -- my data set
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+
+        Node n1 = new Node(new LatLon(0,0));
+        n1.setOsmId(1,1);
+        my.addPrimitive(n1);
+
+        Node n2 = new Node(new LatLon(1,1));
+        n2.setOsmId(2,1);
+        my.addPrimitive(n2);
+
+        Way myWay = new Way();
+        myWay.setOsmId(3,1);
+        myWay.addNode(n1);
+        myWay.addNode(n2);
+        my.addPrimitive(myWay);
+
+        // --- their data set
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+
+        Node n3 = new Node(new LatLon(0,0));
+        n3.setOsmId(1,1);
+        their.addPrimitive(n3);
+
+        Node n5 = new Node(new LatLon(1,1));
+        n5.setOsmId(4,1);
+
+        their.addPrimitive(n5);
+
+        Node n4 = new Node(new LatLon(2,2));
+        n4.setOsmId(2,2);
+        n4.put("key1", "value1");
+        their.addPrimitive(n4);
+
+
+        Way theirWay = new Way();
+        theirWay.setOsmId(3,2);
+        theirWay.addNode(n3);
+        theirWay.addNode(n5); // insert a node
+        theirWay.addNode(n4); // this one is updated
+        their.addPrimitive(theirWay);
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        // -- tests
+        Way merged = (Way)my.getPrimitiveById(3, OsmPrimitiveType.WAY);
+        assertEquals(0,visitor.getConflicts().size());
+        assertEquals(3,merged.getId());
+        assertEquals(2,merged.getVersion());
+        assertEquals(3,merged.getNodesCount());
+        assertEquals(1,merged.getNode(0).getId());
+        assertEquals(4,merged.getNode(1).getId());
+        assertEquals(2,merged.getNode(2).getId());
+        assertEquals("value1",merged.getNode(2).get("key1"));
+
+        assertTrue(merged.getNode(0) == n1);
+        assertTrue(merged.getNode(1) != n5); // must be clone of the original node in their
+        assertTrue(merged.getNode(2) == n2);
+
+        assertTrue(!merged.isModified());  // the target wasn't modified before merging, it
+        // mustn't be after merging
+    }
+
+    /**
+     * their way has a higher version and different nodes. My way is modified.
+     *
+     * => merge onto my way not possible, create a conflict
+     *
+     */
+    @Test
+    public void waySimple_DifferentNodesAndMyIsModified() {
+
+        // -- the target dataset
+        DataSet target = new DataSet();
+        target.setVersion("0.6");
+
+        Node n1 = new Node(new LatLon(0,0));
+        n1.setOsmId(1,1);
+        target.addPrimitive(n1);
+
+        Node n2 = new Node(new LatLon(1,1));
+        n2.setOsmId(2,1);
+        target.addPrimitive(n2);
+
+        Way myWay = new Way();
+        myWay.setOsmId(3,1);
+
+        myWay.addNode(n1);
+        myWay.addNode(n2);
+        myWay.setModified(true);
+        myWay.put("key1", "value1");
+        target.addPrimitive(myWay);
+
+        // -- the source dataset
+        DataSet source = new DataSet();
+        source.setVersion("0.6");
+
+        Node n3 = new Node(new LatLon(0,0));
+        n3.setOsmId(1,1);
+        source.addPrimitive(n3);
+
+        Node n5 = new Node(new LatLon(1,1));
+        n5.setOsmId(4,1);
+        source.addPrimitive(n5);
+
+        Node n4 = new Node(new LatLon(2,2));
+        n4.setOsmId(2,1);
+        n4.put("key1", "value1");
+        source.addPrimitive(n4);
+
+
+        Way theirWay = new Way();
+        theirWay.setOsmId(3,2);
+
+        theirWay.addNode(n3);
+        theirWay.addNode(n5); // insert a node
+        theirWay.addNode(n4); // this one is updated
+        source.addPrimitive(theirWay);
+
+
+        DataSetMerger visitor = new DataSetMerger(target,source);
+        visitor.merge();
+
+        Way merged = (Way)target.getPrimitiveById(3, OsmPrimitiveType.WAY);
+        assertEquals(1,visitor.getConflicts().size());
+        assertEquals(3,merged.getId());
+        assertEquals(1,merged.getVersion());
+        assertEquals(2,merged.getNodesCount());
+        assertEquals(1,merged.getNode(0).getId());
+        assertEquals(2,merged.getNode(1).getId());
+        assertEquals("value1",merged.get("key1"));
+    }
+
+
+    /**
+     * their way is not visible anymore.
+     *
+     * => conflict
+     *
+     */
+    @Test
+    public void waySimple_TheirVersionNotVisible() {
+
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+
+        Node n1 = new Node(new LatLon(0,0));
+        n1.setOsmId(1,1);
+        my.addPrimitive(n1);
+
+        Node n2 = new Node(new LatLon(1,1));
+        n2.setOsmId(2,1);
+        my.addPrimitive(n2);
+
+        Way myWay = new Way();
+        myWay.setOsmId(3,1);
+        myWay.addNode(n1);
+        myWay.addNode(n2);
+        my.addPrimitive(myWay);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+
+        Way theirWay = new Way();
+        theirWay.setOsmId(3,2);
+        theirWay.setVisible(false);
+        their.addPrimitive(theirWay);
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        Way merged = (Way)my.getPrimitiveById(3, OsmPrimitiveType.WAY);
+        assertEquals(1,visitor.getConflicts().size());
+        assertEquals(true, visitor.getConflicts().hasConflictForMy(myWay));
+        assertEquals(true, visitor.getConflicts().hasConflictForTheir(theirWay));
+        assertEquals(myWay,merged);
+    }
+
+    /**
+     * my and their way have no ids,  nodes they refer to have an id. but
+     * my and  their way are semantically equal. so technical attributes of
+     * their way can be merged on my way. No conflict.
+     *
+     */
+    @Test
+    public void waySimple_twoWaysWithNoId_NodesWithId() {
+
+        // -- my data set
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+
+        Node n1 = new Node(new LatLon(0,0));
+        n1.setOsmId(1,1);
+        my.addPrimitive(n1);
+
+        Node n2 = new Node(new LatLon(1,1));
+        n2.setOsmId(2,1);
+        my.addPrimitive(n2);
+
+        Way myWay = new Way();
+        myWay.addNode(n1);
+        myWay.addNode(n2);
+        my.addPrimitive(myWay);
+
+        // -- their data set
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+
+        Node n3 = new Node(new LatLon(0,0));
+        n3.setOsmId(1,1);
+        their.addPrimitive(n3);
+
+        Node n4 = new Node(new LatLon(1,1));
+        n4.setOsmId(2,1);
+        their.addPrimitive(n4);
+
+        Way theirWay = new Way();
+        theirWay.addNode(n3);
+        theirWay.addNode(n4);
+        theirWay.setUser(User.createOsmUser(1111, "their"));
+        theirWay.setTimestamp(new Date());
+        their.addPrimitive(theirWay);
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        // -- tests
+        Way merged = (Way)my.getWays().toArray()[0];
+        assertEquals(0,visitor.getConflicts().size());
+        assertEquals("their", merged.getUser().getName());
+        assertEquals(1111, merged.getUser().getId());
+        assertEquals(theirWay.getTimestamp(), merged.getTimestamp());
+        assertTrue(merged == myWay);
+        assertTrue(merged.getNode(0) == n1);
+        assertTrue(merged.getNode(1) == n2);
+
+        assertTrue(!merged.isModified());
+    }
+
+    /**
+     * my and their way have no ids, neither do the nodes they refer to. but
+     * my and  their way are semantically equal. so technical attributes of
+     * their way can be merged on my way. No conflict.
+     *
+     */
+    @Test
+    public void waySimple_twoWaysWithNoId_NodesWithoutId() {
+
+        // -- my data set
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+
+        Node n1 = new Node(new LatLon(0,0));
+        my.addPrimitive(n1);
+
+        Node n2 = new Node(new LatLon(1,1));
+        my.addPrimitive(n2);
+
+        Way myWay = new Way();
+        myWay.addNode(n1);
+        myWay.addNode(n2);
+        my.addPrimitive(myWay);
+
+        // -- their data set
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+
+        Node n3 = new Node(new LatLon(0,0));
+        their.addPrimitive(n3);
+
+        Node n4 = new Node(new LatLon(1,1));
+        their.addPrimitive(n4);
+
+        Way theirWay = new Way();
+        theirWay.addNode(n3);
+        theirWay.addNode(n4);
+        User user = User.createOsmUser(1111, "their");
+        theirWay.setUser(user);
+        theirWay.setTimestamp(new Date());
+        their.addPrimitive(theirWay);
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        // -- tests
+        Way merged = (Way)my.getWays().toArray()[0];
+        assertEquals(0,visitor.getConflicts().size());
+        assertEquals("their", merged.getUser().getName());
+        assertEquals(1111, merged.getUser().getId());
+        assertEquals(theirWay.getTimestamp(), merged.getTimestamp());
+        assertTrue(merged == myWay);
+        assertTrue(merged.getNode(0) == n1);
+        assertTrue(merged.getNode(1) == n2);
+
+        assertTrue(!merged.isModified());
+    }
+
+
+    /**
+     * My dataset includes a deleted node.
+     * Their dataset includes a way with three nodes, the first one being my node.
+     *
+     * => the merged way should include two nodes only. the deleted node should still be
+     * in the data set
+     *
+     */
+    @Test
+    public void wayComplex_mergingADeletedNode() {
+
+        // -- my dataset
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+
+        Node n1 = new Node(new LatLon(0,0));
+        n1.setOsmId(1,1);
+        n1.setDeleted(true);
+        my.addPrimitive(n1);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+
+        Node n3 = new Node(new LatLon(0,0));
+        n3.setOsmId(1,1);
+        their.addPrimitive(n3);
+
+        Node n4 = new Node(new LatLon(1,1));
+        n4.setOsmId(2,1);
+        their.addPrimitive(n4);
+
+        Node n5 = new Node(new LatLon(2,2));
+        n5.setOsmId(3,1);
+        their.addPrimitive(n5);
+
+        // -- their data set
+        Way theirWay = new Way();
+        theirWay.setOsmId(4,1);
+        theirWay.addNode(n3);
+        theirWay.addNode(n4);
+        theirWay.addNode(n5);
+        theirWay.setUser(User.createOsmUser(1111, "their"));
+        theirWay.setTimestamp(new Date());
+        their.addPrimitive(theirWay);
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        assertEquals(0,visitor.getConflicts().size());
+
+        Way myWay = (Way)my.getPrimitiveById(4,OsmPrimitiveType.WAY);
+        assertEquals(2, myWay.getNodesCount());
+
+        Node n = (Node)my.getPrimitiveById(1,OsmPrimitiveType.NODE);
+        assertTrue(!myWay.getNodes().contains(n));
+        assertTrue(n != null);
+
+        //a node was removed from the way,it should thus be modified
+        assertTrue(myWay.isModified());
+    }
+
+    /**
+     * My dataset includes a deleted node.
+     * Their dataset includes a relation with three nodes, the first one being my node.
+     *
+     * => the merged relation should include two nodes only. the deleted node should still be
+     * in the data set
+     *
+     */
+    @Test
+    public void relationComplex_mergingADeletedNode() {
+
+        DataSet my = new DataSet();
+        my.setVersion("0.6");
+
+        Node n1 = new Node(new LatLon(0,0));
+        n1.setOsmId(1,1);
+        n1.setDeleted(true);
+        my.addPrimitive(n1);
+
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+
+        Node n3 = new Node(new LatLon(0,0));
+        n3.setOsmId(1,1);
+        their.addPrimitive(n3);
+
+        Node n4 = new Node(new LatLon(1,1));
+        n4.setOsmId(2,1);
+        their.addPrimitive(n4);
+
+        Node n5 = new Node(new LatLon(2,2));
+        n5.setOsmId(3,1);
+        their.addPrimitive(n5);
+
+
+        Relation theirRelation = new Relation();
+        theirRelation.setOsmId(4,1);
+
+        theirRelation.addMember(new RelationMember("", n3));
+        theirRelation.addMember(new RelationMember("", n4));
+        theirRelation.addMember(new RelationMember("", n5));
+        their.addPrimitive(theirRelation);
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        assertEquals(0,visitor.getConflicts().size());
+
+        Relation r = (Relation)my.getPrimitiveById(4,OsmPrimitiveType.RELATION);
+        assertEquals(2, r.getMembersCount());
+
+        Node n = (Node)my.getPrimitiveById(1,OsmPrimitiveType.NODE);
+        assertTrue(n != null);
+
+        assertTrue(r.isModified());
+    }
+
+    /**
+     * Merge an incomplete way with two incomplete nodes into an empty dataset.
+     * 
+     * Use case: a way loaded with a multiget, i.e. GET /api/0.6/ways?ids=123456
+     */
+    @Test
+    public void newIncompleteWay() {
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+
+        Node n1 = new Node(1);
+        their.addPrimitive(n1);
+
+        Node n2 = new Node(2);
+        their.addPrimitive(n2);
+
+        Way w3 = new Way(3);
+        w3.incomplete = true;
+        w3.setNodes(Arrays.asList(n1,n2));
+        their.addPrimitive(w3);
+
+        DataSet my = new DataSet();
+        their.setVersion("0.6");
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        assertEquals(0,visitor.getConflicts().size());
+
+        OsmPrimitive p= my.getPrimitiveById(1, OsmPrimitiveType.NODE);
+        assertNotNull(p);
+        assertTrue(p.incomplete);
+        p= my.getPrimitiveById(2, OsmPrimitiveType.NODE);
+        assertNotNull(p);
+        assertTrue(p.incomplete);
+        p= my.getPrimitiveById(3, OsmPrimitiveType.WAY);
+        assertNotNull(p);
+        assertTrue(p.incomplete);
+
+        Way w = (Way)my.getPrimitiveById(3, OsmPrimitiveType.WAY);
+        assertNotNull(w);
+        assertTrue(p.incomplete);
+        assertEquals(2, w.getNodesCount());
+        assertTrue(w.getNode(0).incomplete);
+        assertTrue(w.getNode(1).incomplete);
+    }
+
+    /**
+     * Merge an incomplete way with two incomplete nodes into a dataset where the way already exists as complete way.
+     * 
+     * Use case: a way loaded with a multiget, i.e. GET /api/0.6/ways?ids=123456 after a "Update selection " of this way
+     */
+    @Test
+    public void incompleteWayOntoCompleteWay() {
+        DataSet their = new DataSet();
+        their.setVersion("0.6");
+
+        // an incomplete node
+        Node n1 = new Node(1);
+        their.addPrimitive(n1);
+
+        // another incomplete node
+        Node n2 = new Node(2);
+        their.addPrimitive(n2);
+
+        // an incomplete way with two incomplete nodes
+        Way w3 = new Way(3);
+        w3.incomplete = true;
+        w3.setNodes(Arrays.asList(n1,n2));
+        their.addPrimitive(w3);
+
+        DataSet my = new DataSet();
+        their.setVersion("0.6");
+
+        Node n4 = new Node(new LatLon(0,0));
+        n4.setOsmId(1,1);
+        my.addPrimitive(n4);
+
+        Node n5 = new Node(new LatLon(1,1));
+        n5.setOsmId(2,1);
+        my.addPrimitive(n5);
+
+        Way w6 = new Way(3);
+        w6.incomplete = false;
+        w6.setNodes(Arrays.asList(n4,n5));
+        my.addPrimitive(w6);
+
+        DataSetMerger visitor = new DataSetMerger(my,their);
+        visitor.merge();
+
+        assertEquals(0,visitor.getConflicts().size());
+
+        OsmPrimitive p= my.getPrimitiveById(1, OsmPrimitiveType.NODE);
+        assertNotNull(p);
+        assertTrue(!p.incomplete);
+        p= my.getPrimitiveById(2, OsmPrimitiveType.NODE);
+        assertNotNull(p);
+        assertTrue(!p.incomplete);
+        p= my.getPrimitiveById(3,OsmPrimitiveType.WAY);
+        assertNotNull(p);
+        assertTrue(!p.incomplete);
+
+        Way w = (Way)my.getPrimitiveById(3,OsmPrimitiveType.WAY);
+        assertNotNull(w);
+        assertTrue(!p.incomplete);
+        assertEquals(2, w.getNodesCount());
+        assertTrue(!w.getNode(0).incomplete);
+        assertTrue(!w.getNode(1).incomplete);
+    }
+}
Index: unk/test/unit/org/openstreetmap/josm/data/osm/visitor/MergeVisitorTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/osm/visitor/MergeVisitorTest.java	(revision 2442)
+++ 	(revision )
@@ -1,1069 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.data.osm.visitor;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.File;
-import java.text.MessageFormat;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.Properties;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.data.osm.DataSetMerger;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
-import org.openstreetmap.josm.data.osm.Relation;
-import org.openstreetmap.josm.data.osm.RelationMember;
-import org.openstreetmap.josm.data.osm.User;
-import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.projection.Mercator;
-
-public class MergeVisitorTest {
-    private static Logger logger = Logger.getLogger(MergeVisitorTest.class.getName());
-
-    static Properties testProperties;
-
-    @BeforeClass
-    static public void init() {
-
-        if(System.getProperty("josm.home") == null){
-            testProperties = new Properties();
-
-            // load properties
-            //
-            try {
-                testProperties.load(MergeVisitorTest.class.getResourceAsStream("/test-unit-env.properties"));
-            } catch(Exception e){
-                logger.log(Level.SEVERE, MessageFormat.format("failed to load property file ''{0}''", "/test-unit-env.properties"));
-                fail(MessageFormat.format("failed to load property file ''{0}''", "/test-unit-env.properties"));
-            }
-
-            // check josm.home
-            //
-            String josmHome = testProperties.getProperty("josm.home");
-            if (josmHome == null) {
-                fail(MessageFormat.format("property ''{0}'' not set in test environment", "josm.home"));
-            } else {
-                File f = new File(josmHome);
-                if (! f.exists() || ! f.canRead()) {
-                    fail(MessageFormat.format("property ''{0}'' points to ''{1}'' which is either not existing or not readable", "josm.home", josmHome));
-                }
-            }
-            System.setProperty("josm.home", josmHome);
-        }
-        Main.pref.init(false);
-
-        // init projection
-        Main.proj = new Mercator();
-    }
-
-    /**
-     * two identical nodes, even in id and version. No confict expected.
-     *
-     * Can happen if data is loaded in two layers and then merged from one layer
-     * on the other.
-     */
-    @Test
-    public void nodeSimple_IdenticalNoConflict() {
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-        Node n = new Node(new LatLon(0,0));
-        n.setOsmId(1,1);
-        n.setModified(false);
-        n.put("key1", "value1");
-        my.addPrimitive(n);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-        Node n1 = new Node(new LatLon(0,0));
-        n1.setOsmId(1,1);
-        n1.setModified(false);
-        n1.put("key1", "value1");
-        their.addPrimitive(n1);
-
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        Node n2 = (Node)my.getPrimitiveById(1, OsmPrimitiveType.NODE);
-        assertTrue(visitor.getConflicts().isEmpty());
-        assertTrue(n1 != n2); // make sure we have a clone
-        assertEquals(1, n2.getId());
-        assertEquals(1, n2.getVersion());
-        assertEquals(false, n2.isModified());
-        assertEquals("value1", n2.get("key1"));
-    }
-
-    /**
-     * two  nodes, my is unmodified, their is updated and has a higher version
-     * => their version is going to be the merged version
-     *
-     */
-    @Test
-    public void nodeSimple_locallyUnmodifiedNoConflict() {
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-        Node n = new Node(new LatLon(0,0));
-        n.setOsmId(1,1);
-        n.setModified(false);
-        n.put("key1", "value1");
-        my.addPrimitive(n);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-        Node n1 = new Node(new LatLon(0,0));
-        n1.setOsmId(1,2);
-        n1.setModified(false);
-        n1.put("key1", "value1-new");
-        n1.put("key2", "value2");
-        their.addPrimitive(n1);
-
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        Node n2 = (Node)my.getPrimitiveById(1, OsmPrimitiveType.NODE);
-        assertTrue(visitor.getConflicts().isEmpty());
-        assertTrue(n == n2); // make sure the merged node is still the original node
-        assertTrue(n2.getDataSet() == my);
-        assertEquals(1, n2.getId());
-        assertEquals(2, n2.getVersion());
-        assertEquals(false, n2.isModified());
-        assertEquals("value1-new", n2.get("key1"));
-        assertEquals("value2", n2.get("key2"));
-    }
-
-    /**
-     * Node with same id, my is modified, their has a higher version
-     * => results in a conflict
-     *
-     * Use case: node which is modified locally and updated by another mapper on
-     * the server
-     */
-    @Test
-    public void nodeSimple_TagConflict() {
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-        Node n = new Node(new LatLon(0,0));
-        n.setOsmId(1,1);
-        n.setModified(true);
-        n.put("key1", "value1");
-        n.put("key2", "value2");
-        my.addPrimitive(n);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-        Node n1 = new Node(new LatLon(0,0));
-        n1.setOsmId(1,2);
-        n1.setModified(false);
-        n1.put("key1", "value1-new");
-
-        their.addPrimitive(n1);
-
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        Node n2 = (Node)my.getPrimitiveById(1, OsmPrimitiveType.NODE);
-        assertEquals(1,visitor.getConflicts().size());
-        assertTrue(n == n2);
-        assertTrue(n1 != n2);
-        assertTrue(n1.getDataSet() == their);
-    }
-
-    /**
-     * node with same id, my is deleted, their has a higher version
-     * => results in a conflict
-     *
-     * Use case: node which is deleted locally and updated by another mapper on
-     * the server
-     */
-    @Test
-    public void nodeSimple_DeleteConflict() {
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-        Node n = new Node(1);
-        n.setCoor(new LatLon(0,0));
-        n.incomplete = false;
-        n.setDeleted(true);
-        n.put("key1", "value1");
-        my.addPrimitive(n);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-        Node n1 = new Node(new LatLon(0,0));
-        n1.setOsmId(1,1);
-        n1.setModified(false);
-        n1.put("key1", "value1-new");
-        n1.put("key2", "value2");
-        their.addPrimitive(n1);
-
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        Node n2 = (Node)my.getPrimitiveById(1, OsmPrimitiveType.NODE);
-        assertEquals(1,visitor.getConflicts().size());
-        assertTrue(n == n2);
-        assertTrue(n1 != n2);
-        assertTrue(n1.getDataSet() == their);
-    }
-
-    /**
-     * My node is visible, their version has a higher version and is not visible
-     * => create a conflict
-     *
-     */
-    @Test
-    public void nodeSimple_VisibleConflict() {
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-        Node n = new Node(new LatLon(0,0));
-        n.setOsmId(1,1);
-        n.setModified(false);
-        n.setVisible(true);
-        my.addPrimitive(n);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-        Node n1 = new Node(new LatLon(0,0));
-        n1.setOsmId(1,2);
-
-        n1.setModified(false);
-        n1.setVisible(false);
-        their.addPrimitive(n1);
-
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        Node n2 = (Node)my.getPrimitiveById(1,OsmPrimitiveType.NODE);
-        assertEquals(1,visitor.getConflicts().size());
-        assertEquals(true, n2.isVisible());
-        assertTrue(n == n2);
-        assertTrue(n1 != n2);
-        assertTrue(n1.getDataSet() == their);
-    }
-
-    /**
-     * My node is deleted, their node has the same id and version and is not deleted.
-     * => mine has precedence
-     *
-     */
-    @Test
-    public void nodeSimple_DeleteConflict_2() {
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-        Node n = new Node(new LatLon(0,0));
-        n.setOsmId(1,1);
-        n.setDeleted(true);
-        my.addPrimitive(n);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-        Node n1 = new Node(new LatLon(0,0));
-        n1.setOsmId(1,1);
-        their.addPrimitive(n1);
-
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        Node n2 = (Node)my.getPrimitiveById(1, OsmPrimitiveType.NODE);
-        assertEquals(0,visitor.getConflicts().size());
-        assertEquals(true, n2.isVisible());
-        assertTrue(n == n2);
-        assertTrue(n.getDataSet() == my);
-        assertTrue(n1.getDataSet() == their);
-    }
-
-    /**
-     * My and their node are new but semantically equal. My node is deleted.
-     *
-     * => create a conflict
-     *
-     */
-    @Test
-    public void nodeSimple_DeleteConflict_3() {
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-        Node n = new Node(new LatLon(1,1));
-        n.setDeleted(true);
-        my.addPrimitive(n);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-        Node n1 = new Node(new LatLon(1,1));
-        their.addPrimitive(n1);
-
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        assertEquals(1,visitor.getConflicts().size());
-        assertTrue(n.getDataSet() == my);
-        assertTrue(n1.getDataSet() == their);
-    }
-
-    /**
-     * My and their node are new but semantically equal. Both are deleted.
-     *
-     * => take mine
-     *
-     */
-    @Test
-    public void nodeSimple_DeleteConflict_4() {
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-        Node n = new Node(new LatLon(1,1));
-        n.setDeleted(true);
-        my.addPrimitive(n);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-        Node n1 = new Node(new LatLon(1,1));
-        n1.setDeleted(true);
-        their.addPrimitive(n1);
-
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        assertEquals(0,visitor.getConflicts().size());
-        Node n2 = (Node)my.getNodes().toArray()[0];
-        assertTrue(n2 == n);
-        assertTrue(n2.isDeleted());
-    }
-
-    /**
-     * their node is not visible and doesn't exist in my data set
-     * => ignore their node
-     *
-     */
-    @Test
-    public void nodeSimple_InvisibleNodeInTheirDataset() {
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-        Node n = new Node(new LatLon(0,0));
-        n.setOsmId(1,1);
-        n.setDeleted(true);
-        my.addPrimitive(n);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-        Node n1 = new Node(new LatLon(0,0));
-        n1.setOsmId(2,1);
-        n1.setVisible(false);
-        their.addPrimitive(n1);
-
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        Node n2 = (Node)my.getPrimitiveById(1,OsmPrimitiveType.NODE);
-        assertEquals(0,visitor.getConflicts().size());
-        assertEquals(1, my.getNodes().size());
-        assertEquals(n,n2);
-    }
-
-    /**
-     * their node has no assigned id (id == 0) and is semantically equal to one of my
-     * nodes with id == 0
-     *
-     * => merge it onto my node.
-     *
-     */
-    @Test
-    public void nodeSimple_NoIdSemanticallyEqual() {
-
-        Calendar cal = GregorianCalendar.getInstance();
-        User myUser = User.createOsmUser(1111, "my");
-
-        User theirUser = User.createOsmUser(222, "their");
-
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-        Node n = new Node();
-        n.setCoor(new LatLon(0,0));
-        n.put("key1", "value1");
-        n.setUser(myUser);
-        n.setTimestamp(cal.getTime());
-
-        my.addPrimitive(n);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-        Node n1 = new Node();
-        n1.setCoor(new LatLon(0,0));
-        n1.put("key1", "value1");
-        cal.add(Calendar.HOUR, 1);
-        Date timestamp = cal.getTime();
-        n1.setTimestamp(timestamp);
-        n1.setUser(theirUser);
-        their.addPrimitive(n1);
-
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        Node n2 = my.getNodes().iterator().next();
-        assertEquals(0,visitor.getConflicts().size());
-        assertEquals("value1",n2.get("key1"));
-        assertEquals(true, n1.getTimestamp().equals(n2.getTimestamp()));
-        assertEquals(theirUser,n2.getUser());
-        assertTrue(n2 == n);
-        assertTrue(n2 != n1);
-        assertTrue(n2.getDataSet() == my);
-    }
-
-    /**
-     * my node is incomplete, their node is complete
-     *
-     * => merge it onto my node. My node becomes complete
-     *
-     */
-    @Test
-    public void nodeSimple_IncompleteNode() {
-
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-        Node n = new Node();
-        n.setCoor(new LatLon(0,0));
-        n.setOsmId(1,1);
-        n.incomplete = true;
-        my.addPrimitive(n);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-        Node n1 = new Node();
-        n1.setCoor(new LatLon(0,0));
-        n1.setOsmId(1,1);
-        n1.put("key1", "value1");
-        Date timestamp = new Date();
-        n1.setTimestamp(timestamp);
-        their.addPrimitive(n1);
-
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        Node n2 = my.getNodes().iterator().next();
-        assertEquals(0,visitor.getConflicts().size());
-        assertEquals("value1",n2.get("key1"));
-        assertEquals(true, n1.getTimestamp().equals(n2.getTimestamp()));
-        assertEquals(false, n2.incomplete);
-        assertTrue(n2 == n);
-    }
-
-    /**
-     * their way has a higher version and different tags. the nodes are the same. My
-     * way is not modified. Merge is possible. No conflict.
-     *
-     * => merge it onto my way.
-     *
-     */
-    @Test
-    public void waySimple_IdenicalNodesDifferentTags() {
-
-
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-
-        Node n1 = new Node();
-        n1.setCoor(new LatLon(0,0));
-        n1.setOsmId(1,1);
-        my.addPrimitive(n1);
-
-
-        Node n2 = new Node();
-        n2.setCoor(new LatLon(0,0));
-        n2.setOsmId(2,1);
-
-        my.addPrimitive(n2);
-
-        Way myWay = new Way();
-        myWay.setOsmId(3,1);
-        myWay.put("key1", "value1");
-        myWay.addNode(n1);
-        myWay.addNode(n2);
-        my.addPrimitive(myWay);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-
-        Node n3 = new Node(new LatLon(0,0));
-        n3.setOsmId(1,1);
-        their.addPrimitive(n3);
-
-        Node n4 = new Node(new LatLon(1,1));
-        n4.setOsmId(2,1);
-        their.addPrimitive(n4);
-
-        Way theirWay = new Way();
-        theirWay.setOsmId(3,2);
-        theirWay.put("key1", "value1");
-        theirWay.put("key2", "value2");
-        theirWay.addNode(n3);
-        theirWay.addNode(n4);
-        their.addPrimitive(theirWay);
-
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        Way merged = (Way)my.getPrimitiveById(3, OsmPrimitiveType.WAY);
-        assertEquals(0,visitor.getConflicts().size());
-        assertEquals("value1",merged.get("key1"));
-        assertEquals("value2",merged.get("key2"));
-        assertEquals(3,merged.getId());
-        assertEquals(2,merged.getVersion());
-        assertEquals(2,merged.getNodesCount());
-        assertEquals(1,merged.getNode(0).getId());
-        assertEquals(2,merged.getNode(1).getId());
-        assertTrue(merged == myWay);
-        assertTrue(merged.getDataSet() == my);
-
-        Node mergedNode = (Node)my.getPrimitiveById(1, OsmPrimitiveType.NODE);
-        assertTrue(mergedNode == n1);
-        mergedNode = (Node)my.getPrimitiveById(2, OsmPrimitiveType.NODE);
-        assertTrue(mergedNode == n2);
-    }
-
-    /**
-     * their way has a higher version and different tags. And it has more nodes. Two
-     * of the existing nodes are modified.
-     *
-     * => merge it onto my way, no conflict
-     *
-     */
-    @Test
-    public void waySimple_AdditionalNodesAndChangedNodes() {
-
-        // -- my data set
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-
-        Node n1 = new Node(new LatLon(0,0));
-        n1.setOsmId(1,1);
-        my.addPrimitive(n1);
-
-        Node n2 = new Node(new LatLon(1,1));
-        n2.setOsmId(2,1);
-        my.addPrimitive(n2);
-
-        Way myWay = new Way();
-        myWay.setOsmId(3,1);
-        myWay.addNode(n1);
-        myWay.addNode(n2);
-        my.addPrimitive(myWay);
-
-        // --- their data set
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-
-        Node n3 = new Node(new LatLon(0,0));
-        n3.setOsmId(1,1);
-        their.addPrimitive(n3);
-
-        Node n5 = new Node(new LatLon(1,1));
-        n5.setOsmId(4,1);
-
-        their.addPrimitive(n5);
-
-        Node n4 = new Node(new LatLon(2,2));
-        n4.setOsmId(2,2);
-        n4.put("key1", "value1");
-        their.addPrimitive(n4);
-
-
-        Way theirWay = new Way();
-        theirWay.setOsmId(3,2);
-        theirWay.addNode(n3);
-        theirWay.addNode(n5); // insert a node
-        theirWay.addNode(n4); // this one is updated
-        their.addPrimitive(theirWay);
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        Way merged = (Way)my.getPrimitiveById(3, OsmPrimitiveType.WAY);
-        assertEquals(0,visitor.getConflicts().size());
-        assertEquals(3,merged.getId());
-        assertEquals(2,merged.getVersion());
-        assertEquals(3,merged.getNodesCount());
-        assertEquals(1,merged.getNode(0).getId());
-        assertEquals(4,merged.getNode(1).getId());
-        assertEquals(2,merged.getNode(2).getId());
-        assertEquals("value1",merged.getNode(2).get("key1"));
-
-        assertTrue(merged.getNode(0) == n1);
-        assertTrue(merged.getNode(1) != n5); // must be clone of the original node in their
-        assertTrue(merged.getNode(2) == n2);
-    }
-
-    /**
-     * their way has a higher version and different nodes. My way is modified.
-     *
-     * => merge onto my way not possible, create a conflict
-     *
-     */
-    @Test
-    public void waySimple_DifferentNodesAndMyIsModified() {
-
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-
-        Node n1 = new Node(new LatLon(0,0));
-        n1.setOsmId(1,1);
-        my.addPrimitive(n1);
-
-        Node n2 = new Node(new LatLon(1,1));
-        n2.setOsmId(2,1);
-        my.addPrimitive(n2);
-
-        Way myWay = new Way();
-        myWay.setOsmId(3,1);
-
-        myWay.addNode(n1);
-        myWay.addNode(n2);
-        myWay.setModified(true);
-        myWay.put("key1", "value1");
-        my.addPrimitive(myWay);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-
-        Node n3 = new Node(new LatLon(0,0));
-        n3.setOsmId(1,1);
-        their.addPrimitive(n3);
-
-        Node n5 = new Node(new LatLon(1,1));
-        n5.setOsmId(4,1);
-        their.addPrimitive(n5);
-
-        Node n4 = new Node(new LatLon(2,2));
-        n4.setOsmId(2,1);
-        n4.put("key1", "value1");
-        their.addPrimitive(n4);
-
-
-        Way theirWay = new Way();
-        theirWay.setOsmId(3,2);
-
-        theirWay.addNode(n3);
-        theirWay.addNode(n5); // insert a node
-        theirWay.addNode(n4); // this one is updated
-        their.addPrimitive(theirWay);
-
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        Way merged = (Way)my.getPrimitiveById(3, OsmPrimitiveType.WAY);
-        assertEquals(1,visitor.getConflicts().size());
-        assertEquals(3,merged.getId());
-        assertEquals(1,merged.getVersion());
-        assertEquals(2,merged.getNodesCount());
-        assertEquals(1,merged.getNode(0).getId());
-        assertEquals(2,merged.getNode(1).getId());
-        assertEquals("value1",merged.get("key1"));
-    }
-
-
-    /**
-     * their way is not visible anymore.
-     *
-     * => conflict
-     *
-     */
-    @Test
-    public void waySimple_TheirVersionNotVisible() {
-
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-
-        Node n1 = new Node(new LatLon(0,0));
-        n1.setOsmId(1,1);
-        my.addPrimitive(n1);
-
-        Node n2 = new Node(new LatLon(1,1));
-        n2.setOsmId(2,1);
-        my.addPrimitive(n2);
-
-        Way myWay = new Way();
-        myWay.setOsmId(3,1);
-        myWay.addNode(n1);
-        myWay.addNode(n2);
-        my.addPrimitive(myWay);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-
-        Way theirWay = new Way();
-        theirWay.setOsmId(3,2);
-        theirWay.setVisible(false);
-        their.addPrimitive(theirWay);
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        Way merged = (Way)my.getPrimitiveById(3, OsmPrimitiveType.WAY);
-        assertEquals(1,visitor.getConflicts().size());
-        assertEquals(true, visitor.getConflicts().hasConflictForMy(myWay));
-        assertEquals(true, visitor.getConflicts().hasConflictForTheir(theirWay));
-        assertEquals(myWay,merged);
-    }
-
-    /**
-     * my and their way have no ids,  nodes they refer to have an id. but
-     * my and  their way are semantically equal. so technical attributes of
-     * their way can be merged on my way. No conflict.
-     *
-     */
-    @Test
-    public void waySimple_twoWaysWithNoId_NodesWithId() {
-
-        // -- my data set
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-
-        Node n1 = new Node(new LatLon(0,0));
-        n1.setOsmId(1,1);
-        my.addPrimitive(n1);
-
-        Node n2 = new Node(new LatLon(1,1));
-        n2.setOsmId(2,1);
-        my.addPrimitive(n2);
-
-        Way myWay = new Way();
-        myWay.addNode(n1);
-        myWay.addNode(n2);
-        my.addPrimitive(myWay);
-
-        // -- their data set
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-
-        Node n3 = new Node(new LatLon(0,0));
-        n3.setOsmId(1,1);
-        their.addPrimitive(n3);
-
-        Node n4 = new Node(new LatLon(1,1));
-        n4.setOsmId(2,1);
-        their.addPrimitive(n4);
-
-        Way theirWay = new Way();
-        theirWay.addNode(n3);
-        theirWay.addNode(n4);
-        theirWay.setUser(User.createOsmUser(1111, "their"));
-        theirWay.setTimestamp(new Date());
-        their.addPrimitive(theirWay);
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        // -- tests
-        Way merged = (Way)my.getWays().toArray()[0];
-        assertEquals(0,visitor.getConflicts().size());
-        assertEquals("their", merged.getUser().getName());
-        assertEquals(1111, merged.getUser().getId());
-        assertEquals(theirWay.getTimestamp(), merged.getTimestamp());
-        assertTrue(merged == myWay);
-        assertTrue(merged.getNode(0) == n1);
-        assertTrue(merged.getNode(1) == n2);
-    }
-
-    /**
-     * my and their way have no ids, neither do the nodes they refer to. but
-     * my and  their way are semantically equal. so technical attributes of
-     * their way can be merged on my way. No conflict.
-     *
-     */
-    @Test
-    public void waySimple_twoWaysWithNoId_NodesWithoutId() {
-
-        // -- my data set
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-
-        Node n1 = new Node(new LatLon(0,0));
-        my.addPrimitive(n1);
-
-        Node n2 = new Node(new LatLon(1,1));
-        my.addPrimitive(n2);
-
-        Way myWay = new Way();
-        myWay.addNode(n1);
-        myWay.addNode(n2);
-        my.addPrimitive(myWay);
-
-        // -- their data set
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-
-        Node n3 = new Node(new LatLon(0,0));
-        their.addPrimitive(n3);
-
-        Node n4 = new Node(new LatLon(1,1));
-        their.addPrimitive(n4);
-
-        Way theirWay = new Way();
-        theirWay.addNode(n3);
-        theirWay.addNode(n4);
-        User user = User.createOsmUser(1111, "their");
-        theirWay.setUser(user);
-        theirWay.setTimestamp(new Date());
-        their.addPrimitive(theirWay);
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        // -- tests
-        Way merged = (Way)my.getWays().toArray()[0];
-        assertEquals(0,visitor.getConflicts().size());
-        assertEquals("their", merged.getUser().getName());
-        assertEquals(1111, merged.getUser().getId());
-        assertEquals(theirWay.getTimestamp(), merged.getTimestamp());
-        assertTrue(merged == myWay);
-        assertTrue(merged.getNode(0) == n1);
-        assertTrue(merged.getNode(1) == n2);
-    }
-
-
-    /**
-     * My dataset includes a deleted node.
-     * Their dataset includes a way with three nodes, the first one being my node.
-     *
-     * => the merged way should include two nodes only. the deleted node should still be
-     * in the data set
-     *
-     */
-    @Test
-    public void wayComplex_mergingADeletedNode() {
-
-        // -- my dataset
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-
-        Node n1 = new Node(new LatLon(0,0));
-        n1.setOsmId(1,1);
-        n1.setDeleted(true);
-        my.addPrimitive(n1);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-
-        Node n3 = new Node(new LatLon(0,0));
-        n3.setOsmId(1,1);
-        their.addPrimitive(n3);
-
-        Node n4 = new Node(new LatLon(1,1));
-        n4.setOsmId(2,1);
-        their.addPrimitive(n4);
-
-        Node n5 = new Node(new LatLon(2,2));
-        n5.setOsmId(3,1);
-        their.addPrimitive(n5);
-
-        // -- their data set
-        Way theirWay = new Way();
-        theirWay.setOsmId(4,1);
-        theirWay.addNode(n3);
-        theirWay.addNode(n4);
-        theirWay.addNode(n5);
-        theirWay.setUser(User.createOsmUser(1111, "their"));
-        theirWay.setTimestamp(new Date());
-        their.addPrimitive(theirWay);
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        assertEquals(0,visitor.getConflicts().size());
-
-        Way myWay = (Way)my.getPrimitiveById(4,OsmPrimitiveType.WAY);
-        assertEquals(2, myWay.getNodesCount());
-
-        Node n = (Node)my.getPrimitiveById(1,OsmPrimitiveType.NODE);
-        assertTrue(!myWay.getNodes().contains(n));
-        assertTrue(n != null);
-    }
-
-    /**
-     * My dataset includes a deleted node.
-     * Their dataset includes a relation with three nodes, the first one being my node.
-     *
-     * => the merged relation should include two nodes only. the deleted node should still be
-     * in the data set
-     *
-     */
-    @Test
-    public void relationComplex_mergingADeletedNode() {
-
-        DataSet my = new DataSet();
-        my.setVersion("0.6");
-
-        Node n1 = new Node(new LatLon(0,0));
-        n1.setOsmId(1,1);
-        n1.setDeleted(true);
-        my.addPrimitive(n1);
-
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-
-        Node n3 = new Node(new LatLon(0,0));
-        n3.setOsmId(1,1);
-        their.addPrimitive(n3);
-
-        Node n4 = new Node(new LatLon(1,1));
-        n4.setOsmId(2,1);
-        their.addPrimitive(n4);
-
-        Node n5 = new Node(new LatLon(2,2));
-        n5.setOsmId(3,1);
-        their.addPrimitive(n5);
-
-
-        Relation theirRelation = new Relation();
-        theirRelation.setOsmId(4,1);
-
-        theirRelation.addMember(new RelationMember("", n3));
-        theirRelation.addMember(new RelationMember("", n4));
-        theirRelation.addMember(new RelationMember("", n5));
-        their.addPrimitive(theirRelation);
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        assertEquals(0,visitor.getConflicts().size());
-
-        Relation r = (Relation)my.getPrimitiveById(4,OsmPrimitiveType.RELATION);
-        assertEquals(2, r.getMembersCount());
-
-        Node n = (Node)my.getPrimitiveById(1,OsmPrimitiveType.NODE);
-        assertTrue(n != null);
-    }
-
-    /**
-     * Merge an incomplete way with two incomplete nodes into an empty dataset.
-     * 
-     * Use case: a way loaded with a multiget, i.e. GET /api/0.6/ways?ids=123456
-     */
-    @Test
-    public void newIncompleteWay() {
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-
-        Node n1 = new Node(1);
-        their.addPrimitive(n1);
-
-        Node n2 = new Node(2);
-        their.addPrimitive(n2);
-
-        Way w3 = new Way(3);
-        w3.incomplete = true;
-        w3.setNodes(Arrays.asList(n1,n2));
-        their.addPrimitive(w3);
-
-        DataSet my = new DataSet();
-        their.setVersion("0.6");
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        assertEquals(0,visitor.getConflicts().size());
-
-        OsmPrimitive p= my.getPrimitiveById(1, OsmPrimitiveType.NODE);
-        assertNotNull(p);
-        assertTrue(p.incomplete);
-        p= my.getPrimitiveById(2, OsmPrimitiveType.NODE);
-        assertNotNull(p);
-        assertTrue(p.incomplete);
-        p= my.getPrimitiveById(3, OsmPrimitiveType.WAY);
-        assertNotNull(p);
-        assertTrue(p.incomplete);
-
-        Way w = (Way)my.getPrimitiveById(3, OsmPrimitiveType.WAY);
-        assertNotNull(w);
-        assertTrue(p.incomplete);
-        assertEquals(2, w.getNodesCount());
-        assertTrue(w.getNode(0).incomplete);
-        assertTrue(w.getNode(1).incomplete);
-    }
-
-    /**
-     * Merge an incomplete way with two incomplete nodes into a dataset where the way already exists as complete way.
-     * 
-     * Use case: a way loaded with a multiget, i.e. GET /api/0.6/ways?ids=123456 after a "Update selection " of this way
-     */
-    @Test
-    public void incompleteWayOntoCompleteWay() {
-        DataSet their = new DataSet();
-        their.setVersion("0.6");
-
-        // an incomplete node
-        Node n1 = new Node(1);
-        their.addPrimitive(n1);
-
-        // another incomplete node
-        Node n2 = new Node(2);
-        their.addPrimitive(n2);
-
-        // an incomplete way with two incomplete nodes
-        Way w3 = new Way(3);
-        w3.incomplete = true;
-        w3.setNodes(Arrays.asList(n1,n2));
-        their.addPrimitive(w3);
-
-        DataSet my = new DataSet();
-        their.setVersion("0.6");
-
-        Node n4 = new Node(new LatLon(0,0));
-        n4.setOsmId(1,1);
-        my.addPrimitive(n4);
-
-        Node n5 = new Node(new LatLon(1,1));
-        n5.setOsmId(2,1);
-        my.addPrimitive(n5);
-
-        Way w6 = new Way(3);
-        w6.incomplete = false;
-        w6.setNodes(Arrays.asList(n4,n5));
-        my.addPrimitive(w6);
-
-        DataSetMerger visitor = new DataSetMerger(my,their);
-        visitor.merge();
-
-        assertEquals(0,visitor.getConflicts().size());
-
-        OsmPrimitive p= my.getPrimitiveById(1, OsmPrimitiveType.NODE);
-        assertNotNull(p);
-        assertTrue(!p.incomplete);
-        p= my.getPrimitiveById(2, OsmPrimitiveType.NODE);
-        assertNotNull(p);
-        assertTrue(!p.incomplete);
-        p= my.getPrimitiveById(3,OsmPrimitiveType.WAY);
-        assertNotNull(p);
-        assertTrue(!p.incomplete);
-
-        Way w = (Way)my.getPrimitiveById(3,OsmPrimitiveType.WAY);
-        assertNotNull(w);
-        assertTrue(!p.incomplete);
-        assertEquals(2, w.getNodesCount());
-        assertTrue(!w.getNode(0).incomplete);
-        assertTrue(!w.getNode(1).incomplete);
-    }
-}
