Index: /trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java	(revision 2094)
+++ /trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java	(revision 2095)
@@ -33,5 +33,5 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.ExtendedDialog;
-import org.openstreetmap.josm.gui.conflict.tags.CombineWaysConflictResolverDialog;
+import org.openstreetmap.josm.gui.conflict.tags.CombinePrimitiveResolverDialog;
 import org.openstreetmap.josm.tools.Pair;
 import org.openstreetmap.josm.tools.Shortcut;
@@ -166,7 +166,7 @@
         completeTagCollectionWithMissingTags(completeWayTags, ways);
 
-        CombineWaysConflictResolverDialog dialog = CombineWaysConflictResolverDialog.getInstance();
+        CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
         dialog.getTagConflictResolverModel().populate(completeWayTags);
-        dialog.setTargetWay(targetWay);
+        dialog.setTargetPrimitive(targetWay);
         dialog.getRelationMemberConflictResolverModel().populate(
                 referringRelations.getRelations(),
@@ -191,5 +191,5 @@
         cmds.add(new DeleteCommand(deletedWays));
         cmds.add(new ChangeCommand(targetWay, modifiedTargetWay));
-        cmds.addAll(dialog.buildResolutionCommands(targetWay));
+        cmds.addAll(dialog.buildResolutionCommands());
         final SequenceCommand sequenceCommand = new SequenceCommand(tr("Combine {0} ways", ways.size()), cmds);
 
Index: /trunk/src/org/openstreetmap/josm/actions/MergeNodesAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/MergeNodesAction.java	(revision 2094)
+++ /trunk/src/org/openstreetmap/josm/actions/MergeNodesAction.java	(revision 2095)
@@ -4,24 +4,13 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.KeyEvent;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
 import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.Map.Entry;
-
-import javax.swing.Box;
-import javax.swing.JComboBox;
-import javax.swing.JLabel;
+
 import javax.swing.JOptionPane;
-import javax.swing.JPanel;
 
 import org.openstreetmap.josm.Main;
@@ -30,23 +19,19 @@
 import org.openstreetmap.josm.command.DeleteCommand;
 import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.osm.BackreferencedDataSet;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Relation;
-import org.openstreetmap.josm.data.osm.RelationMember;
-import org.openstreetmap.josm.data.osm.TigerUtils;
+import org.openstreetmap.josm.data.osm.Tag;
+import org.openstreetmap.josm.data.osm.TagCollection;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor;
-import org.openstreetmap.josm.gui.ExtendedDialog;
-import org.openstreetmap.josm.tools.GBC;
-import org.openstreetmap.josm.tools.Pair;
+import org.openstreetmap.josm.data.osm.BackreferencedDataSet.RelationToChildReference;
+import org.openstreetmap.josm.gui.conflict.tags.CombinePrimitiveResolverDialog;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.tools.Shortcut;
 
 
 /**
- * Merge two or more nodes into one node.
- * (based on Combine ways)
- *
- * @author Matthew Newton
- *
+ * Merges a collection of nodes into one node.
+ * 
  */
 public class MergeNodesAction extends JosmAction {
@@ -60,16 +45,6 @@
         if (!isEnabled())
             return;
-
         Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected();
-        LinkedList<Node> selectedNodes = new LinkedList<Node>();
-
-        // the selection check should stop this procedure starting if
-        // nothing but node are selected - otherwise we don't care
-        // anyway as long as we have at least two nodes
-        for (OsmPrimitive osm : selection)
-            if (osm instanceof Node) {
-                selectedNodes.add((Node)osm);
-            }
-
+        Set<Node> selectedNodes = OsmPrimitive.getFilteredSet(selection, Node.class);
         if (selectedNodes.size() < 2) {
             JOptionPane.showMessageDialog(
@@ -82,4 +57,46 @@
         }
 
+
+        Node targetNode = selectTargetNode(selectedNodes);
+        Command cmd = mergeNodes(Main.main.getEditLayer(), selectedNodes, targetNode);
+        if (cmd != null) {
+            Main.main.undoRedo.add(cmd);
+            Main.main.getEditLayer().data.setSelected(targetNode);
+        }
+    }
+
+    protected void completeTagCollectionWithMissingTags(TagCollection tc, Collection<Node> mergedNodes) {
+        for (String key: tc.getKeys()) {
+            // make sure the empty value is in the tag set if a tag is not present
+            // on all merged nodes
+            //
+            for (Node n: mergedNodes) {
+                if (n.get(key) == null) {
+                    tc.add(new Tag(key)); // add a tag with key and empty value
+                }
+            }
+        }
+        // remove irrelevant tags
+        //
+        tc.removeByKey("created_by");
+    }
+
+    protected void completeTagCollectionForEditing(TagCollection tc) {
+        for (String key: tc.getKeys()) {
+            // make sure the empty value is in the tag set such that we can delete the tag
+            // in the conflict dialog if necessary
+            //
+            tc.add(new Tag(key,""));
+        }
+    }
+
+    /**
+     * Selects a node out of a collection of candidate nodes. The selected
+     * node will become the target node the remaining nodes are merged to.
+     * 
+     * @param candidates the collection of candidate nodes
+     * @return the selected target node
+     */
+    public Node selectTargetNode(Collection<Node> candidates) {
         // Find which node to merge into (i.e. which one will be left)
         // - this should be combined from two things:
@@ -93,170 +110,112 @@
         // that the user doesn't know which node will be chosen (so
         // (2) is not implemented yet.)  :-(
-        Node useNode = null;
-        for (Node n: selectedNodes) {
+        Node targetNode = null;
+        for (Node n: candidates) {
             if (n.getId() > 0) {
-                useNode = n;
+                targetNode = n;
                 break;
             }
         }
-        if (useNode == null) {
-            useNode = selectedNodes.iterator().next();
-        }
-
-        mergeNodes(selectedNodes, useNode);
-    }
-
-    /**
-     * really do the merging - returns the node that is left
-     */
-    public Node mergeNodes(LinkedList<Node> allNodes, Node dest) {
-        Node newNode = new Node(dest);
-
-        // Check whether all ways have identical relationship membership. More
-        // specifically: If one of the selected ways is a member of relation X
-        // in role Y, then all selected ways must be members of X in role Y.
-
-        // FIXME: In a later revision, we should display some sort of conflict
-        // dialog like we do for tags, to let the user choose which relations
-        // should be kept.
-
-        // Step 1, iterate over all relations and figure out which of our
-        // selected ways are members of a relation.
-        HashMap<Pair<Relation,String>, HashSet<Node>> backlinks =
-            new HashMap<Pair<Relation,String>, HashSet<Node>>();
-        HashSet<Relation> relationsUsingNodes = new HashSet<Relation>();
-        for (Relation r : getCurrentDataSet().relations) {
-            if (r.isDeleted() || r.incomplete) {
-                continue;
-            }
-            for (RelationMember rm : r.getMembers()) {
-                if (rm.isNode()) {
-                    for (Node n : allNodes) {
-                        if (rm.getMember() == n) {
-                            Pair<Relation,String> pair = new Pair<Relation,String>(r, rm.getRole());
-                            HashSet<Node> nodelinks = new HashSet<Node>();
-                            if (backlinks.containsKey(pair)) {
-                                nodelinks = backlinks.get(pair);
-                            } else {
-                                nodelinks = new HashSet<Node>();
-                                backlinks.put(pair, nodelinks);
-                            }
-                            nodelinks.add(n);
-
-                            // this is just a cache for later use
-                            relationsUsingNodes.add(r);
-                        }
-                    }
+        if (targetNode == null) {
+            // an arbitrary node
+            targetNode = candidates.iterator().next();
+        }
+        return targetNode;
+    }
+
+
+    /**
+     * Merges the nodes in <code>node</code> onto one of the nodes. Uses the dataset
+     * managed by <code>layer</code> as reference.
+     * 
+     * @param layer the reference data layer. Must not be null.
+     * @param nodes the collection of nodes. Ignored if null.
+     * @param targetNode the target node the collection of nodes is merged to. Must not be null.
+     * @throws IllegalArgumentException thrown if layer is null
+     * @throws IllegalArgumentException thrown if targetNode is null
+     * 
+     */
+    public Command mergeNodes(OsmDataLayer layer, Collection<Node> nodes, Node targetNode) throws IllegalArgumentException{
+        if (layer == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "nodes"));
+        if (targetNode == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "targetNode"));
+
+        if (nodes == null)
+            return null;
+        nodes.remove(null); // just in case
+        BackreferencedDataSet backreferences = new BackreferencedDataSet(layer.data);
+        backreferences.build();
+        return mergeNodes(layer,backreferences, nodes, targetNode);
+    }
+
+    /**
+     * Merges the nodes in <code>node</code> onto one of the nodes. Uses the dataset
+     * managed by <code>layer</code> as reference. <code>backreferences</code> is precomputed
+     * collection of all parent/child references in the dataset.
+     *
+     * @param layer layer the reference data layer. Must not be null.
+     * @param backreferences if null, backreferneces are first computed from layer.data; otherwise
+     *    backreferences.getSource() == layer.data must hold
+     * @param nodes the collection of nodes. Ignored if null.
+     * @param targetNode the target node the collection of nodes is merged to. Must not be null.
+     * @throw IllegalArgumentException thrown if layer is null
+     * @throw IllegalArgumentException thrown if  backreferences.getSource() != layer.data
+     */
+    public Command mergeNodes(OsmDataLayer layer, BackreferencedDataSet backreferences, Collection<Node> nodes, Node targetNode) {
+        if (layer == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "nodes"));
+        if (targetNode == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "targetNode"));
+        if (nodes == null)
+            return null;
+        if (backreferences == null) {
+            backreferences = new BackreferencedDataSet(layer.data);
+            backreferences.build();
+        }
+
+        Set<RelationToChildReference> relationToNodeReferences = backreferences.getRelationToChildReferences(nodes);
+
+        // build the tag collection
+        //
+        TagCollection nodeTags = TagCollection.unionOfAllPrimitives(nodes);
+        completeTagCollectionWithMissingTags(nodeTags, nodes);
+        TagCollection nodeTagsToEdit = new TagCollection(nodeTags);
+        completeTagCollectionForEditing(nodeTagsToEdit);
+
+        // launch a conflict resolution dialog, if necessary
+        //
+        CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
+        dialog.getTagConflictResolverModel().populate(nodeTagsToEdit);
+        dialog.getRelationMemberConflictResolverModel().populate(relationToNodeReferences);
+        dialog.setTargetPrimitive(targetNode);
+        dialog.prepareDefaultDecisions();
+        if (! nodeTags.isApplicableToPrimitive() || relationToNodeReferences.size() > 1) {
+            dialog.setVisible(true);
+            if (dialog.isCancelled())
+                return null;
+        }
+        LinkedList<Command> cmds = new LinkedList<Command>();
+
+        // the nodes we will have to delete
+        //
+        Collection<OsmPrimitive> nodesToDelete = new HashSet<OsmPrimitive>(nodes);
+        nodesToDelete.remove(targetNode);
+
+        // change the ways referring to at least one of the merge nodes
+        //
+        Collection<Way> waysToDelete= new HashSet<Way>();
+        for (Way w : OsmPrimitive.getFilteredList(backreferences.getParents(nodesToDelete), Way.class)) {
+            // OK - this way contains one or more nodes to change
+            ArrayList<Node> newNodes = new ArrayList<Node>(w.getNodesCount());
+            for (Node n: w.getNodes()) {
+                if (! nodesToDelete.contains(n)) {
+                    newNodes.add(n);
                 }
             }
-        }
-
-        // Complain to the user if the ways don't have equal memberships.
-        for (HashSet<Node> nodelinks : backlinks.values()) {
-            if (!nodelinks.containsAll(allNodes)) {
-                ExtendedDialog ed = new ExtendedDialog(Main.parent,
-                        tr("Merge nodes with different memberships?"),
-                        new String[] {tr("Merge Anyway"), tr("Cancel")});
-                ed.setButtonIcons(new String[] {"mergenodes.png", "cancel.png"});
-                ed.setContent(tr("The selected nodes have differing relation memberships.  "
-                        + "Do you still want to merge them?"));
-                ed.showDialog();
-
-                if (ed.getValue() == 1) {
-                    break;
-                }
-                return null;
-            }
-        }
-
-        // collect properties for later conflict resolving
-        Map<String, Set<String>> props = new TreeMap<String, Set<String>>();
-        for (Node n : allNodes) {
-            for (Entry<String,String> e : n.entrySet()) {
-                if (!props.containsKey(e.getKey())) {
-                    props.put(e.getKey(), new TreeSet<String>());
-                }
-                props.get(e.getKey()).add(e.getValue());
-            }
-        }
-
-        // display conflict dialog
-        Map<String, JComboBox> components = new HashMap<String, JComboBox>();
-        JPanel p = new JPanel(new GridBagLayout());
-        for (Entry<String, Set<String>> e : props.entrySet()) {
-            if (TigerUtils.isTigerTag(e.getKey())) {
-                String combined = TigerUtils.combineTags(e.getKey(), e.getValue());
-                newNode.put(e.getKey(), combined);
-            } else if (e.getValue().size() > 1) {
-                JComboBox c = new JComboBox(e.getValue().toArray());
-                c.setEditable(true);
-                p.add(new JLabel(e.getKey()), GBC.std());
-                p.add(Box.createHorizontalStrut(10), GBC.std());
-                p.add(c, GBC.eol());
-                components.put(e.getKey(), c);
-            } else {
-                newNode.put(e.getKey(), e.getValue().iterator().next());
-            }
-        }
-
-        if (!components.isEmpty()) {
-            ExtendedDialog dialog = new ExtendedDialog(
-                    Main.parent,
-                    tr("Enter values for all conflicts."),
-                    new String[] {tr("Solve Conflicts"), tr("Cancel")}
-            );
-            dialog.setButtonIcons(new String[] {"dialogs/conflict.png", "cancel.png"});
-            dialog.setContent(p);
-            dialog.showDialog();
-            int answer = dialog.getValue();
-
-            if (answer != 1)
-                return null;
-            for (Entry<String, JComboBox> e : components.entrySet()) {
-                newNode.put(e.getKey(), e.getValue().getEditor().getItem().toString());
-            }
-        }
-
-        LinkedList<Command> cmds = new LinkedList<Command>();
-
-	if (!newNode.getKeys().equals(dest.getKeys())) {
-            cmds.add(new ChangeCommand(dest, newNode));
-	}
-
-        Collection<OsmPrimitive> del = new HashSet<OsmPrimitive>();
-
-        for (Way w : getCurrentDataSet().ways) {
-            if (w.isDeleted() || w.incomplete || w.getNodesCount() < 1) {
-                continue;
-            }
-            boolean modify = false;
-            for (Node sn : allNodes) {
-                if (sn == dest) {
-                    continue;
-                }
-                if (w.containsNode(sn)) {
-                    modify = true;
-                }
-            }
-            if (!modify) {
-                continue;
-            }
-            // OK - this way contains one or more nodes to change
-            ArrayList<Node> nn = new ArrayList<Node>();
-            Node lastNode = null;
-            for (Node pushNode: w.getNodes()) {
-                if (allNodes.contains(pushNode)) {
-                    pushNode = dest;
-                }
-                if (pushNode != lastNode) {
-                    nn.add(pushNode);
-                }
-                lastNode = pushNode;
-            }
-            if (nn.size() < 2) {
-                CollectBackReferencesVisitor backRefs =
-                    new CollectBackReferencesVisitor(getCurrentDataSet(), false);
-                w.visit(backRefs);
-                if (!backRefs.data.isEmpty()) {
+            if (newNodes.size() < 2) {
+                if (backreferences.getParents(w).isEmpty()) {
+                    waysToDelete.add(w);
+                } else {
                     JOptionPane.showMessageDialog(
                             Main.parent,
@@ -268,44 +227,24 @@
                     return null;
                 }
-                del.add(w);
+            } else if(newNodes.size() < 2 && backreferences.getParents(w).isEmpty()) {
+                waysToDelete.add(w);
             } else {
                 Way newWay = new Way(w);
-                newWay.setNodes(nn);
+                newWay.setNodes(newNodes);
                 cmds.add(new ChangeCommand(w, newWay));
             }
         }
 
-        // delete any merged nodes
-        del.addAll(allNodes);
-        del.remove(dest);
-        if (!del.isEmpty()) {
-            cmds.add(new DeleteCommand(del));
-        }
-
-        // modify all relations containing the now-deleted nodes
-        for (Relation r : relationsUsingNodes) {
-            List<RelationMember> newMembers = new ArrayList<RelationMember>();
-            HashSet<String> rolesToReAdd = new HashSet<String>();
-            for (RelationMember rm : r.getMembers()) {
-                // Don't copy the member if it points to one of our nodes,
-                // just keep a note to re-add it later on.
-                if (allNodes.contains(rm.getMember())) {
-                    rolesToReAdd.add(rm.getRole());
-                } else {
-                    newMembers.add(rm);
-                }
-            }
-            for (String role : rolesToReAdd) {
-                newMembers.add(new RelationMember(role, dest));
-            }
-            Relation newRel = new Relation(r);
-            newRel.setMembers(newMembers);
-            cmds.add(new ChangeCommand(r, newRel));
-        }
-
-        Main.main.undoRedo.add(new SequenceCommand(tr("Merge {0} nodes", allNodes.size()), cmds));
-        getCurrentDataSet().setSelected(dest);
-
-        return dest;
+        // build the commands
+        //
+        if (!nodesToDelete.isEmpty()) {
+            cmds.add(new DeleteCommand(nodesToDelete));
+        }
+        if (!waysToDelete.isEmpty()) {
+            cmds.add(new DeleteCommand(waysToDelete));
+        }
+        cmds.addAll(dialog.buildResolutionCommands());
+        Command cmd = new SequenceCommand(tr("Merge {0} nodes", nodes.size()), cmds);
+        return cmd;
     }
 
Index: /trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java	(revision 2094)
+++ /trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java	(revision 2095)
@@ -17,4 +17,5 @@
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 import java.util.TreeSet;
 import java.util.logging.Logger;
@@ -43,4 +44,5 @@
 import org.openstreetmap.josm.gui.SelectionManager;
 import org.openstreetmap.josm.gui.SelectionManager.SelectionEnded;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog.MergeAction;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
@@ -489,12 +491,10 @@
                     if (nn != null) {
                         Node n = nn.iterator().next();
-                        LinkedList<Node> selNodes = new LinkedList<Node>();
-                        for (OsmPrimitive osm : selection)
-                            if (osm instanceof Node) {
-                                selNodes.add((Node)osm);
-                            }
-                        if (selNodes.size() > 0) {
-                            selNodes.add(n);
-                            new MergeNodesAction().mergeNodes(selNodes, n);
+                        Set<Node> selectedNodes = OsmPrimitive.getFilteredSet(selection, Node.class);
+                        if (!selectedNodes.isEmpty()) {
+                            selectedNodes.add(n);
+                            MergeNodesAction mergeAction = new MergeNodesAction();
+                            Node targetNode = mergeAction.selectTargetNode(selectedNodes);
+                            mergeAction.mergeNodes(Main.main.getEditLayer(),selectedNodes, targetNode);
                         }
                     }
Index: /trunk/src/org/openstreetmap/josm/data/osm/BackreferencedDataSet.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/BackreferencedDataSet.java	(revision 2095)
+++ /trunk/src/org/openstreetmap/josm/data/osm/BackreferencedDataSet.java	(revision 2095)
@@ -0,0 +1,227 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+public class BackreferencedDataSet {
+    public static class RelationToChildReference {
+        private Relation parent;
+        private int position;
+        private String role;
+        private OsmPrimitive child;
+
+        public RelationToChildReference(Relation parent, int position, String role, OsmPrimitive child) {
+            this.parent = parent;
+            this.position = position;
+            this.role = role;
+            this.child = child;
+        }
+
+        public RelationToChildReference(Relation parent, int position, RelationMember member) {
+            this.parent = parent;
+            this.position = position;
+            this.role = member.getRole();
+            this.child = member.getMember();
+        }
+
+        public Relation getParent() {
+            return parent;
+        }
+
+        public int getPosition() {
+            return position;
+        }
+
+        public String getRole() {
+            return role;
+        }
+
+        public OsmPrimitive getChild() {
+            return child;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((child == null) ? 0 : child.hashCode());
+            result = prime * result + ((parent == null) ? 0 : parent.hashCode());
+            result = prime * result + position;
+            result = prime * result + ((role == null) ? 0 : role.hashCode());
+            return result;
+        }
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            RelationToChildReference other = (RelationToChildReference) obj;
+            if (child == null) {
+                if (other.child != null)
+                    return false;
+            } else if (!child.equals(other.child))
+                return false;
+            if (parent == null) {
+                if (other.parent != null)
+                    return false;
+            } else if (!parent.equals(other.parent))
+                return false;
+            if (position != other.position)
+                return false;
+            if (role == null) {
+                if (other.role != null)
+                    return false;
+            } else if (!role.equals(other.role))
+                return false;
+            return true;
+        }
+    }
+
+    private DataSet source;
+    private Map<OsmPrimitive, Set<OsmPrimitive>> referers;
+    private boolean built = false;
+
+
+    /**
+     * Creates a new backreference data set based on the dataset <code>source</code>.
+     * 
+     * Doesn't create the cache of backreferences yet. Invoke {@see #build()} on the
+     * created {@see BackreferencedDataSet}.
+     * 
+     * @param source the source. Must not be null.
+     * @throws IllegalArgumentException thrown if source is null
+     */
+    public BackreferencedDataSet(DataSet source) {
+        if (source == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null"));
+        this.source = source;
+        int size = source.ways.size() + source.relations.size();
+        referers = new HashMap<OsmPrimitive, Set<OsmPrimitive>>(size, 0.75f);
+    }
+
+    /**
+     * Remembers a reference from a parent primitive to a child primitive
+     * 
+     * @param parent the parent primitive
+     * @param child the child primitive
+     */
+    protected void remember(OsmPrimitive parent, OsmPrimitive child) {
+        Set<OsmPrimitive> parents = referers.get(child);
+        if (parents != null) {
+            parents.add(parent);
+            return;
+        }
+        parents = new HashSet<OsmPrimitive>();
+        parents.add(parent);
+        referers.put(child, parents);
+    }
+
+    /**
+     * Builds the dataset of back references
+     * 
+     */
+    public void build() {
+        for (Way w: source.ways) {
+            for (Node n: w.getNodes()) {
+                remember(w,n);
+            }
+        }
+        for (Relation r: source.relations) {
+            for (RelationMember m: r.getMembers()) {
+                remember(r, m.getMember());
+            }
+        }
+        built = true;
+    }
+
+    /**
+     * Replies the set of parent primitives for a given child primitive. Replies
+     * an empty set if no parents refer to the child.
+     * 
+     * @param child the child primitive
+     * @return  the set of parent primitives for a given child primitive.
+     */
+    public Set<OsmPrimitive> getParents(OsmPrimitive child) {
+        Set<OsmPrimitive> parents = referers.get(child);
+        return parents == null ? new HashSet<OsmPrimitive>() : parents;
+    }
+
+    public Set<OsmPrimitive> getParents(Collection<? extends OsmPrimitive> children) {
+        if (children == null) return Collections.emptySet();
+        children.remove(null);
+        Set<OsmPrimitive> parents = new HashSet<OsmPrimitive>();
+        for(OsmPrimitive child: children) {
+            if (referers.get(child) != null) {
+                parents.addAll(referers.get(child));
+            }
+        }
+        return parents;
+    }
+
+    /**
+     * Replies true if there is at least one parent referring to child;
+     * false otherwise
+     * 
+     * @param child the child primitive
+     * @return true if there is at least one parent referring to child;
+     */
+    public boolean hasParents(OsmPrimitive child) {
+        return ! getParents(child).isEmpty();
+    }
+
+    /**
+     * Replies the source dataset for this Backreference DataSet
+     * 
+     * @return the source dataset
+     */
+    public DataSet getSource() {
+        return source;
+    }
+
+    /**
+     * Replies a set of all {@see RelationToChildReference}s for a given child primitive.
+     * 
+     * @param child the child primitive
+     * @return  a set of all {@see RelationToChildReference}s for a given child primitive
+     */
+    public Set<RelationToChildReference> getRelationToChildReferences(OsmPrimitive child) {
+        Set<Relation> parents = OsmPrimitive.getFilteredSet(getParents(child), Relation.class);
+        Set<RelationToChildReference> references = new HashSet<RelationToChildReference>();
+        for (Relation parent: parents) {
+            for (int i=0; i < parent.getMembersCount(); i++) {
+                if (parent.getMember(i).refersTo(child)) {
+                    references.add(new RelationToChildReference(parent, i, parent.getMember(i)));
+                }
+            }
+        }
+        return references;
+    }
+
+    /**
+     * Replies a set of all {@see RelationToChildReference}s for a collection of child primitives
+     * 
+     * @param children the collection of child primitives
+     * @return  a set of all {@see RelationToChildReference}s to the children in the collection of child
+     * primitives
+     */
+    public Set<RelationToChildReference> getRelationToChildReferences(Collection<? extends OsmPrimitive> children) {
+        Set<RelationToChildReference> references = new HashSet<RelationToChildReference>();
+        for (OsmPrimitive child: children) {
+            references.addAll(getRelationToChildReferences(child));
+        }
+        return references;
+    }
+
+    public boolean isBuilt() {
+        return built;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java	(revision 2095)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java	(revision 2095)
@@ -0,0 +1,345 @@
+package org.openstreetmap.josm.gui.conflict.tags;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JSplitPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.ChangePropertyCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.TagCollection;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.DefaultNameFormatter;
+import org.openstreetmap.josm.gui.SideButton;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.WindowGeometry;
+
+/**
+ * This dialog helps to resolve conflicts occuring when combining or merging primitives.
+ * 
+ * There is a singleton instance of this dialog which can be retrieved using
+ * {@see #getInstance()}.
+ * 
+ * The dialog uses two model: one model for resolving tag conflicts, the other model
+ * for resolving conflicts in relation membership. For both models there are accessors,
+ * i.e {@see #getTagConflictResolverModel()} and {@see #getRelationMemberConflictResolverModel()}.
+ * 
+ * Models have to be <strong>populated</strong> before the dialog is launched. Example:
+ * <pre>
+ *    CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
+ *    dialog.getTagConflictResolverModel().populate(aTagCollection);
+ *    dialog.getRelationMemberConflictResolverModel().populate(aRelationLinkCollection);
+ *    dialog.prepareDefaultDecisions();
+ * </pre>
+ * 
+ * You should also set the target primitive which other primitives (ways or nodes) are
+ * merged to. Use {@see #setTargetPrimitive(OsmPrimitive)}.
+ * 
+ * After the dialog closed use {@see #isCancelled()} to check whether the user cancelled
+ * the dialog. If it wasn't cancelled you may build a collection of {@see Command} objects
+ * which reflect the conflict resolution decisions the user made in the dialog:
+ * see {@see #buildResolutionCommands()}
+ * 
+ *
+ */
+public class CombinePrimitiveResolverDialog extends JDialog {
+
+    static private CombinePrimitiveResolverDialog instance;
+
+    public static CombinePrimitiveResolverDialog getInstance() {
+        if (instance == null) {
+            instance = new CombinePrimitiveResolverDialog(Main.parent);
+        }
+        return instance;
+    }
+
+    private JSplitPane spTagConflictTypes;
+    private TagConflictResolver pnlTagConflictResolver;
+    private RelationMemberConflictResolver pnlRelationMemberConflictResolver;
+    private boolean cancelled;
+    private JPanel pnlButtons;
+    private OsmPrimitive targetPrimitive;
+
+
+
+    public OsmPrimitive getTargetPrimitmive() {
+        return targetPrimitive;
+    }
+
+    public void setTargetPrimitive(OsmPrimitive primitive) {
+        this.targetPrimitive = primitive;
+        updateTitle();
+    }
+
+    protected void updateTitle() {
+        if (targetPrimitive == null) {
+            setTitle(tr("Conflicts when combining primitives"));
+            return;
+        }
+        if (targetPrimitive instanceof Way) {
+            setTitle(
+                    tr(
+                            "Conflicts when combining ways - combined way is ''{0}''",
+                            targetPrimitive.getDisplayName(DefaultNameFormatter.getInstance())
+                    )
+            );
+        } else if (targetPrimitive instanceof Node) {
+            setTitle(
+                    tr(
+                            "Conflicts when merging nodes - merged node is ''{0}''",
+                            targetPrimitive.getDisplayName(DefaultNameFormatter.getInstance())
+                    )
+            );
+        }
+    }
+
+    protected void build() {
+        getContentPane().setLayout(new BorderLayout());
+        updateTitle();
+        spTagConflictTypes = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+        spTagConflictTypes.setTopComponent(buildTagConflictResolverPanel());
+        spTagConflictTypes.setBottomComponent(buildRelationMemberConflictResolverPanel());
+        getContentPane().add(pnlButtons = buildButtonPanel(), BorderLayout.SOUTH);
+        addWindowListener(new AdjustDividerLocationAction());
+    }
+
+    protected JPanel buildTagConflictResolverPanel() {
+        pnlTagConflictResolver = new TagConflictResolver();
+        return pnlTagConflictResolver;
+    }
+
+    protected JPanel buildRelationMemberConflictResolverPanel() {
+        pnlRelationMemberConflictResolver = new RelationMemberConflictResolver();
+        return pnlRelationMemberConflictResolver;
+    }
+
+    protected JPanel buildButtonPanel() {
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new FlowLayout(FlowLayout.CENTER));
+
+        // -- apply button
+        ApplyAction applyAction = new ApplyAction();
+        pnlTagConflictResolver.getModel().addPropertyChangeListener(applyAction);
+        pnlRelationMemberConflictResolver.getModel().addPropertyChangeListener(applyAction);
+        pnl.add(new SideButton(applyAction));
+
+        // -- cancel button
+        CancelAction cancelAction = new CancelAction();
+        pnl.add(new SideButton(cancelAction));
+
+        return pnl;
+    }
+
+    public CombinePrimitiveResolverDialog(Component owner) {
+        super(JOptionPane.getFrameForComponent(owner),true /* modal */);
+        build();
+    }
+
+    public TagConflictResolverModel getTagConflictResolverModel() {
+        return pnlTagConflictResolver.getModel();
+    }
+
+    public RelationMemberConflictResolverModel getRelationMemberConflictResolverModel() {
+        return pnlRelationMemberConflictResolver.getModel();
+    }
+
+    protected List<Command> buildTagChangeCommand(OsmPrimitive primitive, TagCollection tc) {
+        LinkedList<Command> cmds = new LinkedList<Command>();
+        for (String key : tc.getKeys()) {
+            if (tc.hasUniqueEmptyValue(key)) {
+                if (primitive.get(key) != null) {
+                    cmds.add(new ChangePropertyCommand(primitive, key, null));
+                }
+            } else {
+                String value = tc.getJoinedValues(key);
+                if (!value.equals(primitive.get(key))) {
+                    cmds.add(new ChangePropertyCommand(primitive, key, value));
+                }
+            }
+        }
+        return cmds;
+    }
+
+    public List<Command> buildResolutionCommands() {
+        List<Command> cmds = new LinkedList<Command>();
+
+        if (getTagConflictResolverModel().getNumDecisions() >0) {
+            TagCollection tc = getTagConflictResolverModel().getResolution();
+            cmds.addAll(buildTagChangeCommand(targetPrimitive, tc));
+        }
+
+        if (getRelationMemberConflictResolverModel().getNumDecisions() >0) {
+            cmds.addAll(getRelationMemberConflictResolverModel().buildResolutionCommands(targetPrimitive));
+        }
+
+        Command cmd = pnlRelationMemberConflictResolver.buildTagApplyCommands(
+                getRelationMemberConflictResolverModel().getModifiedRelations(targetPrimitive)
+        );
+        if (cmd != null) {
+            cmds.add(cmd);
+        }
+        return cmds;
+    }
+
+    protected void prepareDefaultTagDecisions() {
+        TagConflictResolverModel model = getTagConflictResolverModel();
+        for (int i =0; i< model.getRowCount(); i++) {
+            MultiValueResolutionDecision decision = model.getDecision(i);
+            List<String> values = decision.getValues();
+            values.remove("");
+            if (values.size() == 1) {
+                decision.keepOne(values.get(0));
+            } else {
+                decision.keepAll();
+            }
+        }
+        model.refresh();
+    }
+
+    protected void prepareDefaultRelationDecisions() {
+        RelationMemberConflictResolverModel model = getRelationMemberConflictResolverModel();
+        for (int i=0; i < model.getNumDecisions(); i++) {
+            model.getDecision(i).decide(RelationMemberConflictDecisionType.REPLACE);
+        }
+        model.refresh();
+    }
+
+    public void prepareDefaultDecisions() {
+        prepareDefaultTagDecisions();
+        prepareDefaultRelationDecisions();
+    }
+
+    protected JPanel buildEmptyConflictsPanel() {
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new BorderLayout());
+        pnl.add(new JLabel(tr("No conflicts to resolve")));
+        return pnl;
+    }
+
+    protected void prepareGUIBeforeConflictResolutionStarts() {
+        RelationMemberConflictResolverModel relModel = getRelationMemberConflictResolverModel();
+        TagConflictResolverModel tagModel = getTagConflictResolverModel();
+        getContentPane().removeAll();
+        if (relModel.getNumDecisions() > 0 && tagModel.getNumDecisions() > 0) {
+            spTagConflictTypes.setTopComponent(pnlTagConflictResolver);
+            spTagConflictTypes.setBottomComponent(pnlRelationMemberConflictResolver);
+            getContentPane().add(spTagConflictTypes, BorderLayout.CENTER);
+        } else if (relModel.getNumDecisions() > 0) {
+            getContentPane().add(pnlRelationMemberConflictResolver, BorderLayout.CENTER);
+        } else if (tagModel.getNumDecisions() >0) {
+            getContentPane().add(pnlTagConflictResolver, BorderLayout.CENTER);
+        } else {
+            getContentPane().add(buildEmptyConflictsPanel(), BorderLayout.CENTER);
+        }
+        getContentPane().add(pnlButtons, BorderLayout.SOUTH);
+        validate();
+        int numTagDecisions = getTagConflictResolverModel().getNumDecisions();
+        int numRelationDecisions = getRelationMemberConflictResolverModel().getNumDecisions();
+        if (numTagDecisions > 0 &&  numRelationDecisions > 0) {
+            spTagConflictTypes.setDividerLocation(0.5);
+        }
+    }
+
+    protected void setCancelled(boolean cancelled) {
+        this.cancelled = cancelled;
+    }
+
+    public boolean isCancelled() {
+        return cancelled;
+    }
+
+    @Override
+    public void setVisible(boolean visible) {
+        if (visible) {
+            prepareGUIBeforeConflictResolutionStarts();
+            new WindowGeometry(
+                    getClass().getName()  + ".geometry",
+                    WindowGeometry.centerInWindow(
+                            Main.parent,
+                            new Dimension(600,400)
+                    )
+            ).applySafe(this);
+            setCancelled(false);
+        } else {
+            new WindowGeometry(this).remember(getClass().getName() + ".geometry");
+        }
+        super.setVisible(visible);
+    }
+
+
+    class CancelAction extends AbstractAction {
+
+        public CancelAction() {
+            putValue(Action.SHORT_DESCRIPTION, tr("Cancel conflict resolution"));
+            putValue(Action.NAME, tr("Cancel"));
+            putValue(Action.SMALL_ICON, ImageProvider.get("", "cancel"));
+            setEnabled(true);
+        }
+
+        public void actionPerformed(ActionEvent arg0) {
+            setCancelled(true);
+            setVisible(false);
+        }
+    }
+
+    class ApplyAction extends AbstractAction implements PropertyChangeListener {
+
+        public ApplyAction() {
+            putValue(Action.SHORT_DESCRIPTION, tr("Apply resolved conflicts"));
+            putValue(Action.NAME, tr("Apply"));
+            putValue(Action.SMALL_ICON, ImageProvider.get("ok"));
+            updateEnabledState();
+        }
+
+        public void actionPerformed(ActionEvent arg0) {
+            setVisible(false);
+        }
+
+        protected void updateEnabledState() {
+            setEnabled(
+                    pnlTagConflictResolver.getModel().getNumConflicts() == 0
+                    && pnlRelationMemberConflictResolver.getModel().getNumConflicts() == 0
+            );
+        }
+
+        public void propertyChange(PropertyChangeEvent evt) {
+            if (evt.getPropertyName().equals(TagConflictResolverModel.NUM_CONFLICTS_PROP)) {
+                updateEnabledState();
+            }
+            if (evt.getPropertyName().equals(RelationMemberConflictResolverModel.NUM_CONFLICTS_PROP)) {
+                updateEnabledState();
+            }
+        }
+    }
+
+    class AdjustDividerLocationAction extends WindowAdapter {
+        @Override
+        public void windowOpened(WindowEvent e) {
+            int numTagDecisions = getTagConflictResolverModel().getNumDecisions();
+            int numRelationDecisions = getRelationMemberConflictResolverModel().getNumDecisions();
+            if (numTagDecisions > 0 &&  numRelationDecisions > 0) {
+                spTagConflictTypes.setDividerLocation(0.5);
+            }
+        }
+    }
+}
Index: unk/src/org/openstreetmap/josm/gui/conflict/tags/CombineWaysConflictResolverDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/tags/CombineWaysConflictResolverDialog.java	(revision 2094)
+++ 	(revision )
@@ -1,306 +1,0 @@
-package org.openstreetmap.josm.gui.conflict.tags;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.awt.FlowLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.util.LinkedList;
-import java.util.List;
-
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.JDialog;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JSplitPane;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.command.ChangePropertyCommand;
-import org.openstreetmap.josm.command.Command;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.TagCollection;
-import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.gui.DefaultNameFormatter;
-import org.openstreetmap.josm.gui.SideButton;
-import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.WindowGeometry;
-
-public class CombineWaysConflictResolverDialog extends JDialog {
-
-    static private CombineWaysConflictResolverDialog instance;
-
-    public static CombineWaysConflictResolverDialog getInstance() {
-        if (instance == null) {
-            instance = new CombineWaysConflictResolverDialog(Main.parent);
-        }
-        return instance;
-    }
-
-    private JSplitPane spTagConflictTypes;
-    private TagConflictResolver pnlTagConflictResolver;
-    private RelationMemberConflictResolver pnlRelationMemberConflictResolver;
-    private boolean cancelled;
-    private JPanel pnlButtons;
-    private Way targetWay;
-
-
-
-    public Way getTargetWay() {
-        return targetWay;
-    }
-
-    public void setTargetWay(Way targetWay) {
-        this.targetWay = targetWay;
-        updateTitle();
-    }
-
-    protected void updateTitle() {
-        if (targetWay == null) {
-            setTitle(tr("Conflicts when combining ways"));
-            return;
-        }
-        setTitle(
-                tr(
-                        "Conflicts when combining ways - combined way is ''{0}''",
-                        targetWay.getDisplayName(DefaultNameFormatter.getInstance())
-                )
-        );
-    }
-
-    protected void build() {
-        getContentPane().setLayout(new BorderLayout());
-        updateTitle();
-        spTagConflictTypes = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
-        spTagConflictTypes.setTopComponent(buildTagConflictResolverPanel());
-        spTagConflictTypes.setBottomComponent(buildRelationMemberConflictResolverPanel());
-        getContentPane().add(pnlButtons = buildButtonPanel(), BorderLayout.SOUTH);
-        addWindowListener(new AdjustDividerLocationAction());
-    }
-
-    protected JPanel buildTagConflictResolverPanel() {
-        pnlTagConflictResolver = new TagConflictResolver();
-        return pnlTagConflictResolver;
-    }
-
-    protected JPanel buildRelationMemberConflictResolverPanel() {
-        pnlRelationMemberConflictResolver = new RelationMemberConflictResolver();
-        return pnlRelationMemberConflictResolver;
-    }
-
-    protected JPanel buildButtonPanel() {
-        JPanel pnl = new JPanel();
-        pnl.setLayout(new FlowLayout(FlowLayout.CENTER));
-
-        // -- apply button
-        ApplyAction applyAction = new ApplyAction();
-        pnlTagConflictResolver.getModel().addPropertyChangeListener(applyAction);
-        pnlRelationMemberConflictResolver.getModel().addPropertyChangeListener(applyAction);
-        pnl.add(new SideButton(applyAction));
-
-        // -- cancel button
-        CancelAction cancelAction = new CancelAction();
-        pnl.add(new SideButton(cancelAction));
-
-        return pnl;
-    }
-
-    public CombineWaysConflictResolverDialog(Component owner) {
-        super(JOptionPane.getFrameForComponent(owner),true /* modal */);
-        build();
-    }
-
-    public TagConflictResolverModel getTagConflictResolverModel() {
-        return pnlTagConflictResolver.getModel();
-    }
-
-    public RelationMemberConflictResolverModel getRelationMemberConflictResolverModel() {
-        return pnlRelationMemberConflictResolver.getModel();
-    }
-
-    protected List<Command> buildTagChangeCommand(OsmPrimitive primitive, TagCollection tc) {
-        LinkedList<Command> cmds = new LinkedList<Command>();
-        for (String key : tc.getKeys()) {
-            if (tc.hasUniqueEmptyValue(key)) {
-                if (primitive.get(key) != null) {
-                    cmds.add(new ChangePropertyCommand(primitive, key, null));
-                }
-            } else {
-                String value = tc.getJoinedValues(key);
-                if (!value.equals(primitive.get(key))) {
-                    cmds.add(new ChangePropertyCommand(primitive, key, value));
-                }
-            }
-        }
-        return cmds;
-    }
-
-    public List<Command> buildResolutionCommands(Way targetWay) {
-        List<Command> cmds = new LinkedList<Command>();
-
-        if (getTagConflictResolverModel().getNumDecisions() >0) {
-            TagCollection tc = getTagConflictResolverModel().getResolution();
-            cmds.addAll(buildTagChangeCommand(targetWay, tc));
-        }
-
-        if (getRelationMemberConflictResolverModel().getNumDecisions() >0) {
-            cmds.addAll(getRelationMemberConflictResolverModel().buildResolutionCommands(targetWay));
-        }
-
-        Command cmd = pnlRelationMemberConflictResolver.buildTagApplyCommands(
-                getRelationMemberConflictResolverModel().getModifiedRelations(targetWay)
-        );
-        if (cmd != null) {
-            cmds.add(cmd);
-        }
-        return cmds;
-    }
-
-    protected void prepareDefaultTagDecisions() {
-        TagConflictResolverModel model = getTagConflictResolverModel();
-        for (int i =0; i< model.getRowCount(); i++) {
-            MultiValueResolutionDecision decision = model.getDecision(i);
-            List<String> values = decision.getValues();
-            values.remove("");
-            if (values.size() == 1) {
-                decision.keepOne(values.get(0));
-            } else {
-                decision.keepAll();
-            }
-        }
-        model.refresh();
-    }
-
-    protected void prepareDefaultRelationDecisions() {
-        RelationMemberConflictResolverModel model = getRelationMemberConflictResolverModel();
-        for (int i=0; i < model.getNumDecisions(); i++) {
-            model.getDecision(i).decide(RelationMemberConflictDecisionType.REPLACE);
-        }
-        model.refresh();
-    }
-
-    public void prepareDefaultDecisions() {
-        prepareDefaultTagDecisions();
-        prepareDefaultRelationDecisions();
-    }
-
-    protected JPanel buildEmptyConflictsPanel() {
-        JPanel pnl = new JPanel();
-        pnl.setLayout(new BorderLayout());
-        pnl.add(new JLabel(tr("No conflicts to resolve")));
-        return pnl;
-    }
-
-    protected void prepareGUIBeforeConflictResolutionStarts() {
-        RelationMemberConflictResolverModel relModel = getRelationMemberConflictResolverModel();
-        TagConflictResolverModel tagModel = getTagConflictResolverModel();
-        getContentPane().removeAll();
-        if (relModel.getNumDecisions() > 0 && tagModel.getNumDecisions() > 0) {
-            spTagConflictTypes.setTopComponent(pnlTagConflictResolver);
-            spTagConflictTypes.setBottomComponent(pnlRelationMemberConflictResolver);
-            getContentPane().add(spTagConflictTypes, BorderLayout.CENTER);
-        } else if (relModel.getNumDecisions() > 0) {
-            getContentPane().add(pnlRelationMemberConflictResolver, BorderLayout.CENTER);
-        } else if (tagModel.getNumDecisions() >0) {
-            getContentPane().add(pnlTagConflictResolver, BorderLayout.CENTER);
-        } else {
-            getContentPane().add(buildEmptyConflictsPanel(), BorderLayout.CENTER);
-        }
-        getContentPane().add(pnlButtons, BorderLayout.SOUTH);
-        validate();
-        int numTagDecisions = getTagConflictResolverModel().getNumDecisions();
-        int numRelationDecisions = getRelationMemberConflictResolverModel().getNumDecisions();
-        if (numTagDecisions > 0 &&  numRelationDecisions > 0) {
-            spTagConflictTypes.setDividerLocation(0.5);
-        }
-    }
-
-    protected void setCancelled(boolean cancelled) {
-        this.cancelled = cancelled;
-    }
-
-    public boolean isCancelled() {
-        return cancelled;
-    }
-
-    @Override
-    public void setVisible(boolean visible) {
-        if (visible) {
-            prepareGUIBeforeConflictResolutionStarts();
-            new WindowGeometry(
-                    getClass().getName()  + ".geometry",
-                    WindowGeometry.centerInWindow(
-                            Main.parent,
-                            new Dimension(600,400)
-                    )
-            ).applySafe(this);
-        } else {
-            new WindowGeometry(this).remember(getClass().getName() + ".geometry");
-        }
-        super.setVisible(visible);
-    }
-
-
-    class CancelAction extends AbstractAction {
-
-        public CancelAction() {
-            putValue(Action.SHORT_DESCRIPTION, tr("Cancel conflict resolution"));
-            putValue(Action.NAME, tr("Cancel"));
-            putValue(Action.SMALL_ICON, ImageProvider.get("", "cancel"));
-            setEnabled(true);
-        }
-
-        public void actionPerformed(ActionEvent arg0) {
-            setCancelled(true);
-            setVisible(false);
-        }
-    }
-
-    class ApplyAction extends AbstractAction implements PropertyChangeListener {
-
-        public ApplyAction() {
-            putValue(Action.SHORT_DESCRIPTION, tr("Apply resolved conflicts"));
-            putValue(Action.NAME, tr("Apply"));
-            putValue(Action.SMALL_ICON, ImageProvider.get("ok"));
-            updateEnabledState();
-        }
-
-        public void actionPerformed(ActionEvent arg0) {
-            setVisible(false);
-        }
-
-        protected void updateEnabledState() {
-            setEnabled(
-                    pnlTagConflictResolver.getModel().getNumConflicts() == 0
-                    && pnlRelationMemberConflictResolver.getModel().getNumConflicts() == 0
-            );
-        }
-
-        public void propertyChange(PropertyChangeEvent evt) {
-            if (evt.getPropertyName().equals(TagConflictResolverModel.NUM_CONFLICTS_PROP)) {
-                updateEnabledState();
-            }
-            if (evt.getPropertyName().equals(RelationMemberConflictResolverModel.NUM_CONFLICTS_PROP)) {
-                updateEnabledState();
-            }
-        }
-    }
-
-    class AdjustDividerLocationAction extends WindowAdapter {
-        @Override
-        public void windowOpened(WindowEvent e) {
-            int numTagDecisions = getTagConflictResolverModel().getNumDecisions();
-            int numRelationDecisions = getRelationMemberConflictResolverModel().getNumDecisions();
-            if (numTagDecisions > 0 &&  numRelationDecisions > 0) {
-                spTagConflictTypes.setDividerLocation(0.5);
-            }
-        }
-    }
-}
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/tags/RelationMemberConflictResolverModel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/tags/RelationMemberConflictResolverModel.java	(revision 2094)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/tags/RelationMemberConflictResolverModel.java	(revision 2095)
@@ -18,18 +18,40 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
-
+import org.openstreetmap.josm.data.osm.BackreferencedDataSet.RelationToChildReference;
+
+/**
+ * This model manages a list of conflicting relation members.
+ * 
+ * It can be used as {@see TableModel}.
+ *
+ *
+ */
 public class RelationMemberConflictResolverModel extends DefaultTableModel {
+    /** the property name for the number conflicts managed by this model */
     static public final String NUM_CONFLICTS_PROP = RelationMemberConflictResolverModel.class.getName() + ".numConflicts";
 
+    /** the list of conflict decisions */
     private List<RelationMemberConflictDecision> decisions;
+    /** the collection of relations for which we manage conflicts */
     private Collection<Relation> relations;
+    /** the number of conflicts */
     private int numConflicts;
     private PropertyChangeSupport support;
 
 
+    /**
+     * Replies the current number of conflicts
+     * 
+     * @return the current number of conflicts
+     */
     public int getNumConflicts() {
         return numConflicts;
     }
 
+    /**
+     * Updates the current number of conflicts from list of decisions and emits
+     * a property change event if necessary.
+     * 
+     */
     protected void updateNumConflicts() {
         int count = 0;
@@ -71,9 +93,9 @@
         RelationMemberConflictDecision d = decisions.get(row);
         switch(column) {
-            case 0: /* relation */ return d.getRelation();
-            case 1: /* pos */ return Integer.toString(d.getPos() + 1); // position in "user space" starting at 1
-            case 2: /* role */ return d.getRole();
-            case 3: /* original */ return d.getOriginalPrimitive();
-            case 4: /* decision */ return d.getDecision();
+        case 0: /* relation */ return d.getRelation();
+        case 1: /* pos */ return Integer.toString(d.getPos() + 1); // position in "user space" starting at 1
+        case 2: /* role */ return d.getRole();
+        case 3: /* original */ return d.getOriginalPrimitive();
+        case 4: /* decision */ return d.getDecision();
         }
         return null;
@@ -84,16 +106,24 @@
         RelationMemberConflictDecision d = decisions.get(row);
         switch(column) {
-            case 2: /* role */
-                d.setRole((String)value);
-                break;
-            case 4: /* decision */
-                d.decide((RelationMemberConflictDecisionType)value);
-                refresh();
-                break;
+        case 2: /* role */
+            d.setRole((String)value);
+            break;
+        case 4: /* decision */
+            d.decide((RelationMemberConflictDecisionType)value);
+            refresh();
+            break;
         }
         fireTableDataChanged();
     }
 
+    /**
+     * Populates the model with the members of the relation <code>relation</code>
+     * referring to <code>primitive</code>.
+     * 
+     * @param relation the parent relation
+     * @param primitive the child primitive
+     */
     protected void populate(Relation relation, OsmPrimitive primitive) {
+        decisions.clear();
         for (int i =0; i<relation.getMembersCount();i++) {
             if (relation.getMember(i).refersTo(primitive)) {
@@ -103,6 +133,15 @@
     }
 
+    /**
+     * Populates the model with the relation members belonging to one of the relations in <code>relations</code>
+     * and referring to one of the primitives in <code>memberPrimitives</code>.
+     * 
+     * @param relations  the parent relations. Empty list assumed if null.
+     * @param memberPrimitives the child primitives. Empty list assumed if null.
+     */
     public void populate(Collection<Relation> relations, Collection<? extends OsmPrimitive> memberPrimitives) {
         decisions.clear();
+        relations = relations == null ? new LinkedList<Relation>() : relations;
+        memberPrimitives = memberPrimitives == null ? new LinkedList<OsmPrimitive>() : memberPrimitives;
         for (Relation r : relations) {
             for (OsmPrimitive p: memberPrimitives) {
@@ -114,12 +153,49 @@
     }
 
+    /**
+     * Populates the model with the relation members represented as a collection of
+     * {@see RelationToChildReference}s.
+     * 
+     * @param references the references. Empty list assumed if null.
+     */
+    public void populate(Collection<RelationToChildReference> references) {
+        references = references == null ? new LinkedList<RelationToChildReference>() : references;
+        if (references.isEmpty()) {
+            this.relations = new HashSet<Relation>(references.size());
+            return;
+        }
+        decisions.clear();
+        this.relations = new HashSet<Relation>(references.size());
+        for (RelationToChildReference reference: references) {
+            decisions.add(new RelationMemberConflictDecision(reference.getParent(), reference.getPosition()));
+            relations.add(reference.getParent());
+        }
+        refresh();
+    }
+
+    /**
+     * Replies the decision at position <code>row</code>
+     * 
+     * @param row
+     * @return the decision at position <code>row</code>
+     */
     public RelationMemberConflictDecision getDecision(int row) {
         return decisions.get(row);
     }
 
+    /**
+     * Replies the number of decisions managed by this model
+     * 
+     * @return the number of decisions managed by this model
+     */
     public int getNumDecisions() {
         return  getRowCount();
     }
 
+    /**
+     * Refreshes the model state. Invoke this method to trigger necessary change
+     * events after an update of the model data.
+     * 
+     */
     public void refresh() {
         updateNumConflicts();
@@ -127,4 +203,9 @@
     }
 
+    /**
+     * Apply a role to all member managed by this model.
+     * 
+     * @param role the role. Empty string assumed if null.
+     */
     public void applyRole(String role) {
         role = role == null ? "" : role;
@@ -154,16 +235,16 @@
             } else {
                 switch(decision.getDecision()) {
-                    case REPLACE:
-                        rmNew = new RelationMember(decision.getRole(),newPrimitive);
-                        modifiedRelation.addMember(rmNew);
-                        isChanged |= ! rm.equals(rmNew);
-                        break;
-                    case REMOVE:
-                        isChanged = true;
-                        // do nothing
-                        break;
-                    case UNDECIDED:
-                        // FIXME: this is an error
-                        break;
+                case REPLACE:
+                    rmNew = new RelationMember(decision.getRole(),newPrimitive);
+                    modifiedRelation.addMember(rmNew);
+                    isChanged |= ! rm.equals(rmNew);
+                    break;
+                case REMOVE:
+                    isChanged = true;
+                    // do nothing
+                    break;
+                case UNDECIDED:
+                    // FIXME: this is an error
+                    break;
                 }
             }
@@ -174,4 +255,11 @@
     }
 
+    /**
+     * Builds a collection of commands executing the decisions made in this model.
+     * 
+     * @param newPrimitive the primitive which members shall refer to if the
+     * decision is {@see RelationMemberConflictDecisionType#REPLACE}
+     * @return a list of commands
+     */
     public List<Command> buildResolutionCommands(OsmPrimitive newPrimitive) {
         List<Command> command = new LinkedList<Command>();
@@ -192,12 +280,12 @@
             }
             switch(decision.getDecision()) {
-                case REMOVE: return true;
-                case REPLACE:
-                    if (!relation.getMember(i).getRole().equals(decision.getRole()))
-                        return true;
-                    if (relation.getMember(i).getMember() != newPrimitive)
-                        return true;
-                case UNDECIDED:
-                    // FIXME: handle error
+            case REMOVE: return true;
+            case REPLACE:
+                if (!relation.getMember(i).getRole().equals(decision.getRole()))
+                    return true;
+                if (relation.getMember(i).getMember() != newPrimitive)
+                    return true;
+            case UNDECIDED:
+                // FIXME: handle error
             }
         }
@@ -205,4 +293,14 @@
     }
 
+    /**
+     * Replies the set of relations which have to be modified according
+     * to the decisions managed by this model.
+     * 
+     * @param newPrimitive the primitive which members shall refer to if the
+     * decision is {@see RelationMemberConflictDecisionType#REPLACE}
+     * 
+     * @return the set of relations which have to be modified according
+     * to the decisions managed by this model
+     */
     public Set<Relation> getModifiedRelations(OsmPrimitive newPrimitive) {
         HashSet<Relation> ret = new HashSet<Relation>();
