Changeset 4612 in josm


Ignore:
Timestamp:
2011-11-26T17:56:25+01:00 (13 years ago)
Author:
bastiK
Message:

see #7027 - internal structures for preferences (expect some problems next few days)

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/data/preferences.xsd

    r4553 r4612  
    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>
     
    2021        </complexType>
    2122
     23    <!-- deprecated: remove mid 2012 -->
    2224        <complexType name="collection">
    2325                <sequence>
     
    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"/>
  • trunk/src/org/openstreetmap/josm/data/Preferences.java

    r4592 r4612  
    2222import java.util.Collection;
    2323import java.util.Collections;
     24import java.util.Iterator;
     25import java.util.LinkedHashMap;
    2426import java.util.LinkedList;
    2527import java.util.List;
     
    3537import javax.swing.JOptionPane;
    3638import javax.swing.UIManager;
     39import javax.xml.parsers.SAXParserFactory;
     40import javax.xml.stream.XMLInputFactory;
     41import javax.xml.stream.XMLStreamConstants;
     42import javax.xml.stream.XMLStreamException;
     43import javax.xml.stream.XMLStreamReader;
     44import javax.xml.transform.stax.StAXSource;
     45import javax.xml.transform.stream.StreamSource;
     46import javax.xml.validation.Schema;
     47import javax.xml.validation.SchemaFactory;
     48import javax.xml.validation.Validator;
    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/**
     
    7587    protected final SortedMap<String, String> colornames = new TreeMap<String, String>();
    7688
    77     public interface PreferenceChangeEvent{
     89    protected final SortedMap<String, List<String>> collectionProperties = new TreeMap<String, List<String>>();
     90    protected final SortedMap<String, List<String>> collectionDefaults = new TreeMap<String, List<String>>();
     91
     92    protected final SortedMap<String, List<List<String>>> arrayProperties = new TreeMap<String, List<List<String>>>();
     93    protected final SortedMap<String, List<List<String>>> arrayDefaults = new TreeMap<String, List<List<String>>>();
     94
     95    protected final SortedMap<String, List<Map<String,String>>> listOfStructsProperties = new TreeMap<String, List<Map<String,String>>>();
     96    protected final SortedMap<String, List<Map<String,String>>> listOfStructsDefaults = new TreeMap<String, List<Map<String,String>>>();
     97
     98    public interface Setting<T> {
     99        T getValue();
     100        void visit(SettingVisitor visitor);
     101    }
     102
     103    abstract public static class AbstractSetting<T> implements Setting<T> {
     104        private T value;
     105        public AbstractSetting(T value) {
     106            this.value = value;
     107        }
     108        public T getValue() {
     109            return value;
     110        }
     111    }
     112
     113    public static class StringSetting extends AbstractSetting<String> {
     114        public StringSetting(String value) {
     115            super(value);
     116        }
     117        public void visit(SettingVisitor visitor) {
     118            visitor.visit(this);
     119        }
     120    }
     121
     122    public static class ListSetting extends AbstractSetting<List<String>> {
     123        public ListSetting(List<String> value) {
     124            super(value);
     125        }
     126        public void visit(SettingVisitor visitor) {
     127            visitor.visit(this);
     128        }
     129    }
     130
     131    public static class ListListSetting extends AbstractSetting<List<List<String>>> {
     132        public ListListSetting(List<List<String>> value) {
     133            super(value);
     134        }
     135        public void visit(SettingVisitor visitor) {
     136            visitor.visit(this);
     137        }
     138    }
     139
     140    public static class MapListSetting extends AbstractSetting<List<Map<String, String>>> {
     141        public MapListSetting(List<Map<String, String>> value) {
     142            super(value);
     143        }
     144        public void visit(SettingVisitor visitor) {
     145            visitor.visit(this);
     146        }
     147    }
     148
     149    public interface SettingVisitor {
     150        void visit(StringSetting setting);
     151        void visit(ListSetting value);
     152        void visit(ListListSetting value);
     153        void visit(MapListSetting value);
     154    }
     155
     156    public interface PreferenceChangeEvent<T> {
    78157        String getKey();
    79         String getOldValue();
    80         String getNewValue();
     158        Setting<T> getOldValue();
     159        Setting<T> getNewValue();
    81160    }
    82161
     
    85164    }
    86165
    87     private static class DefaultPreferenceChangeEvent implements PreferenceChangeEvent {
     166    private static class DefaultPreferenceChangeEvent<T> implements PreferenceChangeEvent<T> {
    88167        private final String key;
    89         private final String oldValue;
    90         private final String newValue;
    91 
    92         public DefaultPreferenceChangeEvent(String key, String oldValue, String newValue) {
     168        private final Setting<T> oldValue;
     169        private final Setting<T> newValue;
     170
     171        public DefaultPreferenceChangeEvent(String key, Setting<T> oldValue, Setting<T> newValue) {
    93172            this.key = key;
    94173            this.oldValue = oldValue;
     
    99178            return key;
    100179        }
    101         public String getOldValue() {
     180        public Setting<T> getOldValue() {
    102181            return oldValue;
    103182        }
    104         public String getNewValue() {
     183        public Setting<T> getNewValue() {
    105184            return newValue;
    106185        }
     
    125204    }
    126205
    127     protected void firePreferenceChanged(String key, String oldValue, String newValue) {
    128         PreferenceChangeEvent evt = new DefaultPreferenceChangeEvent(key, oldValue, newValue);
     206    protected <T> void firePreferenceChanged(String key, Setting<T> oldValue, Setting<T> newValue) {
     207        PreferenceChangeEvent<T> evt = new DefaultPreferenceChangeEvent<T>(key, oldValue, newValue);
    129208        for (PreferenceChangedListener l : listeners) {
    130209            l.preferenceChanged(evt);
     
    346425        if (changed) {
    347426            // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
    348             firePreferenceChanged(key, oldValue, value);
     427            firePreferenceChanged(key, new StringSetting(oldValue), new StringSetting(value));
    349428        }
    350429        return changed;
     
    446525    private void load(boolean old) throws Exception {
    447526        properties.clear();
    448         if(!Main.applet) {
    449             final BufferedReader in = new BufferedReader(new InputStreamReader(
    450                     new FileInputStream(old ? getOldPreferenceFile() : getPreferenceFile()), "utf-8"));
     527        if (!Main.applet) {
     528            File pref = old ? getOldPreferenceFile() : getPreferenceFile();
     529            BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(pref), "utf-8"));
    451530            /* FIXME: TODO: remove old style config file end of 2012 */
    452531            try {
     
    456535                    in.reset();
    457536                    if(v == '<') {
     537                        validateXML(in);
     538                        Utils.close(in);
     539                        in = new BufferedReader(new InputStreamReader(new FileInputStream(pref), "utf-8"));
    458540                        fromXML(in);
    459541                    } else {
     
    476558                    }
    477559                } else {
     560                    validateXML(in);
     561                    Utils.close(in);
     562                    in = new BufferedReader(new InputStreamReader(new FileInputStream(pref), "utf-8"));
    478563                    fromXML(in);
    479564                }
     
    732817    }
    733818
    734     synchronized public String getCollectionAsString(final String key) {
    735         String s = get(key);
    736         if(s != null && s.length() != 0) {
    737             s = s.replaceAll("\u001e",",");
    738         }
    739         return s;
    740     }
    741 
    742     public boolean isCollection(String key, boolean def) {
    743         String s = get(key);
    744         if (s != null && s.length() != 0)
    745             return s.indexOf("\u001e") >= 0;
    746             else
    747                 return def;
    748     }
    749 
    750819    /**
    751820     * Get a list of values for a certain key
     
    755824     *  def otherwise
    756825     */
    757     synchronized public Collection<String> getCollection(String key, Collection<String> def) {
    758         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;
     826    public Collection<String> getCollection(String key, Collection<String> def) {
     827        if (def != null) {
     828            putCollectionDefault(key, new ArrayList<String>(def));
     829        }
     830        Collection<String> prop = getCollectionInternal(key);
     831        if (prop != null)
     832            return prop;
     833        else
     834            return def;
    763835    }
    764836
     
    769841     *  an empty Collection otherwise.
    770842     */
    771     synchronized public Collection<String> getCollection(String key) {
     843    public Collection<String> getCollection(String key) {
    772844        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();
    777     }
    778 
    779     /* old style conversion, replace by above call after some transition time */
    780     /* remove this function, when no more old-style preference collections in the code */
    781     @Deprecated
    782     synchronized public Collection<String> getCollectionOld(String key, String sep) {
    783         putCollectionDefault(key, null);
    784         String s = get(key);
    785         if (s != null && s.length() != 0) {
    786             if(!s.contains("\u001e") && s.contains(sep)) {
    787                 s = s.replace(sep, "\u001e");
    788                 put(key, s);
    789             }
    790             return Arrays.asList(s.split("\u001e", -1));
    791         }
    792         return Collections.emptyList();
     845        Collection<String> prop = getCollectionInternal(key);
     846        if (prop != null)
     847            return prop;
     848        else
     849            return Collections.emptyList();
     850    }
     851
     852    synchronized private List<String> getCollectionInternal(String key) {
     853        List<String> prop = collectionProperties.get(key);
     854        if (prop != null)
     855            return prop;
     856        else {
     857            String s = properties.get(key);
     858            if(s != null) {
     859                prop = Arrays.asList(s.split("\u001e", -1));
     860                collectionProperties.put(key, Collections.unmodifiableList(prop));
     861                properties.remove(key);
     862                defaults.remove(key);
     863                return prop;
     864            }
     865        }
     866        return null;
    793867    }
    794868
     
    799873    }
    800874
    801     synchronized public boolean putCollection(String key, Collection<String> val) {
    802         return put(key, Utils.join("\u001e", val));
     875    public boolean putCollection(String key, Collection<String> value) {
     876        List<String> oldValue = null;
     877        List<String> valueCopy = null;
     878
     879        synchronized (this) {
     880            if (value == null) {
     881                oldValue = collectionProperties.remove(key);
     882                boolean changed = oldValue != null;
     883                changed |= properties.remove(key) != null;
     884                if (!changed) return false;
     885            } else {
     886                oldValue = getCollectionInternal(key);
     887                if (equalCollection(value, oldValue)) return false;
     888                Collection<String> defValue = collectionDefaults.get(key);
     889                if (oldValue == null && equalCollection(value, defValue)) return false;
     890
     891                valueCopy = new ArrayList<String>(value);
     892                collectionProperties.put(key, Collections.unmodifiableList(valueCopy));
     893                try {
     894                    save();
     895                } catch(IOException e){
     896                    System.out.println(tr("Warning: failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
     897                }
     898            }
     899        }
     900        // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
     901        firePreferenceChanged(key, new ListSetting(oldValue), new ListSetting(valueCopy));
     902        return true;
     903    }
     904
     905    private static boolean equalCollection(Collection<String> a, Collection<String> b) {
     906        if (a == null) return b == null;
     907        if (b == null) return false;
     908        if (a.size() != b.size()) return false;
     909        Iterator<String> itA = a.iterator();
     910        Iterator<String> itB = b.iterator();
     911        while (itA.hasNext()) {
     912            String aStr = itA.next();
     913            String bStr = itB.next();
     914            if (!Utils.equal(aStr,bStr)) return false;
     915        }
     916        return true;
    803917    }
    804918
     
    817931    }
    818932
    819     synchronized private void putCollectionDefault(String key, Collection<String> val) {
    820         putDefault(key, Utils.join("\u001e", val));
     933    synchronized private void putCollectionDefault(String key, List<String> val) {
     934        collectionDefaults.put(key, val);
    821935    }
    822936
     
    825939     * If not a single entry could be found, def is returned.
    826940     */
    827     synchronized public Collection<Collection<String>> getArray(String key,
    828             Collection<Collection<String>> def)
    829             {
    830         if(def != null) {
    831             putArrayDefault(key, def);
    832         }
    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             }
    841 
    842     synchronized public boolean putArray(String key, Collection<Collection<String>> val) {
     941    synchronized public Collection<Collection<String>> getArray(String key, Collection<Collection<String>> def) {
     942        if (def != null) {
     943            List<List<String>> defCopy = new ArrayList<List<String>>(def.size());
     944            for (Collection<String> lst : def) {
     945                defCopy.add(Collections.unmodifiableList(new ArrayList<String>(lst)));
     946            }
     947            putArrayDefault(key, Collections.unmodifiableList(defCopy));
     948        }
     949        List<List<String>> prop = getArrayInternal(key);
     950        if (prop != null) {
     951            @SuppressWarnings("unchecked")
     952            Collection<Collection<String>> prop_cast = (Collection) prop;
     953            return prop_cast;
     954        } else
     955            return def;
     956    }
     957
     958    synchronized private List<List<String>> getArrayInternal(String key) {
     959        List<List<String>> prop = arrayProperties.get(key);
     960        if (prop != null)
     961            return prop;
     962        else {
     963            String keyDot = key + ".";
     964            int num = 0;
     965            List<List<String>> col = new ArrayList<List<String>>();
     966            while (true) {
     967                List<String> c = getCollectionInternal(keyDot+num);
     968                if (c == null) {
     969                    break;
     970                }
     971                col.add(c);
     972                collectionProperties.remove(keyDot+num);
     973                collectionDefaults.remove(keyDot+num);
     974                num++;
     975            }
     976            if (num > 0) {
     977                arrayProperties.put(key, Collections.unmodifiableList(col));
     978                return col;
     979            }
     980        }
     981        return null;
     982    }
     983
     984    public boolean putArray(String key, Collection<Collection<String>> value) {
    843985        boolean changed = false;
    844         key += ".";
    845         Collection<String> keys = getAllPrefix(key).keySet();
    846         if(val != null) {
    847             int num = 0;
    848             for(Collection<String> c : val) {
    849                 keys.remove(key+num);
    850                 changed |= putCollection(key+num++, c);
    851             }
    852         }
    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 */
    860             }
    861         }
    862         return changed;
    863     }
    864 
    865     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);
    872         }
    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 */
    880             }
    881         }
     986
     987        List<List<String>> oldValue = null;
     988        List<List<String>> valueCopy = null;
     989
     990        synchronized (this) {
     991            if (value == null) {
     992                oldValue = getArrayInternal(key);
     993                if (arrayProperties.remove(key) != null) return false;
     994            } else {
     995                oldValue = getArrayInternal(key);
     996                if (equalArray(value, oldValue)) return false;
     997
     998                List<List<String>> defValue = arrayDefaults.get(key);
     999                if (oldValue == null && equalArray(value, defValue)) return false;
     1000
     1001                valueCopy = new ArrayList<List<String>>(value.size());
     1002                for (Collection<String> lst : value) {
     1003                    valueCopy.add(Collections.unmodifiableList(new ArrayList<String>(lst)));
     1004                }
     1005                arrayProperties.put(key, Collections.unmodifiableList(valueCopy));
     1006                try {
     1007                    save();
     1008                } catch(IOException e){
     1009                    System.out.println(tr("Warning: failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
     1010                }
     1011            }
     1012        }
     1013        // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
     1014        firePreferenceChanged(key, new ListListSetting(oldValue), new ListListSetting(valueCopy));
     1015        return true;
     1016    }
     1017
     1018    private static boolean equalArray(Collection<Collection<String>> a, Collection<List<String>> b) {
     1019        if (a == null) return b == null;
     1020        if (b == null) return false;
     1021        if (a.size() != b.size()) return false;
     1022        Iterator<Collection<String>> itA = a.iterator();
     1023        Iterator<List<String>> itB = b.iterator();
     1024        while (itA.hasNext()) {
     1025            if (!equalCollection(itA.next(), itB.next())) return false;
     1026        }
     1027        return true;
     1028    }
     1029
     1030    synchronized private void putArrayDefault(String key, List<List<String>> val) {
     1031        arrayDefaults.put(key, val);
     1032    }
     1033
     1034    public Collection<Map<String, String>> getListOfStructs(String key, Collection<Map<String, String>> def) {
     1035        if (def != null) {
     1036            List<Map<String, String>> defCopy = new ArrayList<Map<String, String>>(def.size());
     1037            for (Map<String, String> map : def) {
     1038                defCopy.add(Collections.unmodifiableMap(new LinkedHashMap<String,String>(map)));
     1039            }
     1040            putListOfStructsDefault(key, Collections.unmodifiableList(defCopy));
     1041        }
     1042        Collection<Map<String, String>> prop = getListOfStructsInternal(key);
     1043        if (prop != null)
     1044            return prop;
     1045        else
     1046            return def;
     1047    }
     1048
     1049    private synchronized List<Map<String, String>> getListOfStructsInternal(String key) {
     1050        List<Map<String, String>> prop = listOfStructsProperties.get(key);
     1051        if (prop != null)
     1052            return prop;
     1053        else {
     1054            List<List<String>> array = getArrayInternal(key);
     1055            if (array == null) return null;
     1056            prop = new ArrayList<Map<String, String>>(array.size());
     1057            for (Collection<String> mapStr : array) {
     1058                Map<String, String> map = new LinkedHashMap<String, String>();
     1059                for (String key_value : mapStr) {
     1060                    final int i = key_value.indexOf(':');
     1061                    if (i == -1 || i == 0) {
     1062                        continue;
     1063                    }
     1064                    String k = key_value.substring(0,i);
     1065                    String v = key_value.substring(i+1);
     1066                    map.put(k, v);
     1067                }
     1068                prop.add(Collections.unmodifiableMap(map));
     1069            }
     1070            arrayProperties.remove(key);
     1071            arrayDefaults.remove(key);
     1072            listOfStructsProperties.put(key, Collections.unmodifiableList(prop));
     1073            return prop;
     1074        }
     1075    }
     1076
     1077    public boolean putListOfStructs(String key, Collection<Map<String, String>> value) {
     1078        boolean changed = false;
     1079
     1080        List<Map<String, String>> oldValue;
     1081        List<Map<String, String>> valueCopy = null;
     1082
     1083        synchronized (this) {
     1084            if (value == null) {
     1085                oldValue = getListOfStructsInternal(key);
     1086                if (listOfStructsProperties.remove(key) != null) return false;
     1087            } else {
     1088                oldValue = getListOfStructsInternal(key);
     1089                if (equalListOfStructs(oldValue, value)) return false;
     1090
     1091                List<Map<String, String>> defValue = listOfStructsDefaults.get(key);
     1092                if (oldValue == null && equalListOfStructs(value, defValue)) return false;
     1093
     1094                valueCopy = new ArrayList<Map<String, String>>(value.size());
     1095                for (Map<String, String> map : value) {
     1096                    valueCopy.add(Collections.unmodifiableMap(new LinkedHashMap<String,String>(map)));
     1097                }
     1098                listOfStructsProperties.put(key, Collections.unmodifiableList(valueCopy));
     1099                try {
     1100                    save();
     1101                } catch(IOException e){
     1102                    System.out.println(tr("Warning: failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
     1103                }
     1104            }
     1105        }
     1106        // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
     1107        firePreferenceChanged(key, new MapListSetting(oldValue), new MapListSetting(valueCopy));
     1108        return true;
     1109    }
     1110
     1111    private static boolean equalListOfStructs(Collection<Map<String, String>> a, Collection<Map<String, String>> b) {
     1112        if (a == null) return b == null;
     1113        if (b == null) return false;
     1114        if (a.size() != b.size()) return false;
     1115        Iterator<Map<String, String>> itA = a.iterator();
     1116        Iterator<Map<String, String>> itB = b.iterator();
     1117        while (itA.hasNext()) {
     1118            if (!equalMap(itA.next(), itB.next())) return false;
     1119        }
     1120        return true;
     1121    }
     1122
     1123    private static boolean equalMap(Map<String, String> a, Map<String, String> b) {
     1124        if (a == null) return b == null;
     1125        if (b == null) return false;
     1126        if (a.size() != b.size()) return false;
     1127        for (Entry<String, String> e : a.entrySet()) {
     1128            if (!Utils.equal(e.getValue(), b.get(e.getKey()))) return false;
     1129        }
     1130        return true;
     1131    }
     1132
     1133    synchronized private void putListOfStructsDefault(String key, List<Map<String, String>> val) {
     1134        listOfStructsDefaults.put(key, val);
    8821135    }
    8831136
     
    9111164     */
    9121165    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)
     1166        Collection<Map<String,String>> prop =
     1167            getListOfStructs(key, def == null ? null : serializeListOfStructs(def, klass));
     1168        if (prop == null)
    9161169            return def == null ? null : new ArrayList<T>(def);
    9171170        List<T> lst = new ArrayList<T>();
    918         for (Collection<String> entries : array) {
     1171        for (Map<String,String> entries : prop) {
    9191172            T struct = deserializeStruct(entries, klass);
    9201173            lst.add(struct);
     
    9371190     */
    9381191    public <T> boolean putListOfStructs(String key, Collection<T> val, Class<T> klass) {
    939         return putArray(key, serializeListOfStructs(val, klass));
    940     }
    941 
    942     private <T> Collection<Collection<String>> serializeListOfStructs(Collection<T> l, Class<T> klass) {
     1192        return putListOfStructs(key, serializeListOfStructs(val, klass));
     1193    }
     1194
     1195    private <T> Collection<Map<String,String>> serializeListOfStructs(Collection<T> l, Class<T> klass) {
    9431196        if (l == null)
    9441197            return null;
    945         Collection<Collection<String>> vals = new ArrayList<Collection<String>>();
     1198        Collection<Map<String,String>> vals = new ArrayList<Map<String,String>>();
    9461199        for (T struct : l) {
    9471200            if (struct == null) {
     
    9531206    }
    9541207
    955     private <T> Collection<String> serializeStruct(T struct, Class<T> klass) {
     1208    private <T> Map<String,String> serializeStruct(T struct, Class<T> klass) {
    9561209        T structPrototype;
    9571210        try {
     
    9631216        }
    9641217
    965         Collection<String> hash = new ArrayList<String>();
     1218        Map<String,String> hash = new LinkedHashMap<String,String>();
    9661219        for (Field f : klass.getDeclaredFields()) {
    9671220            if (f.getAnnotation(pref.class) == null) {
     
    9741227                if (fieldValue != null) {
    9751228                    if (f.getAnnotation(writeExplicitly.class) != null || !Utils.equal(fieldValue, defaultFieldValue)) {
    976                         hash.add(String.format("%s:%s", f.getName().replace("_", "-"), fieldValue.toString()));
     1229                        hash.put(f.getName().replace("_", "-"), fieldValue.toString());
    9771230                    }
    9781231                }
     
    9861239    }
    9871240
    988     private <T> T deserializeStruct(Collection<String> hash, Class<T> klass) {
     1241    private <T> T deserializeStruct(Map<String,String> hash, Class<T> klass) {
    9891242        T struct = null;
    9901243        try {
     
    9951248            throw new RuntimeException();
    9961249        }
    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 
     1250        for (Entry<String,String> key_value : hash.entrySet()) {
    10051251            Object value = null;
    10061252            Field f;
    10071253            try {
    1008                 f = klass.getDeclaredField(key.replace("-", "_"));
     1254                f = klass.getDeclaredField(key_value.getKey().replace("-", "_"));
    10091255            } catch (NoSuchFieldException ex) {
    10101256                continue;
     
    10171263            f.setAccessible(true);
    10181264            if (f.getType() == Boolean.class || f.getType() == boolean.class) {
    1019                 value = Boolean.parseBoolean(valueString);
     1265                value = Boolean.parseBoolean(key_value.getValue());
    10201266            } else if (f.getType() == Integer.class || f.getType() == int.class) {
    10211267                try {
    1022                     value = Integer.parseInt(valueString);
     1268                    value = Integer.parseInt(key_value.getValue());
    10231269                } catch (NumberFormatException nfe) {
    10241270                    continue;
     
    10261272            } else if (f.getType() == Double.class || f.getType() == double.class) {
    10271273                try {
    1028                     value = Double.parseDouble(valueString);
     1274                    value = Double.parseDouble(key_value.getValue());
    10291275                } catch (NumberFormatException nfe) {
    10301276                    continue;
    10311277                }
    10321278            } else  if (f.getType() == String.class) {
    1033                 value = valueString;
     1279                value = key_value.getValue();
    10341280            } else
    10351281                throw new RuntimeException("unsupported preference primitive type");
     
    10801326    }
    10811327
    1082     public static class XMLTag {
    1083         public String key;
    1084         public String value;
    1085     }
    1086     public static class XMLCollection {
    1087         public String key;
    1088     }
    1089     public static class XMLEntry {
    1090         public String value;
    1091     }
    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>();
    1109             }
     1328    protected XMLStreamReader parser;
     1329
     1330    public void validateXML(Reader in) throws Exception {
     1331        SchemaFactory factory =  SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
     1332        Schema schema = factory.newSchema(new StreamSource(new MirroredInputStream("resource://data/preferences.xsd")));
     1333        XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(in);
     1334        Validator validator = schema.newValidator();
     1335        validator.validate(new StAXSource(parser));
     1336    }
     1337
     1338    public void fromXML(Reader in) throws XMLStreamException {
     1339        XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(in);
     1340        this.parser = parser;
     1341        parse();
     1342    }
     1343
     1344    public void parse() throws XMLStreamException {
     1345        int event = parser.getEventType();
     1346        while (true) {
     1347            if (event == XMLStreamConstants.START_ELEMENT) {
     1348                parseRoot();
     1349            } else if (event == XMLStreamConstants.END_ELEMENT) {
     1350                return;
     1351            }
     1352            if (parser.hasNext()) {
     1353                event = parser.next();
     1354            } else {
     1355                break;
     1356            }
     1357        }
     1358        parser.close();
     1359    }
     1360
     1361    public void parseRoot() throws XMLStreamException {
     1362        while (true) {
     1363            int event = parser.next();
     1364            if (event == XMLStreamConstants.START_ELEMENT) {
     1365                if (parser.getLocalName().equals("tag")) {
     1366                    properties.put(parser.getAttributeValue(null, "key"), parser.getAttributeValue(null, "value"));
     1367                    jumpToEnd();
     1368                } else if (parser.getLocalName().equals("list") || parser.getLocalName().equals("collection")) {
     1369                    parseToplevelList();
     1370                } else {
     1371                    throwException("Unexpected element: "+parser.getLocalName());
     1372                }
     1373            } else if (event == XMLStreamConstants.END_ELEMENT) {
     1374                return;
     1375            }
     1376        }
     1377    }
     1378
     1379    private void jumpToEnd() throws XMLStreamException {
     1380        while (true) {
     1381            int event = parser.next();
     1382            if (event == XMLStreamConstants.START_ELEMENT) {
     1383                jumpToEnd();
     1384            } else if (event == XMLStreamConstants.END_ELEMENT) {
     1385                return;
     1386            }
     1387        }
     1388    }
     1389
     1390    protected void parseToplevelList() throws XMLStreamException {
     1391        String key = parser.getAttributeValue(null, "key");
     1392        List<String> entries = null;
     1393        List<List<String>> lists = null;
     1394        List<Map<String, String>> maps = null;
     1395        while (true) {
     1396            int event = parser.next();
     1397            if (event == XMLStreamConstants.START_ELEMENT) {
     1398                if (parser.getLocalName().equals("entry")) {
     1399                    if (entries == null) {
     1400                        entries = new ArrayList<String>();
     1401                    }
     1402                    entries.add(parser.getAttributeValue(null, "value"));
     1403                    jumpToEnd();
     1404                } else if (parser.getLocalName().equals("list")) {
     1405                    if (lists == null) {
     1406                        lists = new ArrayList<List<String>>();
     1407                    }
     1408                    lists.add(parseInnerList());
     1409                } else if (parser.getLocalName().equals("map")) {
     1410                    if (maps == null) {
     1411                        maps = new ArrayList<Map<String, String>>();
     1412                    }
     1413                    maps.add(parseMap());
     1414                } else {
     1415                    throwException("Unexpected element: "+parser.getLocalName());
     1416                }
     1417            } else if (event == XMLStreamConstants.END_ELEMENT) {
     1418                break;
     1419            }
     1420        }
     1421        if (entries != null) {
     1422            collectionProperties.put(key, Collections.unmodifiableList(entries));
     1423        }
     1424        if (lists != null) {
     1425            arrayProperties.put(key, Collections.unmodifiableList(lists));
     1426        }
     1427        if (maps != null) {
     1428            listOfStructsProperties.put(key, Collections.unmodifiableList(maps));
     1429        }
     1430    }
     1431
     1432    protected List<String> parseInnerList() throws XMLStreamException {
     1433        List<String> entries = new ArrayList<String>();
     1434        while (true) {
     1435            int event = parser.next();
     1436            if (event == XMLStreamConstants.START_ELEMENT) {
     1437                if (parser.getLocalName().equals("entry")) {
     1438                    entries.add(parser.getAttributeValue(null, "value"));
     1439                    jumpToEnd();
     1440                } else {
     1441                    throwException("Unexpected element: "+parser.getLocalName());
     1442                }
     1443            } else if (event == XMLStreamConstants.END_ELEMENT) {
     1444                break;
     1445            }
     1446        }
     1447        return Collections.unmodifiableList(entries);
     1448    }
     1449
     1450    protected Map<String, String> parseMap() throws XMLStreamException {
     1451        Map<String, String> map = new LinkedHashMap<String, String>();
     1452        while (true) {
     1453            int event = parser.next();
     1454            if (event == XMLStreamConstants.START_ELEMENT) {
     1455                if (parser.getLocalName().equals("tag")) {
     1456                    map.put(parser.getAttributeValue(null, "key"), parser.getAttributeValue(null, "value"));
     1457                    jumpToEnd();
     1458                } else {
     1459                    throwException("Unexpected element: "+parser.getLocalName());
     1460                }
     1461            } else if (event == XMLStreamConstants.END_ELEMENT) {
     1462                break;
     1463            }
     1464        }
     1465        return Collections.unmodifiableMap(map);
     1466    }
     1467
     1468    protected void throwException(String msg) {
     1469        throw new RuntimeException(msg + tr(" (at line {0}, column {1})", parser.getLocation().getLineNumber(), parser.getLocation().getColumnNumber()));
     1470    }
     1471
     1472    private class SettingToXml implements SettingVisitor {
     1473        private StringBuilder b;
     1474        private boolean noPassword;
     1475        private String key;
     1476
     1477        public SettingToXml(StringBuilder b, boolean noPassword) {
     1478            this.b = b;
     1479            this.noPassword = noPassword;
     1480        }
     1481
     1482        public void setKey(String key) {
     1483            this.key = key;
     1484        }
     1485
     1486        public void visit(StringSetting setting) {
     1487            if (noPassword && key.equals("osm-server.password"))
     1488                return; // do not store plain password.
     1489            String r = setting.getValue();
     1490            String s = defaults.get(key);
     1491            /* don't save default values */
     1492            if(s == null || !s.equals(r)) {
     1493                if(r.contains("\u001e"))
     1494                {
     1495                    b.append("  <list key='");
     1496                    b.append(XmlWriter.encode(key));
     1497                    b.append("'>\n");
     1498                    for (String val : r.split("\u001e", -1))
     1499                    {
     1500                        b.append("    <entry value='");
     1501                        b.append(XmlWriter.encode(val));
     1502                        b.append("'/>\n");
     1503                    }
     1504                    b.append("  </list>\n");
     1505                }
     1506                else
     1507                {
     1508                    b.append("  <tag key='");
     1509                    b.append(XmlWriter.encode(key));
     1510                    b.append("' value='");
     1511                    b.append(XmlWriter.encode(setting.getValue()));
     1512                    b.append("'/>\n");
     1513                }
     1514            }
     1515        }
     1516
     1517        public void visit(ListSetting setting) {
     1518            b.append("  <list key='").append(XmlWriter.encode(key)).append("'>\n");
     1519            for (String s : setting.getValue()) {
     1520                b.append("    <entry value='").append(XmlWriter.encode(s)).append("'/>\n");
     1521            }
     1522            b.append("  </list>\n");
     1523        }
     1524
     1525        public void visit(ListListSetting setting) {
     1526            b.append("  <list key='").append(XmlWriter.encode(key)).append("'>\n");
     1527            for (List<String> list : setting.getValue()) {
     1528                b.append("    <list>\n");
     1529                for (String s : list) {
     1530                    b.append("      <entry value='").append(XmlWriter.encode(s)).append("'/>\n");
     1531                }
     1532                b.append("    </list>\n");
     1533            }
     1534            b.append("  </list>\n");
     1535        }
     1536
     1537        public void visit(MapListSetting setting) {
     1538            b.append("  <list key='").append(XmlWriter.encode(key)).append("'>\n");
     1539            for (Map<String, String> struct : setting.getValue()) {
     1540                b.append("    <map>\n");
     1541                for (Entry<String, String> e : struct.entrySet()) {
     1542                    b.append("      <tag key='").append(XmlWriter.encode(e.getKey())).append("' value='").append(XmlWriter.encode(e.getValue())).append("'/>\n");
     1543                }
     1544                b.append("    </map>\n");
     1545            }
     1546            b.append("  </list>\n");
    11101547        }
    11111548    }
     
    11161553                "<preferences xmlns=\"http://josm.openstreetmap.de/preferences-1.0\" version=\""+
    11171554                Version.getInstance().getVersion() + "\">\n");
    1118         for (Entry<String, String> p : properties.entrySet()) {
    1119             if (nopass && p.getKey().equals("osm-server.password")) {
    1120                 continue; // do not store plain password.
    1121             }
    1122             String r = p.getValue();
    1123             String s = defaults.get(p.getKey());
    1124             /* don't save default values */
    1125             if(s == null || !s.equals(r)) {
    1126                 if(r.contains("\u001e"))
    1127                 {
    1128                     b.append(" <collection key='");
    1129                     b.append(XmlWriter.encode(p.getKey()));
    1130                     b.append("'>\n");
    1131                     for (String val : r.split("\u001e", -1))
    1132                     {
    1133                         b.append("  <entry value='");
    1134                         b.append(XmlWriter.encode(val));
    1135                         b.append("' />\n");
    1136                     }
    1137                     b.append(" </collection>\n");
    1138                 }
    1139                 else
    1140                 {
    1141                     b.append(" <tag key='");
    1142                     b.append(XmlWriter.encode(p.getKey()));
    1143                     b.append("' value='");
    1144                     b.append(XmlWriter.encode(p.getValue()));
    1145                     b.append("' />\n");
    1146                 }
    1147             }
    1148         }
    1149         b.append("</preferences>");
     1555        SettingToXml toXml = new SettingToXml(b, nopass);
     1556        Map<String, Setting> settings = new TreeMap<String, Setting>();
     1557
     1558        for (Entry<String, String> e : properties.entrySet()) {
     1559            settings.put(e.getKey(), new StringSetting(e.getValue()));
     1560        }
     1561        for (Entry<String, List<String>> e : collectionProperties.entrySet()) {
     1562            settings.put(e.getKey(), new ListSetting(e.getValue()));
     1563        }
     1564        for (Entry<String, List<List<String>>> e : arrayProperties.entrySet()) {
     1565            settings.put(e.getKey(), new ListListSetting(e.getValue()));
     1566        }
     1567        for (Entry<String, List<Map<String, String>>> e : listOfStructsProperties.entrySet()) {
     1568            settings.put(e.getKey(), new MapListSetting(e.getValue()));
     1569        }
     1570        for (Entry<String, Setting> e : settings.entrySet()) {
     1571            toXml.setKey(e.getKey());
     1572            e.getValue().visit(toXml);
     1573        }
     1574        b.append("</preferences>\n");
    11501575        return b.toString();
    11511576    }
  • trunk/src/org/openstreetmap/josm/data/ServerSidePreferences.java

    r4191 r4612  
    2222
    2323import javax.swing.JOptionPane;
     24import javax.xml.stream.XMLStreamException;
    2425
    2526import org.openstreetmap.josm.Main;
     
    2728import org.openstreetmap.josm.io.OsmTransferException;
    2829import org.openstreetmap.josm.tools.Base64;
    29 import org.xml.sax.SAXException;
    3030
    3131/**
     
    171171        } catch (RuntimeException e) {
    172172            e.printStackTrace();
    173         } catch (SAXException e) {
     173        } catch (XMLStreamException e) {
    174174            e.printStackTrace();
    175175        }
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r4431 r4612  
    608608     */
    609609    static {
    610         // Legacy support - convert list of keys to search pattern
    611         if (Main.pref.isCollection("tags.direction", false)) {
    612             System.out.println("Collection of keys in tags.direction is no longer supported, value will converted to search pattern");
    613             Collection<String> keys = Main.pref.getCollection("tags.direction", null);
    614             StringBuilder builder = new StringBuilder();
    615             for (String key:keys) {
    616                 builder.append(key);
    617                 builder.append("=* | ");
    618             }
    619             builder.delete(builder.length() - 3, builder.length());
    620             Main.pref.put("tags.direction", builder.toString());
    621         }
    622 
    623610        // FIXME: incline=\"-*\" search pattern does not work.
    624611        String reversedDirectionDefault = "oneway=\"-1\" | incline=down | incline=\"-*\"";
  • trunk/src/org/openstreetmap/josm/gui/JosmUserIdentityManager.java

    r4263 r4612  
    99import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
    1010import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
     11import org.openstreetmap.josm.data.Preferences.StringSetting;
    1112import org.openstreetmap.josm.data.osm.UserInfo;
    1213import org.openstreetmap.josm.io.auth.CredentialsManager;
     
    202203    public void preferenceChanged(PreferenceChangeEvent evt) {
    203204        if (evt.getKey().equals("osm-server.username")) {
    204             String newValue = evt.getNewValue();
     205            if (!(evt.getNewValue() instanceof StringSetting)) return;
     206            String newValue = ((StringSetting) evt.getNewValue()).getValue();
    205207            if (newValue == null || newValue.trim().length() == 0) {
    206208                setAnonymous();
     
    214216
    215217        if (evt.getKey().equals("osm-server.url")) {
    216             String newValue = evt.getNewValue();
     218            if (!(evt.getNewValue() instanceof StringSetting)) return;
     219            String newValue = ((StringSetting) evt.getNewValue()).getValue();
    217220            if (newValue == null || newValue.trim().equals("")) {
    218221                setAnonymous();
  • trunk/src/org/openstreetmap/josm/io/MirroredInputStream.java

    r4343 r4612  
    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;
     
    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;
     
    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;
  • trunk/src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java

    r4087 r4612  
    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) {
Note: See TracChangeset for help on using the changeset viewer.