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 21176)
+++ /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java	(revision 21177)
@@ -8,5 +8,4 @@
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
@@ -14,5 +13,4 @@
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 
 import javax.swing.JLabel;
@@ -24,6 +22,8 @@
 import org.openstreetmap.josm.command.Command;
 import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Hash;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Storage;
 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
@@ -39,4 +39,27 @@
 public class DuplicateNode extends Test{
 
+    private class NodeHash implements Hash<Object, Object> {
+
+        @SuppressWarnings("unchecked")
+        private LatLon getLatLon(Object o) {
+            if (o instanceof Node) {
+                return ((Node) o).getCoor().getRoundedToOsmPrecision();
+            } else if (o instanceof List<?>) {
+                return ((List<Node>) o).get(0).getCoor().getRoundedToOsmPrecision();
+            } else {
+                throw new AssertionError();
+            }
+        }
+
+        public boolean equals(Object k, Object t) {
+            return getLatLon(k).equals(getLatLon(t));
+        }
+
+        public int getHashCode(Object k) {
+            return getLatLon(k).hashCode();
+        }
+
+    }
+
     protected static int DUPLICATE_NODE = 1;
 
@@ -47,5 +70,5 @@
      * <pos, NodesByEqualTagsMap>
      */
-    Map<LatLon,Object> potentialDuplicates;
+    Storage<Object> potentialDuplicates;
 
     /**
@@ -55,59 +78,105 @@
     {
         super(tr("Duplicated nodes")+".",
-              tr("This test checks that there are no nodes at the very same location."));
+                tr("This test checks that there are no nodes at the very same location."));
     }
 
     @Override
     public void startTest(ProgressMonitor monitor) {
-    	super.startTest(monitor);
-        potentialDuplicates = new HashMap<LatLon, Object>();
-    }
-
-
-	@Override
-	public void endTest() {
-		for (Entry<LatLon, Object> entry: potentialDuplicates.entrySet()) {
-			Object v = entry.getValue();
-			if (v instanceof Node) {
-				// just one node at this position. Nothing to report as
-				// error
-				continue;
-			}
-
-			// multiple nodes at the same position -> report errors
-			//
-			NodesByEqualTagsMap map = (NodesByEqualTagsMap)v;
-			errors.addAll(map.buildTestErrors(this));
-		}
-		super.endTest();
-		potentialDuplicates = null;
-	}
-
-	@Override
-	public void visit(Node n) {
-		if (n.isUsable()) {
-			LatLon rounded = n.getCoor().getRoundedToOsmPrecision();
-			if (potentialDuplicates.get(rounded) == null) {
-				// in most cases there is just one node at a given position. We
-				// avoid to create an extra object and add remember the node
-				// itself at this position
-				potentialDuplicates.put(rounded, n);
-			} else if (potentialDuplicates.get(rounded) instanceof Node) {
-				// we have an additional node at the same position. Create an extra
-				// object to keep track of the nodes at this position.
-				//
-				Node n1 = (Node)potentialDuplicates.get(rounded);
-				NodesByEqualTagsMap map = new NodesByEqualTagsMap();
-				map.add(n1);
-				map.add(n);
-				potentialDuplicates.put(rounded, map);
-			} else if (potentialDuplicates.get(rounded) instanceof NodesByEqualTagsMap) {
-				// we have multiple nodes at the same position.
-				//
-				NodesByEqualTagsMap map = (NodesByEqualTagsMap)potentialDuplicates.get(rounded);
-				map.add(n);
-			}
-		}
-	}
+        super.startTest(monitor);
+        potentialDuplicates = new Storage<Object>(new NodeHash());
+    }
+
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void endTest() {
+        for (Object v: potentialDuplicates) {
+            if (v instanceof Node) {
+                // just one node at this position. Nothing to report as
+                // error
+                continue;
+            }
+
+            // multiple nodes at the same position -> report errors
+            //
+            List<Node> nodes = (List<Node>)v;
+            errors.addAll(buildTestErrors(this, nodes));
+        }
+        super.endTest();
+        potentialDuplicates = null;
+    }
+
+    public List<TestError> buildTestErrors(Test parentTest, List<Node> nodes) {
+        List<TestError> errors = new ArrayList<TestError>();
+
+        Bag<Map<String,String>, OsmPrimitive> bag = new Bag<Map<String,String>, OsmPrimitive>();
+        for (Node n: nodes) {
+            bag.add(n.getKeys(), n);
+        }
+        // check whether we have multiple nodes at the same position with
+        // the same tag set
+        //
+        for (Iterator<Map<String,String>> it = bag.keySet().iterator(); it.hasNext(); ) {
+            Map<String,String> tagSet = it.next();
+            if (bag.get(tagSet).size() > 1) {
+                errors.add(new TestError(
+                        parentTest,
+                        Severity.ERROR,
+                        tr("Duplicated nodes"),
+                        DUPLICATE_NODE,
+                        bag.get(tagSet)
+                ));
+                it.remove();
+            }
+
+        }
+
+        // check whether we have multiple nodes at the same position with
+        // differing tag sets
+        //
+        if (!bag.isEmpty()) {
+            List<OsmPrimitive> duplicates = new ArrayList<OsmPrimitive>();
+            for (List<OsmPrimitive> l: bag.values()) {
+                duplicates.addAll(l);
+            }
+            if (duplicates.size() > 1) {
+                errors.add(new TestError(
+                        parentTest,
+                        Severity.WARNING,
+                        tr("Nodes at same position"),
+                        DUPLICATE_NODE,
+                        duplicates
+                ));
+            }
+        }
+        return errors;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void visit(Node n) {
+        if (n.isUsable()) {
+            if (potentialDuplicates.get(n) == null) {
+                // in most cases there is just one node at a given position. We
+                // avoid to create an extra object and add remember the node
+                // itself at this position
+                potentialDuplicates.put(n);
+            } else if (potentialDuplicates.get(n) instanceof Node) {
+                // we have an additional node at the same position. Create an extra
+                // object to keep track of the nodes at this position.
+                //
+                Node n1 = (Node)potentialDuplicates.get(n);
+                List<Node> nodes = new ArrayList<Node>(2);
+                nodes.add(n1);
+                nodes.add(n);
+                potentialDuplicates.put(nodes);
+            } else if (potentialDuplicates.get(n) instanceof List<?>) {
+                // we have multiple nodes at the same position.
+                //
+                List<Node> nodes = (List<Node>)potentialDuplicates.get(n);
+                nodes.add(n);
+            }
+        }
+    }
 
     /**
@@ -139,8 +208,8 @@
     }
 
-	@Override
-	public boolean isFixable(TestError testError) {
-		return (testError.getTester() instanceof DuplicateNode);
-	}
+    @Override
+    public boolean isFixable(TestError testError) {
+        return (testError.getTester() instanceof DuplicateNode);
+    }
 
     /**
@@ -166,11 +235,11 @@
 
                         return ConditionalOptionPaneUtil.showConfirmationDialog(
-                            "delete_outside_nodes",
-                            Main.parent,
-                            msg,
-                            tr("Delete confirmation"),
-                            JOptionPane.YES_NO_OPTION,
-                            JOptionPane.QUESTION_MESSAGE,
-                            JOptionPane.YES_OPTION);
+                                "delete_outside_nodes",
+                                Main.parent,
+                                msg,
+                                tr("Delete confirmation"),
+                                JOptionPane.YES_NO_OPTION,
+                                JOptionPane.QUESTION_MESSAGE,
+                                JOptionPane.YES_OPTION);
                     }
                 }
@@ -179,61 +248,3 @@
         return true;
     }
-
-    static private class NodesByEqualTagsMap {
-    	/**
-    	 * a bag of primitives with the same position. The bag key is represented
-    	 * by the tag set of the primitive. This allows for easily find nodes at
-    	 * the same position with the same tag sets later.
-    	 */
-    	private Bag<Map<String,String>, OsmPrimitive> bag;
-
-    	public NodesByEqualTagsMap() {
-    		bag = new Bag<Map<String,String>, OsmPrimitive>();
-    	}
-
-    	public void add(Node n) {
-    		bag.add(n.getKeys(), n);
-    	}
-
-    	public List<TestError> buildTestErrors(Test parentTest) {
-    		List<TestError> errors = new ArrayList<TestError>();
-    		// check whether we have multiple nodes at the same position with
-    		// the same tag set
-    		//
-    		for (Iterator<Map<String,String>> it = bag.keySet().iterator(); it.hasNext(); ) {
-    			Map<String,String> tagSet = it.next();
-    			if (bag.get(tagSet).size() > 1) {
-    				errors.add(new TestError(
-    						parentTest,
-    						Severity.ERROR,
-    						tr("Duplicated nodes"),
-    						DUPLICATE_NODE,
-    						bag.get(tagSet)
-    				));
-    				it.remove();
-    			}
-
-    		}
-
-    		// check whether we have multiple nodes at the same position with
-    		// differing tag sets
-    		//
-    		if (!bag.isEmpty()) {
-    			List<OsmPrimitive> duplicates = new ArrayList<OsmPrimitive>();
-    			for (List<OsmPrimitive> l: bag.values()) {
-    				duplicates.addAll(l);
-    			}
-    			if (duplicates.size() > 1) {
-	    			errors.add(new TestError(
-	    					parentTest,
-	    					Severity.WARNING,
-							tr("Nodes at same position"),
-							DUPLICATE_NODE,
-							duplicates
-					));
-    			}
-    		}
-    		return errors;
-    	}
-    }
 }
