Ticket #7027: preferences-structures.patch

File preferences-structures.patch, 38.2 KB (added by bastiK, 14 years ago)
  • src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java

     
    148148        StringBuilder sb = new StringBuilder();
    149149        try {
    150150            /* replace %<x> with empty string or x=plugins (separated with comma) */
    151             String pl = Main.pref.getCollectionAsString("plugins");
     151            String pl = Utils.join(",", Main.pref.getCollection("plugins"));
    152152            String printsite = site.replaceAll("%<(.*)>", "");
    153153            if(pl != null && pl.length() != 0) {
    154154                site = site.replaceAll("%<(.*)>", "$1"+pl);
  • src/org/openstreetmap/josm/io/MirroredInputStream.java

     
    1414import java.net.MalformedURLException;
    1515import java.net.URL;
    1616import java.net.URLConnection;
     17import java.util.ArrayList;
    1718import java.util.Arrays;
    1819import java.util.Collection;
    1920import java.util.Enumeration;
     21import java.util.List;
    2022import java.util.zip.ZipEntry;
    2123import java.util.zip.ZipFile;
    2224
     
    187189        String prefKey = getPrefKey(url, destDir);
    188190        long age = 0L;
    189191        File localFile = null;
    190         Collection<String> localPathEntry = Main.pref.getCollection(prefKey);
    191         if(localPathEntry.size() == 2) {
    192             String[] lp = (String[]) localPathEntry.toArray();
    193             localFile = new File(lp[1]);
     192        List<String> localPathEntry = new ArrayList<String>(Main.pref.getCollection(prefKey));
     193        if (localPathEntry.size() == 2) {
     194            localFile = new File(localPathEntry.get(1));
    194195            if(!localFile.exists())
    195196                localFile = null;
    196197            else {
     
    199200                ) {
    200201                    maxTime = Main.pref.getInteger("mirror.maxtime", 7*24*60*60);
    201202                }
    202                 age = System.currentTimeMillis() - Long.parseLong(lp[0]);
     203                age = System.currentTimeMillis() - Long.parseLong(localPathEntry.get(0));
    203204                if (age < maxTime*1000) {
    204205                    return localFile;
    205206                }
  • src/org/openstreetmap/josm/data/Preferences.java

     
    2121import java.util.Arrays;
    2222import java.util.Collection;
    2323import java.util.Collections;
     24import java.util.Iterator;
     25import java.util.LinkedHashMap;
    2426import java.util.LinkedList;
    2527import java.util.List;
    2628import java.util.Map;
     
    3436
    3537import javax.swing.JOptionPane;
    3638import javax.swing.UIManager;
     39import javax.xml.stream.XMLInputFactory;
     40import javax.xml.stream.XMLStreamConstants;
     41import javax.xml.stream.XMLStreamException;
     42import javax.xml.stream.XMLStreamReader;
     43import javax.xml.parsers.SAXParserFactory;
     44import javax.xml.transform.stream.StreamSource;
     45import javax.xml.validation.Schema;
     46import javax.xml.validation.Validator;
     47import javax.xml.validation.SchemaFactory;
     48import javax.xml.transform.stax.StAXSource;
    3749
    3850import org.openstreetmap.josm.Main;
     51import org.openstreetmap.josm.io.MirroredInputStream;
    3952import org.openstreetmap.josm.io.XmlWriter;
    4053import org.openstreetmap.josm.tools.ColorHelper;
    4154import org.openstreetmap.josm.tools.Utils;
    4255import org.openstreetmap.josm.tools.XmlObjectParser;
    43 import org.xml.sax.SAXException;
    4456
    4557/**
    4658 * This class holds all preferences for JOSM.
     
    7486    protected final SortedMap<String, String> defaults = new TreeMap<String, String>();
    7587    protected final SortedMap<String, String> colornames = new TreeMap<String, String>();
    7688
    77     public interface PreferenceChangeEvent{
     89    protected final SortedMap<String, Collection<String>> collectionProperties = new TreeMap<String, Collection<String>>();
     90    protected final SortedMap<String, Collection<String>> collectionDefaults = new TreeMap<String, Collection<String>>();
     91
     92    protected final SortedMap<String, Collection<Collection<String>>> arrayProperties = new TreeMap<String, Collection<Collection<String>>>();
     93    protected final SortedMap<String, Collection<Collection<String>>> arrayDefaults = new TreeMap<String, Collection<Collection<String>>>();
     94
     95    protected final SortedMap<String, Collection<Map<String,String>>> listOfStructsProperties = new TreeMap<String, Collection<Map<String,String>>>();
     96    protected final SortedMap<String, Collection<Map<String,String>>> listOfStructsDefaults = new TreeMap<String, Collection<Map<String,String>>>();
     97
     98    public interface PreferenceChangeEvent {
    7899        String getKey();
    79100        String getOldValue();
    80101        String getNewValue();
     
    445466
    446467    private void load(boolean old) throws Exception {
    447468        properties.clear();
    448         if(!Main.applet) {
    449             final BufferedReader in = new BufferedReader(new InputStreamReader(
    450                     new FileInputStream(old ? getOldPreferenceFile() : getPreferenceFile()), "utf-8"));
     469        if (!Main.applet) {
     470            File pref = old ? getOldPreferenceFile() : getPreferenceFile();
     471            BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(pref), "utf-8"));
    451472            /* FIXME: TODO: remove old style config file end of 2012 */
    452473            try {
    453474                if (old) {
     
    455476                    int v = in.read();
    456477                    in.reset();
    457478                    if(v == '<') {
     479                        validateXML(in);
     480                        Utils.close(in);
     481                        in = new BufferedReader(new InputStreamReader(new FileInputStream(pref), "utf-8"));
    458482                        fromXML(in);
    459483                    } else {
    460484                        int lineNumber = 0;
     
    475499                            throw new IOException(tr("Malformed config file at lines {0}", errLines));
    476500                    }
    477501                } else {
     502                    validateXML(in);
     503                    Utils.close(in);
     504                    in = new BufferedReader(new InputStreamReader(new FileInputStream(pref), "utf-8"));
    478505                    fromXML(in);
    479506                }
    480507            } finally {
     
    731758        return 0.0;
    732759    }
    733760
     761    @Deprecated
    734762    synchronized public String getCollectionAsString(final String key) {
    735763        String s = get(key);
    736764        if(s != null && s.length() != 0) {
     
    739767        return s;
    740768    }
    741769
     770    @Deprecated
    742771    public boolean isCollection(String key, boolean def) {
    743772        String s = get(key);
    744773        if (s != null && s.length() != 0)
     
    754783     * @return the corresponding value if the property has been set before,
    755784     *  def otherwise
    756785     */
    757     synchronized public Collection<String> getCollection(String key, Collection<String> def) {
     786    public Collection<String> getCollection(String key, Collection<String> def) {
    758787        putCollectionDefault(key, def);
    759         String s = get(key);
    760         if(s != null && s.length() != 0)
    761             return Arrays.asList(s.split("\u001e", -1));
    762         return def;
     788        Collection<String> prop = getCollectionInternal(key);
     789        if (prop != null)
     790            return prop;
     791        else
     792            return def;
    763793    }
    764794
    765795    /**
     
    768798     * @return the corresponding value if the property has been set before,
    769799     *  an empty Collection otherwise.
    770800     */
    771     synchronized public Collection<String> getCollection(String key) {
     801    public Collection<String> getCollection(String key) {
    772802        putCollectionDefault(key, null);
    773         String s = get(key);
    774         if (s != null && s.length() != 0)
    775             return Arrays.asList(s.split("\u001e", -1));
    776         return Collections.emptyList();
     803        Collection<String> prop = getCollectionInternal(key);
     804        if (prop != null)
     805            return prop;
     806        else
     807            return Collections.emptyList();
    777808    }
    778809
     810    synchronized private Collection<String> getCollectionInternal(String key) {
     811        Collection<String> prop = collectionProperties.get(key);
     812        if (prop != null)
     813            return prop;
     814        else {
     815            String s = properties.get(key);
     816            if(s != null) {
     817                prop = Arrays.asList(s.split("\u001e", -1));
     818                collectionProperties.put(key, prop);
     819                properties.remove(key);
     820                return prop;
     821            }
     822        }
     823        return null;
     824    }
     825
    779826    /* old style conversion, replace by above call after some transition time */
    780827    /* remove this function, when no more old-style preference collections in the code */
    781828    @Deprecated
     
    798845        putCollection(key, a);
    799846    }
    800847
    801     synchronized public boolean putCollection(String key, Collection<String> val) {
    802         return put(key, Utils.join("\u001e", val));
     848    public boolean putCollection(String key, Collection<String> value) {
     849        Collection<String> oldValue = null;
     850        synchronized (this) {
     851            if (value == null) {
     852                boolean changed = collectionProperties.remove(key) != null;
     853                changed |= properties.remove(key) != null;
     854                if (!changed) return false;
     855            } else {
     856                oldValue = getCollectionInternal(key);
     857                if (equalCollection(value, oldValue)) return false;
     858                Collection<String> defValue = collectionDefaults.get(key);
     859                if (oldValue == null && equalCollection(value, defValue)) return false;
     860
     861                Collection<String> valueCopy = new ArrayList<String>(value);
     862                collectionProperties.put(key, valueCopy);
     863                try {
     864                    save();
     865                } catch(IOException e){
     866                    System.out.println(tr("Warning: failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
     867                }
     868            }
     869        }
     870        // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
     871//        firePreferenceChanged(key, oldValue, value);
     872        return true;
    803873    }
    804874
     875    private static boolean equalCollection(Collection<String> a, Collection<String> b) {
     876        if (a == null) return b == null;
     877        if (b == null) return false;
     878        if (a.size() != b.size()) return false;
     879        Iterator<String> itA = a.iterator();
     880        Iterator<String> itB = b.iterator();
     881        while (itA.hasNext()) {
     882            String aStr = itA.next();
     883            String bStr = itB.next();
     884            if (!Utils.equal(aStr,bStr)) return false;
     885        }
     886        return true;
     887    }
     888
    805889    /**
    806890     * Saves at most {@code maxsize} items of collection {@code val}.
    807891     */
     
    817901    }
    818902
    819903    synchronized private void putCollectionDefault(String key, Collection<String> val) {
    820         putDefault(key, Utils.join("\u001e", val));
     904        collectionDefaults.put(key, val);
    821905    }
    822906
    823907    /**
    824908     * Used to read a 2-dimensional array of strings from the preference file.
    825909     * If not a single entry could be found, def is returned.
    826910     */
    827     synchronized public Collection<Collection<String>> getArray(String key,
    828             Collection<Collection<String>> def)
    829             {
    830         if(def != null) {
     911    synchronized public Collection<Collection<String>> getArray(String key, Collection<Collection<String>> def) {
     912        if (def != null) {
    831913            putArrayDefault(key, def);
    832914        }
    833         key += ".";
    834         int num = 0;
    835         Collection<Collection<String>> col = new LinkedList<Collection<String>>();
    836         while(properties.containsKey(key+num)) {
    837             col.add(getCollection(key+num++, null));
    838         }
    839         return num == 0 ? def : col;
    840             }
     915        Collection<Collection<String>> prop = getArrayInternal(key);
     916        if (prop != null)
     917            return prop;
     918        else
     919            return def;
     920    }
    841921
    842     synchronized public boolean putArray(String key, Collection<Collection<String>> val) {
    843         boolean changed = false;
    844         key += ".";
    845         Collection<String> keys = getAllPrefix(key).keySet();
    846         if(val != null) {
     922    synchronized private Collection<Collection<String>> getArrayInternal(String key) {
     923        Collection<Collection<String>> prop = arrayProperties.get(key);
     924        if (prop != null)
     925            return prop;
     926        else {
     927            String keyDot = key + ".";
    847928            int num = 0;
    848             for(Collection<String> c : val) {
    849                 keys.remove(key+num);
    850                 changed |= putCollection(key+num++, c);
     929            Collection<Collection<String>> col = new ArrayList<Collection<String>>();
     930            while (true) {
     931                Collection<String> c = getCollectionInternal(keyDot+num);
     932                if (c == null) {
     933                    break;
     934                }
     935                col.add(c);
     936                collectionProperties.remove(keyDot+num);
     937                num++;
    851938            }
     939            if (num > 0) {
     940                arrayProperties.put(key, col);
     941                return col;
     942            }
    852943        }
    853         int l = key.length();
    854         for(String k : keys) {
    855             try {
    856                 Integer.valueOf(k.substring(l));
    857                 changed |= put(k, null);
    858             } catch(NumberFormatException e) {
    859                 /* everything which does not end with a number should not be deleted */
     944        return null;
     945    }
     946
     947    public boolean putArray(String key, Collection<Collection<String>> value) {
     948        boolean changed = false;
     949
     950        Collection<Collection<String>> oldValue;
     951
     952        synchronized (this) {
     953            if (value == null) {
     954                getArrayInternal(key);
     955                if (arrayProperties.remove(key) != null) return false;
     956            } else {
     957                oldValue = getArrayInternal(key);
     958                if (equalArray(oldValue, value)) return false;
     959
     960                Collection<Collection<String>> defValue = arrayDefaults.get(key);
     961                if (oldValue == null && equalArray(value, defValue)) return false;
     962
     963                Collection<Collection<String>> valueCopy = new ArrayList<Collection<String>>(value.size());
     964                for (Collection<String> lst : value) {
     965                    valueCopy.add(new ArrayList<String>(lst));
     966                }
     967                arrayProperties.put(key, valueCopy);
     968                try {
     969                    save();
     970                } catch(IOException e){
     971                    System.out.println(tr("Warning: failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
     972                }
    860973            }
    861974        }
    862         return changed;
     975        // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
     976//        firePreferenceChanged(key, oldValue, value);
     977        return true;
    863978    }
    864979
     980    private static boolean equalArray(Collection<Collection<String>> a, Collection<Collection<String>> b) {
     981        if (a == null) return b == null;
     982        if (b == null) return false;
     983        if (a.size() != b.size()) return false;
     984        Iterator<Collection<String>> itA = a.iterator();
     985        Iterator<Collection<String>> itB = b.iterator();
     986        while (itA.hasNext()) {
     987            if (!equalCollection(itA.next(), itB.next())) return false;
     988        }
     989        return true;
     990    }
     991
    865992    synchronized private void putArrayDefault(String key, Collection<Collection<String>> val) {
    866         key += ".";
    867         Collection<String> keys = getAllPrefixDefault(key).keySet();
    868         int num = 0;
    869         for(Collection<String> c : val) {
    870             keys.remove(key+num);
    871             putCollectionDefault(key+num++, c);
     993        arrayDefaults.put(key, val);
     994    }
     995
     996    public Collection<Map<String, String>> getListOfStructs(String key, Collection<Map<String, String>> def) {
     997        if (def != null) {
     998            putListOfStructsDefault(key, def);
    872999        }
    873         int l = key.length();
    874         for(String k : keys) {
    875             try {
    876                 Integer.valueOf(k.substring(l));
    877                 defaults.remove(k);
    878             } catch(Exception e) {
    879                 /* everything which does not end with a number should not be deleted */
     1000        Collection<Map<String, String>> prop = getListOfStructsInternal(key);
     1001        if (prop != null)
     1002            return prop;
     1003        else
     1004            return def;
     1005    }
     1006
     1007    public Collection<Map<String, String>> getListOfStructsInternal(String key) {
     1008        Collection<Map<String, String>> prop = listOfStructsProperties.get(key);
     1009        if (prop != null)
     1010            return prop;
     1011        else {
     1012            Collection<Collection<String>> array = getArrayInternal(key);
     1013            if (array == null) return null;
     1014            prop = new ArrayList<Map<String, String>>(array.size());
     1015            for (Collection<String> mapStr : array) {
     1016                Map<String, String> map = new LinkedHashMap<String, String>();
     1017                for (String key_value : mapStr) {
     1018                    final int i = key_value.indexOf(':');
     1019                    if (i == -1 || i == 0) {
     1020                        continue;
     1021                    }
     1022                    String k = key_value.substring(0,i);
     1023                    String v = key_value.substring(i+1);
     1024                    map.put(k, v);
     1025                }
     1026                prop.add(map);
    8801027            }
     1028            arrayProperties.remove(key);
     1029            listOfStructsProperties.put(key,prop);
     1030            return prop;
    8811031        }
    8821032    }
    8831033
     1034    public boolean putListOfStructs(String key, Collection<Map<String, String>> value) {
     1035        boolean changed = false;
     1036
     1037        Collection<Map<String, String>> oldValue;
     1038
     1039        synchronized (this) {
     1040            if (value == null) {
     1041                getListOfStructsInternal(key);
     1042                if (listOfStructsProperties.remove(key) != null) return false;
     1043            } else {
     1044                oldValue = getListOfStructsInternal(key);
     1045                if (equalListOfStructs(oldValue, value)) return false;
     1046
     1047                Collection<Map<String, String>> defValue = listOfStructsDefaults.get(key);
     1048                if (oldValue == null && equalListOfStructs(value, defValue)) return false;
     1049
     1050                Collection<Map<String, String>> valueCopy = new ArrayList<Map<String, String>>(value.size());
     1051                for (Map<String, String> map : value) {
     1052                    valueCopy.add(new LinkedHashMap<String,String>(map));
     1053                }
     1054                listOfStructsProperties.put(key, valueCopy);
     1055                try {
     1056                    save();
     1057                } catch(IOException e){
     1058                    System.out.println(tr("Warning: failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
     1059                }
     1060            }
     1061        }
     1062        // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
     1063//        firePreferenceChanged(key, oldValue, value);
     1064        return true;
     1065    }
     1066
     1067    private static boolean equalListOfStructs(Collection<Map<String, String>> a, Collection<Map<String, String>> b) {
     1068        if (a == null) return b == null;
     1069        if (b == null) return false;
     1070        if (a.size() != b.size()) return false;
     1071        Iterator<Map<String, String>> itA = a.iterator();
     1072        Iterator<Map<String, String>> itB = b.iterator();
     1073        while (itA.hasNext()) {
     1074            if (!equalMap(itA.next(), itB.next())) return false;
     1075        }
     1076        return true;
     1077    }
     1078
     1079    private static boolean equalMap(Map<String, String> a, Map<String, String> b) {
     1080        if (a == null) return b == null;
     1081        if (b == null) return false;
     1082        if (a.size() != b.size()) return false;
     1083        for (Entry<String, String> e : a.entrySet()) {
     1084            if (!Utils.equal(e.getValue(), b.get(e.getKey()))) return false;
     1085        }
     1086        return true;
     1087    }
     1088
     1089    synchronized private void putListOfStructsDefault(String key, Collection<Map<String, String>> val) {
     1090        listOfStructsDefaults.put(key, val);
     1091    }
     1092
    8841093    @Retention(RetentionPolicy.RUNTIME) public @interface pref { }
    8851094    @Retention(RetentionPolicy.RUNTIME) public @interface writeExplicitly { }
    8861095
     
    9101119     * same as above, but returns def if nothing was found
    9111120     */
    9121121    public <T> List<T> getListOfStructs(String key, Collection<T> def, Class<T> klass) {
    913         Collection<Collection<String>> array =
    914             getArray(key, def == null ? null : serializeListOfStructs(def, klass));
    915         if (array == null)
     1122        Collection<Map<String,String>> prop =
     1123            getListOfStructs(key, def == null ? null : serializeListOfStructs(def, klass));
     1124        if (prop == null)
    9161125            return def == null ? null : new ArrayList<T>(def);
    9171126        List<T> lst = new ArrayList<T>();
    918         for (Collection<String> entries : array) {
     1127        for (Map<String,String> entries : prop) {
    9191128            T struct = deserializeStruct(entries, klass);
    9201129            lst.add(struct);
    9211130        }
     
    9361145     * @return true if something has changed
    9371146     */
    9381147    public <T> boolean putListOfStructs(String key, Collection<T> val, Class<T> klass) {
    939         return putArray(key, serializeListOfStructs(val, klass));
     1148        return putListOfStructs(key, serializeListOfStructs(val, klass));
    9401149    }
    9411150
    942     private <T> Collection<Collection<String>> serializeListOfStructs(Collection<T> l, Class<T> klass) {
     1151    private <T> Collection<Map<String,String>> serializeListOfStructs(Collection<T> l, Class<T> klass) {
    9431152        if (l == null)
    9441153            return null;
    945         Collection<Collection<String>> vals = new ArrayList<Collection<String>>();
     1154        Collection<Map<String,String>> vals = new ArrayList<Map<String,String>>();
    9461155        for (T struct : l) {
    9471156            if (struct == null) {
    9481157                continue;
     
    9521161        return vals;
    9531162    }
    9541163
    955     private <T> Collection<String> serializeStruct(T struct, Class<T> klass) {
     1164    private <T> Map<String,String> serializeStruct(T struct, Class<T> klass) {
    9561165        T structPrototype;
    9571166        try {
    9581167            structPrototype = klass.newInstance();
     
    9621171            throw new RuntimeException(ex);
    9631172        }
    9641173
    965         Collection<String> hash = new ArrayList<String>();
     1174        Map<String,String> hash = new LinkedHashMap<String,String>();
    9661175        for (Field f : klass.getDeclaredFields()) {
    9671176            if (f.getAnnotation(pref.class) == null) {
    9681177                continue;
     
    9731182                Object defaultFieldValue = f.get(structPrototype);
    9741183                if (fieldValue != null) {
    9751184                    if (f.getAnnotation(writeExplicitly.class) != null || !Utils.equal(fieldValue, defaultFieldValue)) {
    976                         hash.add(String.format("%s:%s", f.getName().replace("_", "-"), fieldValue.toString()));
     1185                        hash.put(f.getName().replace("_", "-"), fieldValue.toString());
    9771186                    }
    9781187                }
    9791188            } catch (IllegalArgumentException ex) {
     
    9851194        return hash;
    9861195    }
    9871196
    988     private <T> T deserializeStruct(Collection<String> hash, Class<T> klass) {
     1197    private <T> T deserializeStruct(Map<String,String> hash, Class<T> klass) {
    9891198        T struct = null;
    9901199        try {
    9911200            struct = klass.newInstance();
     
    9941203        } catch (IllegalAccessException ex) {
    9951204            throw new RuntimeException();
    9961205        }
    997         for (String key_value : hash) {
    998             final int i = key_value.indexOf(':');
    999             if (i == -1 || i == 0) {
    1000                 continue;
    1001             }
    1002             String key = key_value.substring(0,i);
    1003             String valueString = key_value.substring(i+1);
    1004 
     1206        for (Entry<String,String> key_value : hash.entrySet()) {
    10051207            Object value = null;
    10061208            Field f;
    10071209            try {
    1008                 f = klass.getDeclaredField(key.replace("-", "_"));
     1210                f = klass.getDeclaredField(key_value.getKey().replace("-", "_"));
    10091211            } catch (NoSuchFieldException ex) {
    10101212                continue;
    10111213            } catch (SecurityException ex) {
     
    10161218            }
    10171219            f.setAccessible(true);
    10181220            if (f.getType() == Boolean.class || f.getType() == boolean.class) {
    1019                 value = Boolean.parseBoolean(valueString);
     1221                value = Boolean.parseBoolean(key_value.getValue());
    10201222            } else if (f.getType() == Integer.class || f.getType() == int.class) {
    10211223                try {
    1022                     value = Integer.parseInt(valueString);
     1224                    value = Integer.parseInt(key_value.getValue());
    10231225                } catch (NumberFormatException nfe) {
    10241226                    continue;
    10251227                }
    10261228            } else if (f.getType() == Double.class || f.getType() == double.class) {
    10271229                try {
    1028                     value = Double.parseDouble(valueString);
     1230                    value = Double.parseDouble(key_value.getValue());
    10291231                } catch (NumberFormatException nfe) {
    10301232                    continue;
    10311233                }
    10321234            } else  if (f.getType() == String.class) {
    1033                 value = valueString;
     1235                value = key_value.getValue();
    10341236            } else
    10351237                throw new RuntimeException("unsupported preference primitive type");
    10361238
     
    10791281        putCollection("pluginmanager.sites", sites);
    10801282    }
    10811283
    1082     public static class XMLTag {
    1083         public String key;
    1084         public String value;
     1284    protected XMLStreamReader parser;
     1285
     1286    public void validateXML(Reader in) throws Exception {
     1287        SchemaFactory factory =  SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
     1288        Schema schema = factory.newSchema(new StreamSource(new MirroredInputStream("resource://data/preferences.xsd")));
     1289        XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(in);
     1290        Validator validator = schema.newValidator();
     1291        validator.validate(new StAXSource(parser));
    10851292    }
    1086     public static class XMLCollection {
    1087         public String key;
     1293
     1294    public void fromXML(Reader in) throws XMLStreamException {
     1295        XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(in);
     1296        this.parser = parser;
     1297        parse();
    10881298    }
    1089     public static class XMLEntry {
    1090         public String value;
     1299
     1300    public void parse() throws XMLStreamException {
     1301        int event = parser.getEventType();
     1302        while (true) {
     1303            if (event == XMLStreamConstants.START_ELEMENT) {
     1304                parseRoot();
     1305            } else if (event == XMLStreamConstants.END_ELEMENT) {
     1306                return;
     1307            }
     1308            if (parser.hasNext()) {
     1309                event = parser.next();
     1310            } else {
     1311                break;
     1312            }
     1313        }
     1314        parser.close();
    10911315    }
    1092     public void fromXML(Reader in) throws SAXException {
    1093         XmlObjectParser parser = new XmlObjectParser();
    1094         parser.map("tag", XMLTag.class);
    1095         parser.map("entry", XMLEntry.class);
    1096         parser.map("collection", XMLCollection.class);
    1097         parser.startWithValidation(in,
    1098                 "http://josm.openstreetmap.de/preferences-1.0", "resource://data/preferences.xsd");
    1099         LinkedList<String> vals = new LinkedList<String>();
    1100         while(parser.hasNext()) {
    1101             Object o = parser.next();
    1102             if(o instanceof XMLTag) {
    1103                 properties.put(((XMLTag)o).key, ((XMLTag)o).value);
    1104             } else if (o instanceof XMLEntry) {
    1105                 vals.add(((XMLEntry)o).value);
    1106             } else if (o instanceof XMLCollection) {
    1107                 properties.put(((XMLCollection)o).key, Utils.join("\u001e", vals));
    1108                 vals = new LinkedList<String>();
     1316
     1317    public void parseRoot() throws XMLStreamException {
     1318        while (true) {
     1319            int event = parser.next();
     1320            if (event == XMLStreamConstants.START_ELEMENT) {
     1321                if (parser.getLocalName().equals("tag")) {
     1322                    properties.put(parser.getAttributeValue(null, "key"), parser.getAttributeValue(null, "value"));
     1323                    jumpToEnd();
     1324                } else if (parser.getLocalName().equals("list") || parser.getLocalName().equals("collection")) {
     1325                    parseToplevelList();
     1326                } else {
     1327                    throwException("Unexpected element: "+parser.getLocalName());
     1328                }
     1329            } else if (event == XMLStreamConstants.END_ELEMENT) {
     1330                return;
    11091331            }
    11101332        }
    11111333    }
    11121334
     1335    private void jumpToEnd() throws XMLStreamException {
     1336        while (true) {
     1337            int event = parser.next();
     1338            if (event == XMLStreamConstants.START_ELEMENT) {
     1339                jumpToEnd();
     1340            } else if (event == XMLStreamConstants.END_ELEMENT) {
     1341                return;
     1342            }
     1343        }
     1344    }
     1345
     1346    protected void parseToplevelList() throws XMLStreamException {
     1347        String key = parser.getAttributeValue(null, "key");
     1348        Collection<String> entries = null;
     1349        Collection<Collection<String>> lists = null;
     1350        Collection<Map<String, String>> maps = null;
     1351        while (true) {
     1352            int event = parser.next();
     1353            if (event == XMLStreamConstants.START_ELEMENT) {
     1354                if (parser.getLocalName().equals("entry")) {
     1355                    if (entries == null) {
     1356                        entries = new ArrayList<String>();
     1357                    }
     1358                    entries.add(parser.getAttributeValue(null, "value"));
     1359                    jumpToEnd();
     1360                } else if (parser.getLocalName().equals("list")) {
     1361                    if (lists == null) {
     1362                        lists = new ArrayList<Collection<String>>();
     1363                    }
     1364                    lists.add(parseInnerList());
     1365                } else if (parser.getLocalName().equals("map")) {
     1366                    if (maps == null) {
     1367                        maps = new ArrayList<Map<String, String>>();
     1368                    }
     1369                    maps.add(parseMap());
     1370                } else {
     1371                    throwException("Unexpected element: "+parser.getLocalName());
     1372                }
     1373            } else if (event == XMLStreamConstants.END_ELEMENT) {
     1374                break;
     1375            }
     1376        }
     1377        if (entries != null) {
     1378            collectionProperties.put(key, entries);
     1379        }
     1380        if (lists != null) {
     1381            arrayProperties.put(key, lists);
     1382        }
     1383        if (maps != null) {
     1384            listOfStructsProperties.put(key, maps);
     1385        }
     1386    }
     1387
     1388    protected Collection<String> parseInnerList() throws XMLStreamException {
     1389        Collection<String> entries = new ArrayList<String>();
     1390        while (true) {
     1391            int event = parser.next();
     1392            if (event == XMLStreamConstants.START_ELEMENT) {
     1393                if (parser.getLocalName().equals("entry")) {
     1394                    entries.add(parser.getAttributeValue(null, "value"));
     1395                    jumpToEnd();
     1396                } else {
     1397                    throwException("Unexpected element: "+parser.getLocalName());
     1398                }
     1399            } else if (event == XMLStreamConstants.END_ELEMENT) {
     1400                break;
     1401            }
     1402        }
     1403        return entries;
     1404    }
     1405
     1406    protected Map<String, String> parseMap() throws XMLStreamException {
     1407        Map<String, String> map = new LinkedHashMap<String, String>();
     1408        while (true) {
     1409            int event = parser.next();
     1410            if (event == XMLStreamConstants.START_ELEMENT) {
     1411                if (parser.getLocalName().equals("tag")) {
     1412                    map.put(parser.getAttributeValue(null, "key"), parser.getAttributeValue(null, "value"));
     1413                    jumpToEnd();
     1414                } else {
     1415                    throwException("Unexpected element: "+parser.getLocalName());
     1416                }
     1417            } else if (event == XMLStreamConstants.END_ELEMENT) {
     1418                break;
     1419            }
     1420        }
     1421        return map;
     1422    }
     1423
     1424    protected void throwException(String msg) {
     1425        throw new RuntimeException(msg + tr(" (at line {0}, column {1})", parser.getLocation().getLineNumber(), parser.getLocation().getColumnNumber()));
     1426    }
     1427
    11131428    public String toXML(boolean nopass) {
    11141429        StringBuilder b = new StringBuilder(
    11151430                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
     
    11251440            if(s == null || !s.equals(r)) {
    11261441                if(r.contains("\u001e"))
    11271442                {
    1128                     b.append(" <collection key='");
     1443                    b.append("  <list key='");
    11291444                    b.append(XmlWriter.encode(p.getKey()));
    11301445                    b.append("'>\n");
    11311446                    for (String val : r.split("\u001e", -1))
    11321447                    {
    1133                         b.append("  <entry value='");
     1448                        b.append("    <entry value='");
    11341449                        b.append(XmlWriter.encode(val));
    1135                         b.append("' />\n");
     1450                        b.append("'/>\n");
    11361451                    }
    1137                     b.append(" </collection>\n");
     1452                    b.append("  </list>\n");
    11381453                }
    11391454                else
    11401455                {
    1141                     b.append(" <tag key='");
     1456                    b.append("  <tag key='");
    11421457                    b.append(XmlWriter.encode(p.getKey()));
    11431458                    b.append("' value='");
    11441459                    b.append(XmlWriter.encode(p.getValue()));
    1145                     b.append("' />\n");
     1460                    b.append("'/>\n");
    11461461                }
    11471462            }
    11481463        }
    1149         b.append("</preferences>");
     1464        for (Entry<String, Collection<String>> listEntry : collectionProperties.entrySet()) {
     1465            b.append("  <list key='").append(XmlWriter.encode(listEntry.getKey())).append("'>\n");
     1466            for (String s : listEntry.getValue()) {
     1467                b.append("    <entry value='").append(XmlWriter.encode(s)).append("'/>\n");
     1468            }
     1469            b.append("  </list>\n");
     1470        }
     1471        for (Entry<String, Collection<Collection<String>>> arrayEntry : arrayProperties.entrySet()) {
     1472            b.append("  <list key='").append(XmlWriter.encode(arrayEntry.getKey())).append("'>\n");
     1473            for (Collection<String> list : arrayEntry.getValue()) {
     1474                b.append("    <list>\n");
     1475                for (String s : list) {
     1476                    b.append("      <entry value='").append(XmlWriter.encode(s)).append("'/>\n");
     1477                }
     1478                b.append("    </list>\n");
     1479            }
     1480            b.append("  </list>\n");
     1481        }
     1482        for (Entry<String, Collection<Map<String, String>>> listOfStructsEntry : listOfStructsProperties.entrySet()) {
     1483            b.append("  <list key='").append(XmlWriter.encode(listOfStructsEntry.getKey())).append("'>\n");
     1484            for (Map<String, String> struct : listOfStructsEntry.getValue()) {
     1485                b.append("    <map>\n");
     1486                for (Entry<String, String> e : struct.entrySet()) {
     1487                    b.append("      <tag key='").append(XmlWriter.encode(e.getKey())).append("' value='").append(XmlWriter.encode(e.getValue())).append("'/>\n");
     1488                }
     1489                b.append("    </map>\n");
     1490            }
     1491            b.append("  </list>\n");
     1492        }
     1493        b.append("</preferences>\n");
    11501494        return b.toString();
    11511495    }
    11521496}
  • src/org/openstreetmap/josm/data/ServerSidePreferences.java

     
    2121import java.util.Map.Entry;
    2222
    2323import javax.swing.JOptionPane;
     24import javax.xml.stream.XMLStreamException;
    2425
    2526import org.openstreetmap.josm.Main;
    2627import org.openstreetmap.josm.io.OsmConnection;
    2728import org.openstreetmap.josm.io.OsmTransferException;
    2829import org.openstreetmap.josm.tools.Base64;
    29 import org.xml.sax.SAXException;
    3030
    3131/**
    3232 * This class tweak the Preferences class to provide server side preference settings, as example
     
    170170            fromXML(in);
    171171        } catch (RuntimeException e) {
    172172            e.printStackTrace();
    173         } catch (SAXException e) {
     173        } catch (XMLStreamException e) {
    174174            e.printStackTrace();
    175175        }
    176176        return res;
  • data/preferences.xsd

     
    99                        <choice minOccurs="0" maxOccurs="unbounded">
    1010                                <element name="tag" type="tns:tag" />
    1111                                <element name="collection" type="tns:collection" />
     12                                <element name="list" type="tns:list" />
    1213                        </choice>
    1314                </sequence>
    1415                <attribute name="version" type="string" />
     
    1920                <attribute name="value" type="string" use="required"/>
    2021        </complexType>
    2122
     23    <!-- deprecated: remove mid 2012 -->
    2224        <complexType name="collection">
    2325                <sequence>
    2426                        <choice minOccurs="1" maxOccurs="unbounded">
     
    2830                <attribute name="key" type="string" use="required" />
    2931        </complexType>
    3032
     33        <complexType name="list">
     34            <choice>
     35            <sequence>
     36                            <element name="entry" type="tns:entry" minOccurs="0" maxOccurs="unbounded"/>
     37            </sequence>
     38            <sequence>
     39                            <element name="list" type="tns:slist" minOccurs="0" maxOccurs="unbounded"/>
     40            </sequence>
     41            <sequence>
     42                            <element name="map" type="tns:map" minOccurs="0" maxOccurs="unbounded"/>
     43            </sequence>
     44        </choice>
     45                <attribute name="key" type="string" use="required" />
     46        </complexType>
     47       
     48        <complexType name="slist">
     49        <sequence>
     50                    <element name="entry" type="tns:entry" minOccurs="0" maxOccurs="unbounded"/>
     51        </sequence>
     52        </complexType>
     53
     54        <complexType name="map">
     55        <sequence>
     56                    <element name="tag" type="tns:tag" minOccurs="0" maxOccurs="unbounded"/>
     57        </sequence>
     58        </complexType>
     59
    3160        <complexType name="entry">
    3261                <attribute name="value" type="string" use="required"/>
    3362        </complexType>