Index: /trunk/src/org/openstreetmap/josm/data/validation/TestError.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/TestError.java	(revision 3673)
+++ /trunk/src/org/openstreetmap/josm/data/validation/TestError.java	(revision 3674)
@@ -34,7 +34,7 @@
     private String description_en;
     /** The affected primitives */
-    private List<? extends OsmPrimitive> primitives;
+    private Collection<? extends OsmPrimitive> primitives;
     /** The primitives to be highlighted */
-    private List<?> highlighted;
+    private Collection<?> highlighted;
     /** The tester that raised this error */
     private Test tester;
@@ -54,5 +54,5 @@
      */
     public TestError(Test tester, Severity severity, String message, String description, String description_en,
-            int code, List<? extends OsmPrimitive> primitives, List<?> highlighted) {
+            int code, Collection<? extends OsmPrimitive> primitives, Collection<?> highlighted) {
         this.tester = tester;
         this.severity = severity;
@@ -65,15 +65,15 @@
     }
 
-    public TestError(Test tester, Severity severity, String message, int code, List<? extends OsmPrimitive> primitives,
-            List<?> highlighted) {
+    public TestError(Test tester, Severity severity, String message, int code, Collection<? extends OsmPrimitive> primitives,
+            Collection<?> highlighted) {
         this(tester, severity, message, null, null, code, primitives, highlighted);
     }
 
     public TestError(Test tester, Severity severity, String message, String description, String description_en,
-            int code, List<? extends OsmPrimitive> primitives) {
+            int code, Collection<? extends OsmPrimitive> primitives) {
         this(tester, severity, message, description, description_en, code, primitives, primitives);
     }
 
-    public TestError(Test tester, Severity severity, String message, int code, List<? extends OsmPrimitive> primitives) {
+    public TestError(Test tester, Severity severity, String message, int code, Collection<? extends OsmPrimitive> primitives) {
         this(tester, severity, message, null, null, code, primitives, primitives);
     }
@@ -117,5 +117,5 @@
      * @return the list of primitives affected by this error
      */
-    public List<? extends OsmPrimitive> getPrimitives() {
+    public Collection<? extends OsmPrimitive> getPrimitives() {
         return primitives;
     }
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/DuplicateNode.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/DuplicateNode.java	(revision 3673)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/DuplicateNode.java	(revision 3674)
@@ -15,4 +15,5 @@
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import javax.swing.JLabel;
@@ -33,7 +34,7 @@
 import org.openstreetmap.josm.data.validation.Test;
 import org.openstreetmap.josm.data.validation.TestError;
-import org.openstreetmap.josm.data.validation.util.Bag;
 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.tools.MultiMap;
 
 /**
@@ -138,7 +139,7 @@
         List<TestError> errors = new ArrayList<TestError>();
 
-        Bag<Map<String,String>, OsmPrimitive> bag = new Bag<Map<String,String>, OsmPrimitive>();
+        MultiMap<Map<String,String>, OsmPrimitive> mm = new MultiMap<Map<String,String>, OsmPrimitive>();
         for (Node n: nodes) {
-            bag.add(n.getKeys(), n);
+            mm.put(n.getKeys(), n);
         }
 
@@ -150,7 +151,7 @@
         // the same tag set
         //
-        for (Iterator<Map<String,String>> it = bag.keySet().iterator(); it.hasNext();) {
+        for (Iterator<Map<String,String>> it = mm.keySet().iterator(); it.hasNext();) {
             Map<String,String> tagSet = it.next();
-            if (bag.get(tagSet).size() > 1) {
+            if (mm.get(tagSet).size() > 1) {
 
                 for (String type: types) {
@@ -158,5 +159,5 @@
                 }
 
-                for (OsmPrimitive p : bag.get(tagSet)) {
+                for (OsmPrimitive p : mm.get(tagSet)) {
                     if (p.getType()==OsmPrimitiveType.NODE) {
                         Node n = (Node) p;
@@ -198,5 +199,5 @@
                             msg,
                             DUPLICATE_NODE_MIXED,
-                            bag.get(tagSet)
+                            mm.get(tagSet)
                     ));
                 } else if (typeMap.get("highway")) {
@@ -209,5 +210,5 @@
                             msg,
                             DUPLICATE_NODE_HIGHWAY,
-                            bag.get(tagSet)
+                            mm.get(tagSet)
                     ));
                 } else if (typeMap.get("railway")) {
@@ -220,5 +221,5 @@
                             msg,
                             DUPLICATE_NODE_RAILWAY,
-                            bag.get(tagSet)
+                            mm.get(tagSet)
                     ));
                 } else if (typeMap.get("waterway")) {
@@ -231,5 +232,5 @@
                             msg,
                             DUPLICATE_NODE_WATERWAY,
-                            bag.get(tagSet)
+                            mm.get(tagSet)
                     ));
                 } else if (typeMap.get("boundary")) {
@@ -242,5 +243,5 @@
                             msg,
                             DUPLICATE_NODE_BOUNDARY,
-                            bag.get(tagSet)
+                            mm.get(tagSet)
                     ));
                 } else if (typeMap.get("power")) {
@@ -253,5 +254,5 @@
                             msg,
                             DUPLICATE_NODE_POWER,
-                            bag.get(tagSet)
+                            mm.get(tagSet)
                     ));
                 } else if (typeMap.get("natural")) {
@@ -264,5 +265,5 @@
                             msg,
                             DUPLICATE_NODE_NATURAL,
-                            bag.get(tagSet)
+                            mm.get(tagSet)
                     ));
                 } else if (typeMap.get("building")) {
@@ -275,5 +276,5 @@
                             msg,
                             DUPLICATE_NODE_BUILDING,
-                            bag.get(tagSet)
+                            mm.get(tagSet)
                     ));
                 } else if (typeMap.get("landuse")) {
@@ -286,5 +287,5 @@
                             msg,
                             DUPLICATE_NODE_LANDUSE,
-                            bag.get(tagSet)
+                            mm.get(tagSet)
                     ));
                 } else {
@@ -297,5 +298,5 @@
                             msg,
                             DUPLICATE_NODE_OTHER,
-                            bag.get(tagSet)
+                            mm.get(tagSet)
                     ));
 
@@ -308,7 +309,7 @@
         // differing tag sets
         //
-        if (!bag.isEmpty()) {
+        if (!mm.isEmpty()) {
             List<OsmPrimitive> duplicates = new ArrayList<OsmPrimitive>();
-            for (List<OsmPrimitive> l: bag.values()) {
+            for (Set<OsmPrimitive> l: mm.values()) {
                 duplicates.addAll(l);
             }
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/DuplicateWay.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/DuplicateWay.java	(revision 3673)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/DuplicateWay.java	(revision 3674)
@@ -4,4 +4,5 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
@@ -9,5 +10,5 @@
 import java.util.List;
 import java.util.Map;
-import java.util.Vector;
+import java.util.Set;
 
 import org.openstreetmap.josm.command.ChangeCommand;
@@ -24,6 +25,6 @@
 import org.openstreetmap.josm.data.validation.Test;
 import org.openstreetmap.josm.data.validation.TestError;
-import org.openstreetmap.josm.data.validation.util.Bag;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.tools.MultiMap;
 
 /**
@@ -36,5 +37,5 @@
         public List<LatLon> coor;
         public Map<String, String> keys;
-        public WayPair(List<LatLon> _coor,Map<String, String> _keys) {
+        public WayPair(List<LatLon> _coor, Map<String, String> _keys) {
             coor=_coor;
             keys=_keys;
@@ -43,5 +44,5 @@
         @Override
         public int hashCode() {
-            return coor.hashCode()+keys.hashCode();
+            return coor.hashCode() + keys.hashCode();
         }
 
@@ -58,5 +59,5 @@
 
     /** Bag of all ways */
-    Bag<WayPair, OsmPrimitive> ways;
+    MultiMap<WayPair, OsmPrimitive> ways;
 
     /**
@@ -72,5 +73,5 @@
     public void startTest(ProgressMonitor monitor) {
         super.startTest(monitor);
-        ways = new Bag<WayPair, OsmPrimitive>(1000);
+        ways = new MultiMap<WayPair, OsmPrimitive>(1000);
     }
 
@@ -78,5 +79,5 @@
     public void endTest() {
         super.endTest();
-        for (List<OsmPrimitive> duplicated : ways.values()) {
+        for (Set<OsmPrimitive> duplicated : ways.values()) {
             if (duplicated.size() > 1) {
                 TestError testError = new TestError(this, Severity.ERROR, tr("Duplicated ways"), DUPLICATE_WAY, duplicated);
@@ -91,13 +92,13 @@
         if (!w.isUsable())
             return;
-        List<Node> wNodes=w.getNodes();
-        Vector<LatLon> wLat=new Vector<LatLon>(wNodes.size());
+        List<Node> wNodes = w.getNodes();
+        List<LatLon> wLat = new ArrayList<LatLon>(wNodes.size());
         for (int i=0;i<wNodes.size();i++) {
              wLat.add(wNodes.get(i).getCoor());
         }
-        Map<String, String> wkeys=w.getKeys();
+        Map<String, String> wkeys = w.getKeys();
         wkeys.remove("created_by");
-        WayPair wKey=new WayPair(wLat,wkeys);
-        ways.add(wKey, w);
+        WayPair wKey = new WayPair(wLat, wkeys);
+        ways.put(wKey, w);
     }
 
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/OverlappingWays.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/OverlappingWays.java	(revision 3673)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/OverlappingWays.java	(revision 3674)
@@ -5,5 +5,7 @@
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -17,6 +19,6 @@
 import org.openstreetmap.josm.data.validation.Test;
 import org.openstreetmap.josm.data.validation.TestError;
-import org.openstreetmap.josm.data.validation.util.Bag;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.tools.MultiMap;
 import org.openstreetmap.josm.tools.Pair;
 
@@ -29,5 +31,5 @@
     
     /** Bag of all way segments */
-    Bag<Pair<Node,Node>, WaySegment> nodePairs;
+    MultiMap<Pair<Node,Node>, WaySegment> nodePairs;
 
     protected static int OVERLAPPING_HIGHWAY = 101;
@@ -49,12 +51,12 @@
     public void startTest(ProgressMonitor monitor)  {
         super.startTest(monitor);
-        nodePairs = new Bag<Pair<Node,Node>, WaySegment>(1000);
+        nodePairs = new MultiMap<Pair<Node,Node>, WaySegment>(1000);
     }
 
     @Override
     public void endTest() {
-        Map<List<Way>, List<WaySegment>> ways_seen = new HashMap<List<Way>, List<WaySegment>>(500);
+        Map<List<Way>, LinkedHashSet<WaySegment>> ways_seen = new HashMap<List<Way>, LinkedHashSet<WaySegment>>(500);
 
-        for (List<WaySegment> duplicated : nodePairs.values()) {
+        for (LinkedHashSet<WaySegment> duplicated : nodePairs.values()) {
             int ways = duplicated.size();
 
@@ -62,5 +64,5 @@
                 List<OsmPrimitive> prims = new ArrayList<OsmPrimitive>();
                 List<Way> current_ways = new ArrayList<Way>();
-                List<WaySegment> highlight;
+                Collection<WaySegment> highlight;
                 int highway = 0;
                 int railway = 0;
@@ -148,5 +150,5 @@
                 continue;
             }
-            nodePairs.add(Pair.sort(new Pair<Node,Node>(lastN, n)),
+            nodePairs.put(Pair.sort(new Pair<Node,Node>(lastN, n)),
                 new WaySegment(w, i));
             lastN = n;
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/SimilarNamedWays.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/SimilarNamedWays.java	(revision 3673)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/SimilarNamedWays.java	(revision 3674)
@@ -15,7 +15,8 @@
 import org.openstreetmap.josm.data.validation.Test;
 import org.openstreetmap.josm.data.validation.TestError;
-import org.openstreetmap.josm.data.validation.util.Bag;
 import org.openstreetmap.josm.data.validation.util.ValUtil;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.tools.MultiMap;
+import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -32,5 +33,5 @@
     Map<Point2D,List<Way>> cellWays;
     /** The already detected errors */
-    Bag<Way, Way> errorWays;
+    MultiMap<Way, Way> errorWays;
 
     /**
@@ -46,5 +47,5 @@
         super.startTest(monitor);
         cellWays = new HashMap<Point2D,List<Way>>(1000);
-        errorWays = new Bag<Way, Way>();
+        errorWays = new MultiMap<Way, Way>();
     }
 
@@ -83,5 +84,5 @@
                     primitives.add(w2);
                     errors.add(new TestError(this, Severity.WARNING, tr("Similarly named ways"), SIMILAR_NAMED, primitives));
-                    errorWays.add(w, w2);
+                    errorWays.put(w, w2);
                 }
             }
@@ -142,5 +143,5 @@
 
                 // Step 6
-                d[i][j] = min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
+                d[i][j] = Utils.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
             }
         }
@@ -149,20 +150,3 @@
         return d[n][m];
     }
-
-    /** // FIXME: move to utils
-     * Get minimum of three values
-     * @param a First value
-     * @param b Second value
-     * @param c Third value
-     * @return The minimum of the three values
-     */
-    private static int min(int a, int b, int c) {
-        int mi = a;
-        if (b < mi) {
-            mi = b;
-        } if (c < mi) {
-            mi = c;
-        }
-        return mi;
-    }
 }
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/TagChecker.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/TagChecker.java	(revision 3673)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/TagChecker.java	(revision 3674)
@@ -24,4 +24,5 @@
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -50,5 +51,4 @@
 import org.openstreetmap.josm.data.validation.Test;
 import org.openstreetmap.josm.data.validation.TestError;
-import org.openstreetmap.josm.data.validation.util.Bag;
 import org.openstreetmap.josm.data.validation.util.Entities;
 import org.openstreetmap.josm.data.validation.util.ValUtil;
@@ -59,4 +59,5 @@
 import org.openstreetmap.josm.io.MirroredInputStream;
 import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.MultiMap;
 
 /**
@@ -75,5 +76,5 @@
     protected static Map<String, String> spellCheckKeyData;
     /** The spell check preset values */
-    protected static Bag<String, String> presetsValueData;
+    protected static MultiMap<String, String> presetsValueData;
     /** The TagChecker data */
     protected static List<CheckerData> checkerData = new ArrayList<CheckerData>();
@@ -294,7 +295,7 @@
         Collection<TaggingPreset> presets = TaggingPresetPreference.taggingPresets;
         if (presets != null) {
-            presetsValueData = new Bag<String, String>();
+            presetsValueData = new MultiMap<String, String>();
             for (String a : OsmPrimitive.getUninterestingKeys()) {
-                presetsValueData.add(a);
+                presetsValueData.putVoid(a);
             }
             // TODO directionKeys are no longer in OsmPrimitive (search pattern is used instead)
@@ -304,5 +305,5 @@
             for (String a : Main.pref.getCollection(ValidatorPreference.PREFIX + ".knownkeys",
                     Arrays.asList(new String[]{"is_in", "int_ref", "fixme", "population"}))) {
-                presetsValueData.add(a);
+                presetsValueData.putVoid(a);
             }
             for (TaggingPreset p : presets) {
@@ -312,17 +313,17 @@
                         if (combo.values != null) {
                             for(String value : combo.values.split(",")) {
-                                presetsValueData.add(combo.key, value);
+                                presetsValueData.put(combo.key, value);
                             }
                         }
                     } else if (i instanceof TaggingPreset.Key) {
                         TaggingPreset.Key k = (TaggingPreset.Key) i;
-                        presetsValueData.add(k.key, k.value);
+                        presetsValueData.put(k.key, k.value);
                     } else if (i instanceof TaggingPreset.Text) {
                         TaggingPreset.Text k = (TaggingPreset.Text) i;
-                        presetsValueData.add(k.key);
+                        presetsValueData.putVoid(k.key);
                     } else if (i instanceof TaggingPreset.Check) {
                         TaggingPreset.Check k = (TaggingPreset.Check) i;
-                        presetsValueData.add(k.key, "yes");
-                        presetsValueData.add(k.key, "no");
+                        presetsValueData.put(k.key, "yes");
+                        presetsValueData.put(k.key, "no");
                     }
                 }
@@ -366,5 +367,5 @@
     private void checkPrimitive(OsmPrimitive p) {
         // Just a collection to know if a primitive has been already marked with error
-        Bag<OsmPrimitive, String> withErrors = new Bag<OsmPrimitive, String>();
+        MultiMap<OsmPrimitive, String> withErrors = new MultiMap<OsmPrimitive, String>();
 
         if (checkComplex) {
@@ -400,5 +401,5 @@
                     errors.add( new TestError(this, Severity.ERROR, tr("Illegal tag/value combinations"),
                             tr("Illegal tag/value combinations"), tr("Illegal tag/value combinations"), 1272, p) );
-                    withErrors.add(p, "TC");
+                    withErrors.put(p, "TC");
                 }
             }
@@ -409,5 +410,5 @@
                     errors.add( new TestError(this, d.getSeverity(), tr("Illegal tag/value combinations"),
                             d.getDescription(), d.getDescriptionOrig(), d.getCode(), p) );
-                    withErrors.add(p, "TC");
+                    withErrors.put(p, "TC");
                 }
             }
@@ -422,48 +423,48 @@
                 errors.add( new TestError(this, Severity.WARNING, tr("Tag value contains character with code less than 0x20"),
                         tr(s, key), MessageFormat.format(s, key), LOW_CHAR_VALUE, p) );
-                withErrors.add(p, "ICV");
+                withErrors.put(p, "ICV");
             }
             if (checkKeys && (containsLow(key)) && !withErrors.contains(p, "ICK")) {
                 errors.add( new TestError(this, Severity.WARNING, tr("Tag key contains character with code less than 0x20"),
                         tr(s, key), MessageFormat.format(s, key), LOW_CHAR_KEY, p) );
-                withErrors.add(p, "ICK");
+                withErrors.put(p, "ICK");
             }
             if (checkValues && (value!=null && value.length() > 255) && !withErrors.contains(p, "LV")) {
                 errors.add( new TestError(this, Severity.ERROR, tr("Tag value longer than allowed"),
                         tr(s, key), MessageFormat.format(s, key), LONG_VALUE, p) );
-                withErrors.add(p, "LV");
+                withErrors.put(p, "LV");
             }
             if (checkKeys && (key!=null && key.length() > 255) && !withErrors.contains(p, "LK")) {
                 errors.add( new TestError(this, Severity.ERROR, tr("Tag key longer than allowed"),
                         tr(s, key), MessageFormat.format(s, key), LONG_KEY, p) );
-                withErrors.add(p, "LK");
+                withErrors.put(p, "LK");
             }
             if (checkValues && (value==null || value.trim().length() == 0) && !withErrors.contains(p, "EV")) {
                 errors.add( new TestError(this, Severity.WARNING, tr("Tags with empty values"),
                         tr(s, key), MessageFormat.format(s, key), EMPTY_VALUES, p) );
-                withErrors.add(p, "EV");
+                withErrors.put(p, "EV");
             }
             if (checkKeys && spellCheckKeyData.containsKey(key) && !withErrors.contains(p, "IPK")) {
                 errors.add( new TestError(this, Severity.WARNING, tr("Invalid property key"),
                         tr(s, key), MessageFormat.format(s, key), INVALID_KEY, p) );
-                withErrors.add(p, "IPK");
+                withErrors.put(p, "IPK");
             }
             if (checkKeys && key.indexOf(" ") >= 0 && !withErrors.contains(p, "IPK")) {
                 errors.add( new TestError(this, Severity.WARNING, tr("Invalid white space in property key"),
                         tr(s, key), MessageFormat.format(s, key), INVALID_KEY_SPACE, p) );
-                withErrors.add(p, "IPK");
+                withErrors.put(p, "IPK");
             }
             if (checkValues && value != null && (value.startsWith(" ") || value.endsWith(" ")) && !withErrors.contains(p, "SPACE")) {
                 errors.add( new TestError(this, Severity.OTHER, tr("Property values start or end with white space"),
                         tr(s, key), MessageFormat.format(s, key), INVALID_SPACE, p) );
-                withErrors.add(p, "SPACE");
+                withErrors.put(p, "SPACE");
             }
             if (checkValues && value != null && !value.equals(entities.unescape(value)) && !withErrors.contains(p, "HTML")) {
                 errors.add( new TestError(this, Severity.OTHER, tr("Property values contain HTML entity"),
                         tr(s, key), MessageFormat.format(s, key), INVALID_HTML, p) );
-                withErrors.add(p, "HTML");
+                withErrors.put(p, "HTML");
             }
             if (checkValues && value != null && value.length() > 0 && presetsValueData != null) {
-                List<String> values = presetsValueData.get(key);
+                Set<String> values = presetsValueData.get(key);
                 if (values == null) {
                     boolean ignore = false;
@@ -487,5 +488,5 @@
                         errors.add( new TestError(this, Severity.OTHER, tr("Presets do not contain property key"),
                                 tr(i, key), MessageFormat.format(i, key), INVALID_VALUE, p) );
-                        withErrors.add(p, "UPK");
+                        withErrors.put(p, "UPK");
                     }
                 } else if (values.size() > 0 && !values.contains(prop.getValue())) {
@@ -507,5 +508,5 @@
                         errors.add( new TestError(this, Severity.OTHER, tr("Presets do not contain property value"),
                                 tr(i, prop.getValue(), key), MessageFormat.format(i, prop.getValue(), key), INVALID_VALUE, p) );
-                        withErrors.add(p, "UPV");
+                        withErrors.put(p, "UPV");
                     }
                 }
@@ -518,5 +519,5 @@
                     errors.add(new TestError(this, Severity.OTHER,
                             tr("FIXMES"), FIXME, p));
-                    withErrors.add(p, "FIXME");
+                    withErrors.put(p, "FIXME");
                 }
             }
