Index: /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java
===================================================================
--- /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java	(revision 2787)
+++ /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java	(revision 2788)
@@ -3,11 +3,12 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.util.List;
+import java.util.*;
 
-import org.openstreetmap.josm.command.Command;
-import org.openstreetmap.josm.command.DeleteCommand;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.*;
 import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.*;
 import org.openstreetmap.josm.plugins.validator.Severity;
 import org.openstreetmap.josm.plugins.validator.Test;
@@ -60,9 +61,115 @@
 	}
 	
+    /**
+     * Merge the nodes into one.
+     * Copied from UtilsPlugin.MergePointsAction
+     */
 	@Override
 	public Command fixError(TestError testError)
 	{
-		//TODO Which should be the fix? 
-		return new DeleteCommand(testError.getPrimitives());
+        Collection<OsmPrimitive> sel = testError.getPrimitives();
+        Collection<OsmPrimitive> nodes = new ArrayList<OsmPrimitive>();
+        
+        Node target = null;
+        for (OsmPrimitive osm : sel)
+            nodes.add(osm);
+        
+        if( nodes.size() < 2 )
+            return null;
+        
+        for ( OsmPrimitive o : nodes )
+        {
+            Node n = (Node)o;
+            if( target == null || target.id == 0 )
+            {
+                target = n;
+                continue;
+            }
+            if( n.id == 0 )
+                continue;
+            if( n.id < target.id )
+                target = n;
+        }
+        if( target == null )
+            return null;
+        
+        // target is what we're merging into
+        // nodes is the list of nodes to be removed
+        nodes.remove(target);
+        
+        // Merge all properties
+        Node newtarget = new Node(target);
+        for (final OsmPrimitive o : nodes) 
+        {
+            Node n = (Node)o;
+            for ( String key : n.keySet() )
+            {
+                if( newtarget.keySet().contains(key) && !newtarget.get(key).equals(n.get(key)) )
+                {
+                    JOptionPane.showMessageDialog(Main.parent, tr("Nodes have conflicting key: " + key + " ["+newtarget.get(key)+", "+n.get(key)+"]"));
+                    return null;
+                }
+                newtarget.put( key, n.get(key) ); 
+            }
+        }        
+        
+        // Since some segment may disappear, we need to track those too
+        Collection<OsmPrimitive> seglist = new ArrayList<OsmPrimitive>();
+                
+        // Now do the merging
+        Collection<Command> cmds = new LinkedList<Command>();
+        for (final Segment s : Main.ds.segments) 
+        {
+            if( s.deleted || s.incomplete )
+                continue;
+            if( !nodes.contains( s.from ) && !nodes.contains( s.to ) )
+                continue;
+                
+            Segment newseg = new Segment(s);
+            if( nodes.contains( s.from ) )
+                newseg.from = target;
+            if( nodes.contains( s.to ) )
+                newseg.to = target;
+
+            // Is this node now a NULL node?
+            if( newseg.from == newseg.to )
+                seglist.add(s);
+            else
+                cmds.add(new ChangeCommand(s,newseg));
+        }
+        
+        if( seglist.size() > 0 )  // Some segments to be deleted?
+        {
+            // We really want to delete this, but we must check if it is part of a way first
+            for (final Way w : Main.ds.ways)
+            {
+                Way newway = null;
+                if( w.deleted )
+                    continue;
+                for (final OsmPrimitive o : seglist )
+                {
+                    Segment s = (Segment)o;
+                    if( w.segments.contains(s) )
+                    {
+                        if( newway == null )
+                            newway = new Way(w);
+                        newway.segments.remove(s);
+                    }
+                }
+                if( newway != null )   // Made changes?
+                {
+                    // If no segments left, delete the way
+                    if( newway.segments.size() == 0 )
+                        cmds.add(new DeleteCommand(Arrays.asList(new OsmPrimitive[]{w})));
+                    else
+                        cmds.add(new ChangeCommand(w,newway));
+                }
+            }
+            cmds.add(new DeleteCommand(seglist));
+        }
+
+        cmds.add(new DeleteCommand(nodes));
+        cmds.add(new ChangeCommand(target, newtarget));
+        return new SequenceCommand(tr("Merge Nodes"), cmds);
 	}
 	
@@ -70,5 +177,5 @@
 	public boolean isFixable(TestError testError)
 	{
-		return false; //(testError.getTester() instanceof DuplicateNode);
+		return (testError.getTester() instanceof DuplicateNode);
 	}	
 }
