Index: src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java
===================================================================
--- src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java	(revision 4591)
+++ src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java	(working copy)
@@ -148,7 +148,7 @@
         StringBuilder sb = new StringBuilder();
         try {
             /* replace %<x> with empty string or x=plugins (separated with comma) */
-            String pl = Main.pref.getCollectionAsString("plugins");
+            String pl = Utils.join(",", Main.pref.getCollection("plugins"));
             String printsite = site.replaceAll("%<(.*)>", "");
             if(pl != null && pl.length() != 0) {
                 site = site.replaceAll("%<(.*)>", "$1"+pl);
Index: src/org/openstreetmap/josm/io/MirroredInputStream.java
===================================================================
--- src/org/openstreetmap/josm/io/MirroredInputStream.java	(revision 4591)
+++ src/org/openstreetmap/josm/io/MirroredInputStream.java	(working copy)
@@ -14,9 +14,11 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Enumeration;
+import java.util.List;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
@@ -187,10 +189,9 @@
         String prefKey = getPrefKey(url, destDir);
         long age = 0L;
         File localFile = null;
-        Collection<String> localPathEntry = Main.pref.getCollection(prefKey);
-        if(localPathEntry.size() == 2) {
-            String[] lp = (String[]) localPathEntry.toArray();
-            localFile = new File(lp[1]);
+        List<String> localPathEntry = new ArrayList<String>(Main.pref.getCollection(prefKey));
+        if (localPathEntry.size() == 2) {
+            localFile = new File(localPathEntry.get(1));
             if(!localFile.exists())
                 localFile = null;
             else {
@@ -199,7 +200,7 @@
                 ) {
                     maxTime = Main.pref.getInteger("mirror.maxtime", 7*24*60*60);
                 }
-                age = System.currentTimeMillis() - Long.parseLong(lp[0]);
+                age = System.currentTimeMillis() - Long.parseLong(localPathEntry.get(0));
                 if (age < maxTime*1000) {
                     return localFile;
                 }
Index: src/org/openstreetmap/josm/data/Preferences.java
===================================================================
--- src/org/openstreetmap/josm/data/Preferences.java	(revision 4592)
+++ src/org/openstreetmap/josm/data/Preferences.java	(working copy)
@@ -21,6 +21,8 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -34,13 +36,23 @@
 
 import javax.swing.JOptionPane;
 import javax.swing.UIManager;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.Validator;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.transform.stax.StAXSource;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.io.MirroredInputStream;
 import org.openstreetmap.josm.io.XmlWriter;
 import org.openstreetmap.josm.tools.ColorHelper;
 import org.openstreetmap.josm.tools.Utils;
 import org.openstreetmap.josm.tools.XmlObjectParser;
-import org.xml.sax.SAXException;
 
 /**
  * This class holds all preferences for JOSM.
@@ -74,7 +86,16 @@
     protected final SortedMap<String, String> defaults = new TreeMap<String, String>();
     protected final SortedMap<String, String> colornames = new TreeMap<String, String>();
 
-    public interface PreferenceChangeEvent{
+    protected final SortedMap<String, Collection<String>> collectionProperties = new TreeMap<String, Collection<String>>();
+    protected final SortedMap<String, Collection<String>> collectionDefaults = new TreeMap<String, Collection<String>>();
+
+    protected final SortedMap<String, Collection<Collection<String>>> arrayProperties = new TreeMap<String, Collection<Collection<String>>>();
+    protected final SortedMap<String, Collection<Collection<String>>> arrayDefaults = new TreeMap<String, Collection<Collection<String>>>();
+
+    protected final SortedMap<String, Collection<Map<String,String>>> listOfStructsProperties = new TreeMap<String, Collection<Map<String,String>>>();
+    protected final SortedMap<String, Collection<Map<String,String>>> listOfStructsDefaults = new TreeMap<String, Collection<Map<String,String>>>();
+
+    public interface PreferenceChangeEvent {
         String getKey();
         String getOldValue();
         String getNewValue();
@@ -445,9 +466,9 @@
 
     private void load(boolean old) throws Exception {
         properties.clear();
-        if(!Main.applet) {
-            final BufferedReader in = new BufferedReader(new InputStreamReader(
-                    new FileInputStream(old ? getOldPreferenceFile() : getPreferenceFile()), "utf-8"));
+        if (!Main.applet) {
+            File pref = old ? getOldPreferenceFile() : getPreferenceFile();
+            BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(pref), "utf-8"));
             /* FIXME: TODO: remove old style config file end of 2012 */
             try {
                 if (old) {
@@ -455,6 +476,9 @@
                     int v = in.read();
                     in.reset();
                     if(v == '<') {
+                        validateXML(in);
+                        Utils.close(in);
+                        in = new BufferedReader(new InputStreamReader(new FileInputStream(pref), "utf-8"));
                         fromXML(in);
                     } else {
                         int lineNumber = 0;
@@ -475,6 +499,9 @@
                             throw new IOException(tr("Malformed config file at lines {0}", errLines));
                     }
                 } else {
+                    validateXML(in);
+                    Utils.close(in);
+                    in = new BufferedReader(new InputStreamReader(new FileInputStream(pref), "utf-8"));
                     fromXML(in);
                 }
             } finally {
@@ -731,6 +758,7 @@
         return 0.0;
     }
 
+    @Deprecated
     synchronized public String getCollectionAsString(final String key) {
         String s = get(key);
         if(s != null && s.length() != 0) {
@@ -739,6 +767,7 @@
         return s;
     }
 
+    @Deprecated
     public boolean isCollection(String key, boolean def) {
         String s = get(key);
         if (s != null && s.length() != 0)
@@ -754,12 +783,13 @@
      * @return the corresponding value if the property has been set before,
      *  def otherwise
      */
-    synchronized public Collection<String> getCollection(String key, Collection<String> def) {
+    public Collection<String> getCollection(String key, Collection<String> def) {
         putCollectionDefault(key, def);
-        String s = get(key);
-        if(s != null && s.length() != 0)
-            return Arrays.asList(s.split("\u001e", -1));
-        return def;
+        Collection<String> prop = getCollectionInternal(key);
+        if (prop != null)
+            return prop;
+        else
+            return def;
     }
 
     /**
@@ -768,14 +798,31 @@
      * @return the corresponding value if the property has been set before,
      *  an empty Collection otherwise.
      */
-    synchronized public Collection<String> getCollection(String key) {
+    public Collection<String> getCollection(String key) {
         putCollectionDefault(key, null);
-        String s = get(key);
-        if (s != null && s.length() != 0)
-            return Arrays.asList(s.split("\u001e", -1));
-        return Collections.emptyList();
+        Collection<String> prop = getCollectionInternal(key);
+        if (prop != null)
+            return prop;
+        else
+            return Collections.emptyList();
     }
 
+    synchronized private Collection<String> getCollectionInternal(String key) {
+        Collection<String> prop = collectionProperties.get(key);
+        if (prop != null)
+            return prop;
+        else {
+            String s = properties.get(key);
+            if(s != null) {
+                prop = Arrays.asList(s.split("\u001e", -1));
+                collectionProperties.put(key, prop);
+                properties.remove(key);
+                return prop;
+            }
+        }
+        return null;
+    }
+
     /* old style conversion, replace by above call after some transition time */
     /* remove this function, when no more old-style preference collections in the code */
     @Deprecated
@@ -798,10 +845,47 @@
         putCollection(key, a);
     }
 
-    synchronized public boolean putCollection(String key, Collection<String> val) {
-        return put(key, Utils.join("\u001e", val));
+    public boolean putCollection(String key, Collection<String> value) {
+        Collection<String> oldValue = null;
+        synchronized (this) {
+            if (value == null) {
+                boolean changed = collectionProperties.remove(key) != null;
+                changed |= properties.remove(key) != null;
+                if (!changed) return false;
+            } else {
+                oldValue = getCollectionInternal(key);
+                if (equalCollection(value, oldValue)) return false;
+                Collection<String> defValue = collectionDefaults.get(key);
+                if (oldValue == null && equalCollection(value, defValue)) return false;
+
+                Collection<String> valueCopy = new ArrayList<String>(value);
+                collectionProperties.put(key, valueCopy);
+                try {
+                    save();
+                } catch(IOException e){
+                    System.out.println(tr("Warning: failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
+                }
+            }
+        }
+        // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
+//        firePreferenceChanged(key, oldValue, value);
+        return true;
     }
 
+    private static boolean equalCollection(Collection<String> a, Collection<String> b) {
+        if (a == null) return b == null;
+        if (b == null) return false;
+        if (a.size() != b.size()) return false;
+        Iterator<String> itA = a.iterator();
+        Iterator<String> itB = b.iterator();
+        while (itA.hasNext()) {
+            String aStr = itA.next();
+            String bStr = itB.next();
+            if (!Utils.equal(aStr,bStr)) return false;
+        }
+        return true;
+    }
+
     /**
      * Saves at most {@code maxsize} items of collection {@code val}.
      */
@@ -817,70 +901,195 @@
     }
 
     synchronized private void putCollectionDefault(String key, Collection<String> val) {
-        putDefault(key, Utils.join("\u001e", val));
+        collectionDefaults.put(key, val);
     }
 
     /**
      * Used to read a 2-dimensional array of strings from the preference file.
      * If not a single entry could be found, def is returned.
      */
-    synchronized public Collection<Collection<String>> getArray(String key,
-            Collection<Collection<String>> def)
-            {
-        if(def != null) {
+    synchronized public Collection<Collection<String>> getArray(String key, Collection<Collection<String>> def) {
+        if (def != null) {
             putArrayDefault(key, def);
         }
-        key += ".";
-        int num = 0;
-        Collection<Collection<String>> col = new LinkedList<Collection<String>>();
-        while(properties.containsKey(key+num)) {
-            col.add(getCollection(key+num++, null));
-        }
-        return num == 0 ? def : col;
-            }
+        Collection<Collection<String>> prop = getArrayInternal(key);
+        if (prop != null)
+            return prop;
+        else
+            return def;
+    }
 
-    synchronized public boolean putArray(String key, Collection<Collection<String>> val) {
-        boolean changed = false;
-        key += ".";
-        Collection<String> keys = getAllPrefix(key).keySet();
-        if(val != null) {
+    synchronized private Collection<Collection<String>> getArrayInternal(String key) {
+        Collection<Collection<String>> prop = arrayProperties.get(key);
+        if (prop != null)
+            return prop;
+        else {
+            String keyDot = key + ".";
             int num = 0;
-            for(Collection<String> c : val) {
-                keys.remove(key+num);
-                changed |= putCollection(key+num++, c);
+            Collection<Collection<String>> col = new ArrayList<Collection<String>>();
+            while (true) {
+                Collection<String> c = getCollectionInternal(keyDot+num);
+                if (c == null) {
+                    break;
+                }
+                col.add(c);
+                collectionProperties.remove(keyDot+num);
+                num++;
             }
+            if (num > 0) {
+                arrayProperties.put(key, col);
+                return col;
+            }
         }
-        int l = key.length();
-        for(String k : keys) {
-            try {
-                Integer.valueOf(k.substring(l));
-                changed |= put(k, null);
-            } catch(NumberFormatException e) {
-                /* everything which does not end with a number should not be deleted */
+        return null;
+    }
+
+    public boolean putArray(String key, Collection<Collection<String>> value) {
+        boolean changed = false;
+
+        Collection<Collection<String>> oldValue;
+
+        synchronized (this) {
+            if (value == null) {
+                getArrayInternal(key);
+                if (arrayProperties.remove(key) != null) return false;
+            } else {
+                oldValue = getArrayInternal(key);
+                if (equalArray(oldValue, value)) return false;
+
+                Collection<Collection<String>> defValue = arrayDefaults.get(key);
+                if (oldValue == null && equalArray(value, defValue)) return false;
+
+                Collection<Collection<String>> valueCopy = new ArrayList<Collection<String>>(value.size());
+                for (Collection<String> lst : value) {
+                    valueCopy.add(new ArrayList<String>(lst));
+                }
+                arrayProperties.put(key, valueCopy);
+                try {
+                    save();
+                } catch(IOException e){
+                    System.out.println(tr("Warning: failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
+                }
             }
         }
-        return changed;
+        // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
+//        firePreferenceChanged(key, oldValue, value);
+        return true;
     }
 
+    private static boolean equalArray(Collection<Collection<String>> a, Collection<Collection<String>> b) {
+        if (a == null) return b == null;
+        if (b == null) return false;
+        if (a.size() != b.size()) return false;
+        Iterator<Collection<String>> itA = a.iterator();
+        Iterator<Collection<String>> itB = b.iterator();
+        while (itA.hasNext()) {
+            if (!equalCollection(itA.next(), itB.next())) return false;
+        }
+        return true;
+    }
+
     synchronized private void putArrayDefault(String key, Collection<Collection<String>> val) {
-        key += ".";
-        Collection<String> keys = getAllPrefixDefault(key).keySet();
-        int num = 0;
-        for(Collection<String> c : val) {
-            keys.remove(key+num);
-            putCollectionDefault(key+num++, c);
+        arrayDefaults.put(key, val);
+    }
+
+    public Collection<Map<String, String>> getListOfStructs(String key, Collection<Map<String, String>> def) {
+        if (def != null) {
+            putListOfStructsDefault(key, def);
         }
-        int l = key.length();
-        for(String k : keys) {
-            try {
-                Integer.valueOf(k.substring(l));
-                defaults.remove(k);
-            } catch(Exception e) {
-                /* everything which does not end with a number should not be deleted */
+        Collection<Map<String, String>> prop = getListOfStructsInternal(key);
+        if (prop != null)
+            return prop;
+        else
+            return def;
+    }
+
+    public Collection<Map<String, String>> getListOfStructsInternal(String key) {
+        Collection<Map<String, String>> prop = listOfStructsProperties.get(key);
+        if (prop != null)
+            return prop;
+        else {
+            Collection<Collection<String>> array = getArrayInternal(key);
+            if (array == null) return null;
+            prop = new ArrayList<Map<String, String>>(array.size());
+            for (Collection<String> mapStr : array) {
+                Map<String, String> map = new LinkedHashMap<String, String>();
+                for (String key_value : mapStr) {
+                    final int i = key_value.indexOf(':');
+                    if (i == -1 || i == 0) {
+                        continue;
+                    }
+                    String k = key_value.substring(0,i);
+                    String v = key_value.substring(i+1);
+                    map.put(k, v);
+                }
+                prop.add(map);
             }
+            arrayProperties.remove(key);
+            listOfStructsProperties.put(key,prop);
+            return prop;
         }
     }
 
+    public boolean putListOfStructs(String key, Collection<Map<String, String>> value) {
+        boolean changed = false;
+
+        Collection<Map<String, String>> oldValue;
+
+        synchronized (this) {
+            if (value == null) {
+                getListOfStructsInternal(key);
+                if (listOfStructsProperties.remove(key) != null) return false;
+            } else {
+                oldValue = getListOfStructsInternal(key);
+                if (equalListOfStructs(oldValue, value)) return false;
+
+                Collection<Map<String, String>> defValue = listOfStructsDefaults.get(key);
+                if (oldValue == null && equalListOfStructs(value, defValue)) return false;
+
+                Collection<Map<String, String>> valueCopy = new ArrayList<Map<String, String>>(value.size());
+                for (Map<String, String> map : value) {
+                    valueCopy.add(new LinkedHashMap<String,String>(map));
+                }
+                listOfStructsProperties.put(key, valueCopy);
+                try {
+                    save();
+                } catch(IOException e){
+                    System.out.println(tr("Warning: failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
+                }
+            }
+        }
+        // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
+//        firePreferenceChanged(key, oldValue, value);
+        return true;
+    }
+
+    private static boolean equalListOfStructs(Collection<Map<String, String>> a, Collection<Map<String, String>> b) {
+        if (a == null) return b == null;
+        if (b == null) return false;
+        if (a.size() != b.size()) return false;
+        Iterator<Map<String, String>> itA = a.iterator();
+        Iterator<Map<String, String>> itB = b.iterator();
+        while (itA.hasNext()) {
+            if (!equalMap(itA.next(), itB.next())) return false;
+        }
+        return true;
+    }
+
+    private static boolean equalMap(Map<String, String> a, Map<String, String> b) {
+        if (a == null) return b == null;
+        if (b == null) return false;
+        if (a.size() != b.size()) return false;
+        for (Entry<String, String> e : a.entrySet()) {
+            if (!Utils.equal(e.getValue(), b.get(e.getKey()))) return false;
+        }
+        return true;
+    }
+
+    synchronized private void putListOfStructsDefault(String key, Collection<Map<String, String>> val) {
+        listOfStructsDefaults.put(key, val);
+    }
+
     @Retention(RetentionPolicy.RUNTIME) public @interface pref { }
     @Retention(RetentionPolicy.RUNTIME) public @interface writeExplicitly { }
 
@@ -910,12 +1119,12 @@
      * same as above, but returns def if nothing was found
      */
     public <T> List<T> getListOfStructs(String key, Collection<T> def, Class<T> klass) {
-        Collection<Collection<String>> array =
-            getArray(key, def == null ? null : serializeListOfStructs(def, klass));
-        if (array == null)
+        Collection<Map<String,String>> prop =
+            getListOfStructs(key, def == null ? null : serializeListOfStructs(def, klass));
+        if (prop == null)
             return def == null ? null : new ArrayList<T>(def);
         List<T> lst = new ArrayList<T>();
-        for (Collection<String> entries : array) {
+        for (Map<String,String> entries : prop) {
             T struct = deserializeStruct(entries, klass);
             lst.add(struct);
         }
@@ -936,13 +1145,13 @@
      * @return true if something has changed
      */
     public <T> boolean putListOfStructs(String key, Collection<T> val, Class<T> klass) {
-        return putArray(key, serializeListOfStructs(val, klass));
+        return putListOfStructs(key, serializeListOfStructs(val, klass));
     }
 
-    private <T> Collection<Collection<String>> serializeListOfStructs(Collection<T> l, Class<T> klass) {
+    private <T> Collection<Map<String,String>> serializeListOfStructs(Collection<T> l, Class<T> klass) {
         if (l == null)
             return null;
-        Collection<Collection<String>> vals = new ArrayList<Collection<String>>();
+        Collection<Map<String,String>> vals = new ArrayList<Map<String,String>>();
         for (T struct : l) {
             if (struct == null) {
                 continue;
@@ -952,7 +1161,7 @@
         return vals;
     }
 
-    private <T> Collection<String> serializeStruct(T struct, Class<T> klass) {
+    private <T> Map<String,String> serializeStruct(T struct, Class<T> klass) {
         T structPrototype;
         try {
             structPrototype = klass.newInstance();
@@ -962,7 +1171,7 @@
             throw new RuntimeException(ex);
         }
 
-        Collection<String> hash = new ArrayList<String>();
+        Map<String,String> hash = new LinkedHashMap<String,String>();
         for (Field f : klass.getDeclaredFields()) {
             if (f.getAnnotation(pref.class) == null) {
                 continue;
@@ -973,7 +1182,7 @@
                 Object defaultFieldValue = f.get(structPrototype);
                 if (fieldValue != null) {
                     if (f.getAnnotation(writeExplicitly.class) != null || !Utils.equal(fieldValue, defaultFieldValue)) {
-                        hash.add(String.format("%s:%s", f.getName().replace("_", "-"), fieldValue.toString()));
+                        hash.put(f.getName().replace("_", "-"), fieldValue.toString());
                     }
                 }
             } catch (IllegalArgumentException ex) {
@@ -985,7 +1194,7 @@
         return hash;
     }
 
-    private <T> T deserializeStruct(Collection<String> hash, Class<T> klass) {
+    private <T> T deserializeStruct(Map<String,String> hash, Class<T> klass) {
         T struct = null;
         try {
             struct = klass.newInstance();
@@ -994,18 +1203,11 @@
         } catch (IllegalAccessException ex) {
             throw new RuntimeException();
         }
-        for (String key_value : hash) {
-            final int i = key_value.indexOf(':');
-            if (i == -1 || i == 0) {
-                continue;
-            }
-            String key = key_value.substring(0,i);
-            String valueString = key_value.substring(i+1);
-
+        for (Entry<String,String> key_value : hash.entrySet()) {
             Object value = null;
             Field f;
             try {
-                f = klass.getDeclaredField(key.replace("-", "_"));
+                f = klass.getDeclaredField(key_value.getKey().replace("-", "_"));
             } catch (NoSuchFieldException ex) {
                 continue;
             } catch (SecurityException ex) {
@@ -1016,21 +1218,21 @@
             }
             f.setAccessible(true);
             if (f.getType() == Boolean.class || f.getType() == boolean.class) {
-                value = Boolean.parseBoolean(valueString);
+                value = Boolean.parseBoolean(key_value.getValue());
             } else if (f.getType() == Integer.class || f.getType() == int.class) {
                 try {
-                    value = Integer.parseInt(valueString);
+                    value = Integer.parseInt(key_value.getValue());
                 } catch (NumberFormatException nfe) {
                     continue;
                 }
             } else if (f.getType() == Double.class || f.getType() == double.class) {
                 try {
-                    value = Double.parseDouble(valueString);
+                    value = Double.parseDouble(key_value.getValue());
                 } catch (NumberFormatException nfe) {
                     continue;
                 }
             } else  if (f.getType() == String.class) {
-                value = valueString;
+                value = key_value.getValue();
             } else
                 throw new RuntimeException("unsupported preference primitive type");
 
@@ -1079,37 +1281,150 @@
         putCollection("pluginmanager.sites", sites);
     }
 
-    public static class XMLTag {
-        public String key;
-        public String value;
+    protected XMLStreamReader parser;
+
+    public void validateXML(Reader in) throws Exception {
+        SchemaFactory factory =  SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
+        Schema schema = factory.newSchema(new StreamSource(new MirroredInputStream("resource://data/preferences.xsd")));
+        XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(in);
+        Validator validator = schema.newValidator();
+        validator.validate(new StAXSource(parser));
     }
-    public static class XMLCollection {
-        public String key;
+
+    public void fromXML(Reader in) throws XMLStreamException {
+        XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(in);
+        this.parser = parser;
+        parse();
     }
-    public static class XMLEntry {
-        public String value;
+
+    public void parse() throws XMLStreamException {
+        int event = parser.getEventType();
+        while (true) {
+            if (event == XMLStreamConstants.START_ELEMENT) {
+                parseRoot();
+            } else if (event == XMLStreamConstants.END_ELEMENT) {
+                return;
+            }
+            if (parser.hasNext()) {
+                event = parser.next();
+            } else {
+                break;
+            }
+        }
+        parser.close();
     }
-    public void fromXML(Reader in) throws SAXException {
-        XmlObjectParser parser = new XmlObjectParser();
-        parser.map("tag", XMLTag.class);
-        parser.map("entry", XMLEntry.class);
-        parser.map("collection", XMLCollection.class);
-        parser.startWithValidation(in,
-                "http://josm.openstreetmap.de/preferences-1.0", "resource://data/preferences.xsd");
-        LinkedList<String> vals = new LinkedList<String>();
-        while(parser.hasNext()) {
-            Object o = parser.next();
-            if(o instanceof XMLTag) {
-                properties.put(((XMLTag)o).key, ((XMLTag)o).value);
-            } else if (o instanceof XMLEntry) {
-                vals.add(((XMLEntry)o).value);
-            } else if (o instanceof XMLCollection) {
-                properties.put(((XMLCollection)o).key, Utils.join("\u001e", vals));
-                vals = new LinkedList<String>();
+
+    public void parseRoot() throws XMLStreamException {
+        while (true) {
+            int event = parser.next();
+            if (event == XMLStreamConstants.START_ELEMENT) {
+                if (parser.getLocalName().equals("tag")) {
+                    properties.put(parser.getAttributeValue(null, "key"), parser.getAttributeValue(null, "value"));
+                    jumpToEnd();
+                } else if (parser.getLocalName().equals("list") || parser.getLocalName().equals("collection")) {
+                    parseToplevelList();
+                } else {
+                    throwException("Unexpected element: "+parser.getLocalName());
+                }
+            } else if (event == XMLStreamConstants.END_ELEMENT) {
+                return;
             }
         }
     }
 
+    private void jumpToEnd() throws XMLStreamException {
+        while (true) {
+            int event = parser.next();
+            if (event == XMLStreamConstants.START_ELEMENT) {
+                jumpToEnd();
+            } else if (event == XMLStreamConstants.END_ELEMENT) {
+                return;
+            }
+        }
+    }
+
+    protected void parseToplevelList() throws XMLStreamException {
+        String key = parser.getAttributeValue(null, "key");
+        Collection<String> entries = null;
+        Collection<Collection<String>> lists = null;
+        Collection<Map<String, String>> maps = null;
+        while (true) {
+            int event = parser.next();
+            if (event == XMLStreamConstants.START_ELEMENT) {
+                if (parser.getLocalName().equals("entry")) {
+                    if (entries == null) {
+                        entries = new ArrayList<String>();
+                    }
+                    entries.add(parser.getAttributeValue(null, "value"));
+                    jumpToEnd();
+                } else if (parser.getLocalName().equals("list")) {
+                    if (lists == null) {
+                        lists = new ArrayList<Collection<String>>();
+                    }
+                    lists.add(parseInnerList());
+                } else if (parser.getLocalName().equals("map")) {
+                    if (maps == null) {
+                        maps = new ArrayList<Map<String, String>>();
+                    }
+                    maps.add(parseMap());
+                } else {
+                    throwException("Unexpected element: "+parser.getLocalName());
+                }
+            } else if (event == XMLStreamConstants.END_ELEMENT) {
+                break;
+            }
+        }
+        if (entries != null) {
+            collectionProperties.put(key, entries);
+        }
+        if (lists != null) {
+            arrayProperties.put(key, lists);
+        }
+        if (maps != null) {
+            listOfStructsProperties.put(key, maps);
+        }
+    }
+
+    protected Collection<String> parseInnerList() throws XMLStreamException {
+        Collection<String> entries = new ArrayList<String>();
+        while (true) {
+            int event = parser.next();
+            if (event == XMLStreamConstants.START_ELEMENT) {
+                if (parser.getLocalName().equals("entry")) {
+                    entries.add(parser.getAttributeValue(null, "value"));
+                    jumpToEnd();
+                } else {
+                    throwException("Unexpected element: "+parser.getLocalName());
+                }
+            } else if (event == XMLStreamConstants.END_ELEMENT) {
+                break;
+            }
+        }
+        return entries;
+    }
+
+    protected Map<String, String> parseMap() throws XMLStreamException {
+        Map<String, String> map = new LinkedHashMap<String, String>();
+        while (true) {
+            int event = parser.next();
+            if (event == XMLStreamConstants.START_ELEMENT) {
+                if (parser.getLocalName().equals("tag")) {
+                    map.put(parser.getAttributeValue(null, "key"), parser.getAttributeValue(null, "value"));
+                    jumpToEnd();
+                } else {
+                    throwException("Unexpected element: "+parser.getLocalName());
+                }
+            } else if (event == XMLStreamConstants.END_ELEMENT) {
+                break;
+            }
+        }
+        return map;
+    }
+
+    protected void throwException(String msg) {
+        throw new RuntimeException(msg + tr(" (at line {0}, column {1})", parser.getLocation().getLineNumber(), parser.getLocation().getColumnNumber()));
+    }
+
     public String toXML(boolean nopass) {
         StringBuilder b = new StringBuilder(
                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
@@ -1125,28 +1440,57 @@
             if(s == null || !s.equals(r)) {
                 if(r.contains("\u001e"))
                 {
-                    b.append(" <collection key='");
+                    b.append("  <list key='");
                     b.append(XmlWriter.encode(p.getKey()));
                     b.append("'>\n");
                     for (String val : r.split("\u001e", -1))
                     {
-                        b.append("  <entry value='");
+                        b.append("    <entry value='");
                         b.append(XmlWriter.encode(val));
-                        b.append("' />\n");
+                        b.append("'/>\n");
                     }
-                    b.append(" </collection>\n");
+                    b.append("  </list>\n");
                 }
                 else
                 {
-                    b.append(" <tag key='");
+                    b.append("  <tag key='");
                     b.append(XmlWriter.encode(p.getKey()));
                     b.append("' value='");
                     b.append(XmlWriter.encode(p.getValue()));
-                    b.append("' />\n");
+                    b.append("'/>\n");
                 }
             }
         }
-        b.append("</preferences>");
+        for (Entry<String, Collection<String>> listEntry : collectionProperties.entrySet()) {
+            b.append("  <list key='").append(XmlWriter.encode(listEntry.getKey())).append("'>\n");
+            for (String s : listEntry.getValue()) {
+                b.append("    <entry value='").append(XmlWriter.encode(s)).append("'/>\n");
+            }
+            b.append("  </list>\n");
+        }
+        for (Entry<String, Collection<Collection<String>>> arrayEntry : arrayProperties.entrySet()) {
+            b.append("  <list key='").append(XmlWriter.encode(arrayEntry.getKey())).append("'>\n");
+            for (Collection<String> list : arrayEntry.getValue()) {
+                b.append("    <list>\n");
+                for (String s : list) {
+                    b.append("      <entry value='").append(XmlWriter.encode(s)).append("'/>\n");
+                }
+                b.append("    </list>\n");
+            }
+            b.append("  </list>\n");
+        }
+        for (Entry<String, Collection<Map<String, String>>> listOfStructsEntry : listOfStructsProperties.entrySet()) {
+            b.append("  <list key='").append(XmlWriter.encode(listOfStructsEntry.getKey())).append("'>\n");
+            for (Map<String, String> struct : listOfStructsEntry.getValue()) {
+                b.append("    <map>\n");
+                for (Entry<String, String> e : struct.entrySet()) {
+                    b.append("      <tag key='").append(XmlWriter.encode(e.getKey())).append("' value='").append(XmlWriter.encode(e.getValue())).append("'/>\n");
+                }
+                b.append("    </map>\n");
+            }
+            b.append("  </list>\n");
+        }
+        b.append("</preferences>\n");
         return b.toString();
     }
 }
Index: src/org/openstreetmap/josm/data/ServerSidePreferences.java
===================================================================
--- src/org/openstreetmap/josm/data/ServerSidePreferences.java	(revision 4591)
+++ src/org/openstreetmap/josm/data/ServerSidePreferences.java	(working copy)
@@ -21,12 +21,12 @@
 import java.util.Map.Entry;
 
 import javax.swing.JOptionPane;
+import javax.xml.stream.XMLStreamException;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.io.OsmConnection;
 import org.openstreetmap.josm.io.OsmTransferException;
 import org.openstreetmap.josm.tools.Base64;
-import org.xml.sax.SAXException;
 
 /**
  * This class tweak the Preferences class to provide server side preference settings, as example
@@ -170,7 +170,7 @@
             fromXML(in);
         } catch (RuntimeException e) {
             e.printStackTrace();
-        } catch (SAXException e) {
+        } catch (XMLStreamException e) {
             e.printStackTrace();
         }
         return res;
Index: data/preferences.xsd
===================================================================
--- data/preferences.xsd	(revision 4591)
+++ data/preferences.xsd	(working copy)
@@ -9,6 +9,7 @@
 			<choice minOccurs="0" maxOccurs="unbounded">
 				<element name="tag" type="tns:tag" />
 				<element name="collection" type="tns:collection" />
+				<element name="list" type="tns:list" />
 			</choice>
 		</sequence>
 		<attribute name="version" type="string" />
@@ -19,6 +20,7 @@
 		<attribute name="value" type="string" use="required"/>
 	</complexType>
 
+    <!-- deprecated: remove mid 2012 -->
 	<complexType name="collection">
 		<sequence>
 			<choice minOccurs="1" maxOccurs="unbounded">
@@ -28,6 +30,33 @@
 		<attribute name="key" type="string" use="required" />
 	</complexType>
 
+	<complexType name="list">
+	    <choice>
+            <sequence>
+			    <element name="entry" type="tns:entry" minOccurs="0" maxOccurs="unbounded"/>
+            </sequence>
+            <sequence>
+			    <element name="list" type="tns:slist" minOccurs="0" maxOccurs="unbounded"/>
+            </sequence>
+            <sequence>
+			    <element name="map" type="tns:map" minOccurs="0" maxOccurs="unbounded"/>
+            </sequence>
+        </choice>
+		<attribute name="key" type="string" use="required" />
+	</complexType>
+	
+	<complexType name="slist">
+        <sequence>
+		    <element name="entry" type="tns:entry" minOccurs="0" maxOccurs="unbounded"/>
+        </sequence>
+	</complexType>
+
+	<complexType name="map">
+        <sequence>
+		    <element name="tag" type="tns:tag" minOccurs="0" maxOccurs="unbounded"/>
+        </sequence>
+	</complexType>
+
 	<complexType name="entry">
 		<attribute name="value" type="string" use="required"/>
 	</complexType>