@@ -755,8 +756,6 @@
         List<Command> commands = new ArrayList<Command>(50);
 
-        int i = -1;
-        List<? extends OsmPrimitive> primitives = testError.getPrimitives();
+        Collection<? extends OsmPrimitive> primitives = testError.getPrimitives();
         for (OsmPrimitive p : primitives) {
-            i++;
             Map<String, String> tags = p.getKeys();
             if (tags == null || tags.isEmpty()) {
@@ -768,17 +767,17 @@
                 String value = prop.getValue();
                 if (value == null || value.trim().length() == 0) {
-                    commands.add(new ChangePropertyCommand(Collections.singleton(primitives.get(i)), key, null));
+                    commands.add(new ChangePropertyCommand(Collections.singleton(p), key, null));
                 } else if (value.startsWith(" ") || value.endsWith(" ")) {
-                    commands.add(new ChangePropertyCommand(Collections.singleton(primitives.get(i)), key, value.trim()));
+                    commands.add(new ChangePropertyCommand(Collections.singleton(p), key, value.trim()));
                 } else if (key.startsWith(" ") || key.endsWith(" ")) {
-                    commands.add(new ChangePropertyKeyCommand(Collections.singleton(primitives.get(i)), key, key.trim()));
+                    commands.add(new ChangePropertyKeyCommand(Collections.singleton(p), key, key.trim()));
                 } else {
                     String evalue = entities.unescape(value);
                     if (!evalue.equals(value)) {
-                        commands.add(new ChangePropertyCommand(Collections.singleton(primitives.get(i)), key, evalue));
+                        commands.add(new ChangePropertyCommand(Collections.singleton(p), key, evalue));
                     } else {
                         String replacementKey = spellCheckKeyData.get(key);
                         if (replacementKey != null) {
-                            commands.add(new ChangePropertyKeyCommand(Collections.singleton(primitives.get(i)),
+                            commands.add(new ChangePropertyKeyCommand(Collections.singleton(p),
                                     key, replacementKey));
                         }
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/UnclosedWays.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/UnclosedWays.java	(revision 3673)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/UnclosedWays.java	(revision 3674)
@@ -17,5 +17,4 @@
 import org.openstreetmap.josm.data.validation.Test;
 import org.openstreetmap.josm.data.validation.TestError;
-import org.openstreetmap.josm.data.validation.util.Bag;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 
@@ -26,6 +25,4 @@
  */
 public class UnclosedWays extends Test {
-    /** The already detected errors */
-    protected Bag<Way, Way> errorWays;
 
     /**
@@ -39,10 +36,8 @@
     public void startTest(ProgressMonitor monitor) {
         super.startTest(monitor);
-        errorWays = new Bag<Way, Way>();
     }
 
     @Override
     public void endTest() {
-        errorWays = null;
         super.endTest();
     }
@@ -133,5 +128,4 @@
             errors.add(new TestError(this, Severity.WARNING, tr("Unclosed way"),
                             type, etype, mode, primitives, highlight));
-            errorWays.add(w, w);
         }
     }
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/WronglyOrderedWays.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/WronglyOrderedWays.java	(revision 3673)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/WronglyOrderedWays.java	(revision 3674)
@@ -12,5 +12,4 @@
 import org.openstreetmap.josm.data.validation.Test;
 import org.openstreetmap.josm.data.validation.TestError;
-import org.openstreetmap.josm.data.validation.util.Bag;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 
@@ -26,7 +25,4 @@
     protected static int WRONGLY_ORDERED_LAND  = 1003;
 
-    /** The already detected errors */
-    protected Bag<Way, Way> errorWays;
-
     /**
      * Constructor
@@ -40,10 +36,8 @@
     public void startTest(ProgressMonitor monitor) {
         super.startTest(monitor);
-        errorWays = new Bag<Way, Way>();
     }
 
     @Override
     public void endTest() {
-        errorWays = null;
         super.endTest();
     }
@@ -96,5 +90,4 @@
                 primitives.add(w);
                 errors.add( new TestError(this, Severity.OTHER, errortype, type, primitives) );
-                errorWays.add(w,w);
             }
         }
Index: unk/src/org/openstreetmap/josm/data/validation/util/Bag.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/util/Bag.java	(revision 3673)
+++ 	(revision )
@@ -1,94 +1,0 @@
-// License: GPL. See LICENSE file for details.
-package org.openstreetmap.josm.data.validation.util;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- *
- * A very simple bag to store multiple occurences of a same key.
- * <p>
- * The bag will keep, for each key, a list of values.
- *
- * @author frsantos
- *
- * @param <K> The key class
- * @param <V> The value class
- */
-public class Bag<K,V> extends HashMap<K, List<V>>
-{
-    /** Serializable ID */
-    private static final long serialVersionUID = 5374049172859211610L;
-
-    /**
-     * Returns the list of elements with the same key
-     * @param key The key to obtain the elements
-     * @return the list of elements with the same key
-     */
-    public List<V> get(K key)
-    {
-        return super.get(key);
-    }
-
-    /**
-     * Adds an element to the bag
-     * @param key The key of the element
-     * @param value The element to add
-     */
-    public void add(K key, V value)
-    {
-        List<V> values = get(key);
-        if( values == null )
-        {
-            values = new ArrayList<V>();
-            put(key, values);
-        }
-        values.add(value);
-    }
-
-    /**
-     * Adds an element to the bag
-     * @param key The key of the element
-     * @param value The element to add
-     */
-    public void add(K key)
-    {
-        List<V> values = get(key);
-        if( values == null )
-        {
-            values = new ArrayList<V>();
-            put(key, values);
-        }
-    }
-
-    /**
-     * Constructor
-     */
-    public Bag()
-    {
-        super();
-    }
-
-    /**
-     * Constructor
-     *
-     * @param initialCapacity The initial capacity
-     */
-    public Bag(int initialCapacity)
-    {
-        super(initialCapacity);
-    }
-
-    /**
-     * Returns true if the bag contains a value for a key
-     * @param key The key
-     * @param value The value
-     * @return true if the key contains the value
-     */
-    public boolean contains(K key, V value)
-    {
-        List<V> values = get(key);
-        return (values == null) ? false : values.contains(value);
-    }
-}
Index: /trunk/src/org/openstreetmap/josm/gui/dialogs/validator/ValidatorTreePanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/dialogs/validator/ValidatorTreePanel.java	(revision 3673)
+++ /trunk/src/org/openstreetmap/josm/gui/dialogs/validator/ValidatorTreePanel.java	(revision 3674)
@@ -8,4 +8,5 @@
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -23,6 +24,6 @@
 import org.openstreetmap.josm.data.validation.Severity;
 import org.openstreetmap.josm.data.validation.TestError;
-import org.openstreetmap.josm.data.validation.util.Bag;
 import org.openstreetmap.josm.data.validation.util.MultipleNameVisitor;
+import org.openstreetmap.josm.tools.MultiMap;
 
 /**
@@ -138,9 +139,9 @@
         }
 
-        Map<Severity, Bag<String, TestError>> errorTree = new HashMap<Severity, Bag<String, TestError>>();
-        Map<Severity, HashMap<String, Bag<String, TestError>>> errorTreeDeep = new HashMap<Severity, HashMap<String, Bag<String, TestError>>>();
+        Map<Severity, MultiMap<String, TestError>> errorTree = new HashMap<Severity, MultiMap<String, TestError>>();
+        Map<Severity, HashMap<String, MultiMap<String, TestError>>> errorTreeDeep = new HashMap<Severity, HashMap<String, MultiMap<String, TestError>>>();
         for (Severity s : Severity.values()) {
-            errorTree.put(s, new Bag<String, TestError>(20));
-            errorTreeDeep.put(s, new HashMap<String, Bag<String, TestError>>());
+            errorTree.put(s, new MultiMap<String, TestError>(20));
+            errorTreeDeep.put(s, new HashMap<String, MultiMap<String, TestError>>());
         }
 
@@ -165,12 +166,12 @@
             }
             if (d != null) {
-                Bag<String, TestError> b = errorTreeDeep.get(s).get(m);
+                MultiMap<String, TestError> b = errorTreeDeep.get(s).get(m);
                 if (b == null) {
-                    b = new Bag<String, TestError>(20);
+                    b = new MultiMap<String, TestError>(20);
                     errorTreeDeep.get(s).put(m, b);
                 }
-                b.add(d, e);
+                b.put(d, e);
             } else {
-                errorTree.get(s).add(m, e);
+                errorTree.get(s).put(m, e);
             }
         }
@@ -178,6 +179,6 @@
         List<TreePath> expandedPaths = new ArrayList<TreePath>();
         for (Severity s : Severity.values()) {
-            Bag<String, TestError> severityErrors = errorTree.get(s);
-            Map<String, Bag<String, TestError>> severityErrorsDeep = errorTreeDeep.get(s);
+            MultiMap<String, TestError> severityErrors = errorTree.get(s);
+            Map<String, MultiMap<String, TestError>> severityErrorsDeep = errorTreeDeep.get(s);
             if (severityErrors.isEmpty() && severityErrorsDeep.isEmpty()) {
                 continue;
@@ -192,7 +193,7 @@
             }
 
-            for (Entry<String, List<TestError>> msgErrors : severityErrors.entrySet()) {
+            for (Entry<String, LinkedHashSet<TestError>> msgErrors : severityErrors.entrySet()) {
                 // Message node
-                List<TestError> errs = msgErrors.getValue();
+                Set<TestError> errs = msgErrors.getValue();
                 String msg = msgErrors.getKey() + " (" + errs.size() + ")";
                 DefaultMutableTreeNode messageNode = new DefaultMutableTreeNode(msg);
@@ -209,7 +210,7 @@
                 }
             }
-            for (Entry<String, Bag<String, TestError>> bag : severityErrorsDeep.entrySet()) {
+            for (Entry<String, MultiMap<String, TestError>> bag : severityErrorsDeep.entrySet()) {
                 // Group node
-                Bag<String, TestError> errorlist = bag.getValue();
+                MultiMap<String, TestError> errorlist = bag.getValue();
                 DefaultMutableTreeNode groupNode = null;
                 if (errorlist.size() > 1) {
@@ -222,7 +223,7 @@
                 }
 
-                for (Entry<String, List<TestError>> msgErrors : errorlist.entrySet()) {
+                for (Entry<String, LinkedHashSet<TestError>> msgErrors : errorlist.entrySet()) {
                     // Message node
-                    List<TestError> errs = msgErrors.getValue();
+                    Set<TestError> errs = msgErrors.getValue();
                     String msg;
                     if (groupNode != null) {
Index: /trunk/src/org/openstreetmap/josm/gui/layer/ValidatorLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/ValidatorLayer.java	(revision 3673)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/ValidatorLayer.java	(revision 3674)
@@ -19,5 +19,4 @@
 import org.openstreetmap.josm.data.validation.Severity;
 import org.openstreetmap.josm.data.validation.TestError;
-import org.openstreetmap.josm.data.validation.util.Bag;
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
@@ -25,4 +24,5 @@
 import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.MultiMap;
 
 /**
@@ -78,8 +78,8 @@
     @Override
     public String getToolTipText() {
-        Bag<Severity, TestError> errorTree = new Bag<Severity, TestError>();
+        MultiMap<Severity, TestError> errorTree = new MultiMap<Severity, TestError>();
         List<TestError> errors = Main.map.validatorDialog.tree.getErrors();
         for (TestError e : errors) {
-            errorTree.add(e.getSeverity(), e);
+            errorTree.put(e.getSeverity(), e);
         }
 
Index: /trunk/src/org/openstreetmap/josm/tools/MultiMap.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/MultiMap.java	(revision 3673)
+++ /trunk/src/org/openstreetmap/josm/tools/MultiMap.java	(revision 3674)
@@ -2,15 +2,30 @@
 package org.openstreetmap.josm.tools;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 /**
- * Maps keys to ordered sets of values.
+ * MultiMap - maps keys to multiple values
+ *
+ * Corresponds to Google guava LinkedHashMultimap and Apache Collections MultiValueMap
+ * but it is an independent (simplistic) implementation.
+ *
  */
-public class MultiMap<A, B>  {
+public class MultiMap<A, B> {
 
-    private final Map<A, LinkedHashSet<B>> map = new HashMap<A, LinkedHashSet<B>>();
+    private final Map<A, LinkedHashSet<B>> map;
+
+    public MultiMap() {
+        map = new HashMap<A, LinkedHashSet<B>>();
+    }
+
+    public MultiMap(int capacity) {
+        map = new HashMap<A, LinkedHashSet<B>>(capacity);
+    }
+
     /**
      * Map a key to a value. Can be called multiple times with the same key, but different value.
@@ -26,5 +41,7 @@
 
     /**
-     * Put a key that maps to nothing.
+     * Put a key that maps to nothing. (Only if it is not already in the map)
+     * Afterwards containsKey(key) will return true and get(key) will return
+     * an empty Set instead of null.
      */
     public void putVoid(A key) {
@@ -35,6 +52,21 @@
 
     /**
-     * Returns a list of values for the given key
-     * or an empty list, if it maps to nothing.
+     * Get the keySet
+     */
+    public Set<A> keySet() {
+        return map.keySet();
+    }
+
+    /**
+     * Return the Set associated with the given key. Result is null if
+     * nothing has been mapped to this key. Modifications of the returned list
+     * changes the underling map, but you should better not do that.
+     */
+    public Set<B> get(A key) {
+        return map.get(key);
+    }
+
+    /**
+     * Like get, but returns an empty Set if nothing has been mapped to the key.
      */
     public LinkedHashSet<B> getValues(A key) {
@@ -44,10 +76,43 @@
     }
 
-    public Set<A> keySet() {
-        return map.keySet();
+    public boolean isEmpty() {
+        return map.isEmpty();
     }
 
-    public Set<B> get(A key) {
-        return map.get(key);
+    public boolean containsKey(A key) {
+        return map.containsKey(key);
+    }
+
+    /**
+     * Returns true if the multimap contains a value for a key
+     * @param key The key
+     * @param value The value
+     * @return true if the key contains the value
+     */
+    public boolean contains(A key, B value) {
+        Set<B> values = get(key);
+        return (values == null) ? false : values.contains(value);
+    }
+
+    public void clear() {
+        map.clear();
+    }
+
+    public Set<Entry<A, LinkedHashSet<B>>> entrySet() {
+        return map.entrySet();
+    }
+
+    /**
+     * number of keys
+     */
+    public int size() {
+        return map.size();
+    }
+
+    /**
+     * returns a collection of all value sets
+     */
+    public Collection<LinkedHashSet<B>> values() {
+        return map.values();
     }
 }
Index: /trunk/src/org/openstreetmap/josm/tools/Utils.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/Utils.java	(revision 3673)
+++ /trunk/src/org/openstreetmap/josm/tools/Utils.java	(revision 3674)
@@ -11,3 +11,19 @@
         return false;
     }
+
+    /**
+     * Get minimum of 3 values
+     */
+    public static int min(int a, int b, int c) {
+        if (b < c) {
+            if (a < b)
+                return a;
+            return b;
+        } else {
+            if (a < c) {
+                return a;
+            }
+            return c;
+        }
+    }
 }
