Changeset 4612 in josm


Ignore:
Timestamp:
26.11.2011 17:56:25 (6 months 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.