Changeset 6578 in josm for trunk


Ignore:
Timestamp:
2013-12-31T13:04:42+01:00 (11 years ago)
Author:
bastiK
Message:

Preferences rework. Merges the 4 maps for the 4 setting
types into a single map. The code for the get...() and set...()
methods is also unified.
Additional consistency checks.

Location:
trunk/src/org/openstreetmap/josm
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/CustomConfigurator.java

    r6552 r6578  
    3939
    4040import org.openstreetmap.josm.Main;
     41import org.openstreetmap.josm.data.Preferences.ListListSetting;
     42import org.openstreetmap.josm.data.Preferences.ListSetting;
     43import org.openstreetmap.josm.data.Preferences.MapListSetting;
    4144import org.openstreetmap.josm.data.Preferences.Setting;
     45import org.openstreetmap.josm.data.Preferences.StringSetting;
    4246import org.openstreetmap.josm.gui.io.DownloadFileTask;
    4347import org.openstreetmap.josm.plugins.PluginDownloadTask;
     
    276280
    277281
    278         public static void deleteFile(String path, String base) {
     282    public static void deleteFile(String path, String base) {
    279283        String dir = getDirectoryByAbbr(base);
    280284        if (dir==null) {
     
    290294            deleteFileOrDirectory(fOut);
    291295        }
    292         return;
    293296    }
    294297
     
    401404    public static Preferences clonePreferences(Preferences pref) {
    402405        Preferences tmp = new Preferences();
    403         tmp.defaults.putAll(   pref.defaults );
    404         tmp.properties.putAll( pref.properties );
    405         tmp.arrayDefaults.putAll(   pref.arrayDefaults );
    406         tmp.arrayProperties.putAll( pref.arrayProperties );
    407         tmp.collectionDefaults.putAll(   pref.collectionDefaults );
    408         tmp.collectionProperties.putAll( pref.collectionProperties );
    409         tmp.listOfStructsDefaults.putAll(   pref.listOfStructsDefaults );
    410         tmp.listOfStructsProperties.putAll( pref.listOfStructsProperties );
     406        tmp.settingsMap.putAll(pref.settingsMap);
     407        tmp.defaultsMap.putAll(pref.defaultsMap);
    411408        tmp.colornames.putAll( pref.colornames );
    412409
     
    738735
    739736    /**
    740      * Helper class to do specific Prefrences operation - appending, replacing,
     737     * Helper class to do specific Preferences operation - appending, replacing,
    741738     * deletion by key and by value
    742739     * Also contains functions that convert preferences object to JavaScript object and back
     
    745742
    746743        private static void replacePreferences(Preferences fragment, Preferences mainpref) {
    747             // normal prefs
    748             for (Entry<String, String> entry : fragment.properties.entrySet()) {
    749                 mainpref.put(entry.getKey(), entry.getValue());
    750             }
    751             // "list"
    752             for (Entry<String, List<String>> entry : fragment.collectionProperties.entrySet()) {
    753                 mainpref.putCollection(entry.getKey(), entry.getValue());
    754             }
    755             // "lists"
    756             for (Entry<String, List<List<String>>> entry : fragment.arrayProperties.entrySet()) {
    757                 List<Collection<String>> array = new ArrayList<Collection<String>>();
    758                 array.addAll(entry.getValue());
    759                 mainpref.putArray(entry.getKey(), array);
    760             }
    761             /// "maps"
    762             for (Entry<String, List<Map<String, String>>> entry : fragment.listOfStructsProperties.entrySet()) {
    763                 mainpref.putListOfStructs(entry.getKey(), entry.getValue());
    764             }
    765 
     744            for (Entry<String, Setting> entry: fragment.settingsMap.entrySet()) {
     745                mainpref.putSetting(entry.getKey(), entry.getValue());
     746            }
    766747        }
    767748
    768749        private static void appendPreferences(Preferences fragment, Preferences mainpref) {
    769             // normal prefs
    770             for (Entry<String, String> entry : fragment.properties.entrySet()) {
    771                 mainpref.put(entry.getKey(), entry.getValue());
    772             }
    773 
    774             // "list"
    775             for (Entry<String, List<String>> entry : fragment.collectionProperties.entrySet()) {
     750            for (Entry<String, Setting> entry: fragment.settingsMap.entrySet()) {
    776751                String key = entry.getKey();
    777 
    778                 Collection<String> newItems = getCollection(mainpref, key, true);
    779                 if (newItems == null) continue;
    780 
    781                 for (String item : entry.getValue()) {
    782                     // add nonexisting elements to then list
    783                     if (!newItems.contains(item)) {
    784                         newItems.add(item);
     752                if (entry.getValue() instanceof StringSetting) {
     753                    mainpref.putSetting(key, entry.getValue());
     754                } else if (entry.getValue() instanceof ListSetting) {
     755                    ListSetting lSetting = (ListSetting) entry.getValue();
     756                    Collection<String> newItems = getCollection(mainpref, key, true);
     757                    if (newItems == null) continue;
     758                    for (String item : lSetting.getValue()) {
     759                        // add nonexisting elements to then list
     760                        if (!newItems.contains(item)) {
     761                            newItems.add(item);
     762                        }
    785763                    }
     764                    mainpref.putCollection(key, newItems);
     765                } else if (entry.getValue() instanceof ListListSetting) {
     766                    ListListSetting llSetting = (ListListSetting) entry.getValue();
     767                    Collection<Collection<String>> newLists = getArray(mainpref, key, true);
     768                    if (newLists == null) continue;
     769
     770                    for (Collection<String> list : llSetting.getValue()) {
     771                        // add nonexisting list (equals comparison for lists is used implicitly)
     772                        if (!newLists.contains(list)) {
     773                            newLists.add(list);
     774                        }
     775                    }
     776                    mainpref.putArray(key, newLists);
     777                } else if (entry.getValue() instanceof MapListSetting) {
     778                    MapListSetting mlSetting = (MapListSetting) entry.getValue();
     779                    List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
     780                    if (newMaps == null) continue;
     781
     782                    // get existing properties as list of maps
     783
     784                    for (Map<String, String> map : mlSetting.getValue()) {
     785                        // add nonexisting map (equals comparison for maps is used implicitly)
     786                        if (!newMaps.contains(map)) {
     787                            newMaps.add(map);
     788                        }
     789                    }
     790                    mainpref.putListOfStructs(entry.getKey(), newMaps);
    786791                }
    787                 mainpref.putCollection(entry.getKey(), newItems);
    788             }
    789 
    790             // "lists"
    791             for (Entry<String, List<List<String>>> entry : fragment.arrayProperties.entrySet()) {
     792            }
     793        }
     794
     795        /**
     796        * Delete items from @param mainpref collections that match items from @param fragment collections
     797        */
     798        private static void deletePreferenceValues(Preferences fragment, Preferences mainpref) {
     799
     800            for (Entry<String, Setting> entry : fragment.settingsMap.entrySet()) {
    792801                String key = entry.getKey();
    793 
    794                 Collection<Collection<String>> newLists = getArray(mainpref, key, true);
    795                 if (newLists == null) continue;
    796 
    797                 for (Collection<String> list : entry.getValue()) {
    798                     // add nonexisting list (equals comparison for lists is used implicitly)
    799                     if (!newLists.contains(list)) {
    800                         newLists.add(list);
     802                if (entry.getValue() instanceof StringSetting) {
     803                    StringSetting sSetting = (StringSetting) entry.getValue();
     804                    // if mentioned value found, delete it
     805                    if (sSetting.equals(mainpref.settingsMap.get(key))) {
     806                        mainpref.put(key, null);
    801807                    }
     808                } else if (entry.getValue() instanceof ListSetting) {
     809                    ListSetting lSetting = (ListSetting) entry.getValue();
     810                    Collection<String> newItems = getCollection(mainpref, key, true);
     811                    if (newItems == null) continue;
     812
     813                    // remove mentioned items from collection
     814                    for (String item : lSetting.getValue()) {
     815                        log("Deleting preferences: from list %s: %s\n", key, item);
     816                        newItems.remove(item);
     817                    }
     818                    mainpref.putCollection(entry.getKey(), newItems);
     819                } else if (entry.getValue() instanceof ListListSetting) {
     820                    ListListSetting llSetting = (ListListSetting) entry.getValue();
     821                    Collection<Collection<String>> newLists = getArray(mainpref, key, true);
     822                    if (newLists == null) continue;
     823
     824                    // if items are found in one of lists, remove that list!
     825                    Iterator<Collection<String>> listIterator = newLists.iterator();
     826                    while (listIterator.hasNext()) {
     827                        Collection<String> list = listIterator.next();
     828                        for (Collection<String> removeList : llSetting.getValue()) {
     829                            if (list.containsAll(removeList)) {
     830                                // remove current list, because it matches search criteria
     831                                log("Deleting preferences: list from lists %s: %s\n", key, list);
     832                                listIterator.remove();
     833                            }
     834                        }
     835                    }
     836
     837                    mainpref.putArray(key, newLists);
     838                } else if (entry.getValue() instanceof MapListSetting) {
     839                    MapListSetting mlSetting = (MapListSetting) entry.getValue();
     840                    List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
     841                    if (newMaps == null) continue;
     842
     843                    Iterator<Map<String, String>> mapIterator = newMaps.iterator();
     844                    while (mapIterator.hasNext()) {
     845                        Map<String, String> map = mapIterator.next();
     846                        for (Map<String, String> removeMap : mlSetting.getValue()) {
     847                            if (map.entrySet().containsAll(removeMap.entrySet())) {
     848                                // the map contain all mentioned key-value pair, so it should be deleted from "maps"
     849                                log("Deleting preferences: deleting map from maps %s: %s\n", key, map);
     850                                mapIterator.remove();
     851                            }
     852                        }
     853                    }
     854                    mainpref.putListOfStructs(entry.getKey(), newMaps);
    802855                }
    803                 mainpref.putArray(entry.getKey(), newLists);
    804             }
    805 
    806             /// "maps"
    807             for (Entry<String, List<Map<String, String>>> entry : fragment.listOfStructsProperties.entrySet()) {
    808                 String key = entry.getKey();
    809 
    810                 List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
    811                 if (newMaps == null) continue;
    812 
    813                 // get existing properties as list of maps
    814 
    815                 for (Map<String, String> map : entry.getValue()) {
    816                     // add nonexisting map (equals comparison for maps is used implicitly)
    817                     if (!newMaps.contains(map)) {
    818                         newMaps.add(map);
    819                     }
    820                 }
    821                 mainpref.putListOfStructs(entry.getKey(), newMaps);
    822             }
    823         }
    824 
    825         /**
    826      * Delete items from @param mainpref collections that match items from @param fragment collections
    827      */
    828     private static void deletePreferenceValues(Preferences fragment, Preferences mainpref) {
    829 
    830 
    831         // normal prefs
    832         for (Entry<String, String> entry : fragment.properties.entrySet()) {
    833             // if mentioned value found, delete it
    834             if (entry.getValue().equals(mainpref.properties.get(entry.getKey()))) {
    835                 mainpref.put(entry.getKey(), null);
    836             }
    837         }
    838 
    839         // "list"
    840         for (Entry<String, List<String>> entry : fragment.collectionProperties.entrySet()) {
    841             String key = entry.getKey();
    842 
    843             Collection<String> newItems = getCollection(mainpref, key, true);
    844             if (newItems == null) continue;
    845 
    846             // remove mentioned items from collection
    847             for (String item : entry.getValue()) {
    848                 log("Deleting preferences: from list %s: %s\n", key, item);
    849                 newItems.remove(item);
    850             }
    851             mainpref.putCollection(entry.getKey(), newItems);
    852         }
    853 
    854         // "lists"
    855         for (Entry<String, List<List<String>>> entry : fragment.arrayProperties.entrySet()) {
    856             String key = entry.getKey();
    857 
    858 
    859             Collection<Collection<String>> newLists = getArray(mainpref, key, true);
    860             if (newLists == null) continue;
    861 
    862             // if items are found in one of lists, remove that list!
    863             Iterator<Collection<String>> listIterator = newLists.iterator();
    864             while (listIterator.hasNext()) {
    865                 Collection<String> list = listIterator.next();
    866                 for (Collection<String> removeList : entry.getValue()) {
    867                     if (list.containsAll(removeList)) {
    868                         // remove current list, because it matches search criteria
    869                         log("Deleting preferences: list from lists %s: %s\n", key, list);
    870                         listIterator.remove();
    871                     }
    872                 }
    873             }
    874 
    875             mainpref.putArray(entry.getKey(), newLists);
    876         }
    877 
    878         /// "maps"
    879         for (Entry<String, List<Map<String, String>>> entry : fragment.listOfStructsProperties.entrySet()) {
    880             String key = entry.getKey();
    881 
    882             List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
    883             if (newMaps == null) continue;
    884 
    885             Iterator<Map<String, String>> mapIterator = newMaps.iterator();
    886             while (mapIterator.hasNext()) {
    887                 Map<String, String> map = mapIterator.next();
    888                 for (Map<String, String> removeMap : entry.getValue()) {
    889                     if (map.entrySet().containsAll(removeMap.entrySet())) {
    890                         // the map contain all mentioned key-value pair, so it should be deleted from "maps"
    891                         log("Deleting preferences: deleting map from maps %s: %s\n", key, map);
    892                         mapIterator.remove();
    893                     }
    894                 }
    895             }
    896             mainpref.putListOfStructs(entry.getKey(), newMaps);
    897         }
    898     }
     856            }
     857        }
    899858
    900859    private static void deletePreferenceKeyByPattern(String pattern, Preferences pref) {
     
    904863            if (key.matches(pattern)) {
    905864                log("Deleting preferences: deleting key from preferences: " + key);
    906                 pref.putSetting(key, entry.getValue().getNullInstance());
     865                pref.putSetting(key, null);
    907866            }
    908867        }
     
    913872        if (allSettings.containsKey(key)) {
    914873            log("Deleting preferences: deleting key from preferences: " + key);
    915             pref.putSetting(key, allSettings.get(key).getNullInstance());
     874            pref.putSetting(key, null);
    916875        }
    917876    }
    918877
    919878    private static Collection<String> getCollection(Preferences mainpref, String key, boolean warnUnknownDefault)  {
    920         Collection<String> existing = mainpref.collectionProperties.get(key);
    921         Collection<String> defaults = mainpref.collectionDefaults.get(key);
    922 
     879        ListSetting existing = Utils.cast(mainpref.settingsMap.get(key), ListSetting.class);
     880        ListSetting defaults = Utils.cast(mainpref.defaultsMap.get(key), ListSetting.class);
    923881        if (existing == null && defaults == null) {
    924882            if (warnUnknownDefault) defaultUnknownWarning(key);
    925883            return null;
    926884        }
    927         return  (existing != null)
    928                 ? new ArrayList<String>(existing) : new ArrayList<String>(defaults);
     885        if (existing != null)
     886            return new ArrayList<String>(existing.getValue());
     887        else
     888            return defaults.getValue() == null ? null : new ArrayList<String>(defaults.getValue());
    929889    }
    930890
    931891    private static Collection<Collection<String>> getArray(Preferences mainpref, String key, boolean warnUnknownDefault)  {
    932         Collection<List<String>> existing = mainpref.arrayProperties.get(key);
    933         Collection<List<String>> defaults = mainpref.arrayDefaults.get(key);
     892        ListListSetting existing = Utils.cast(mainpref.settingsMap.get(key), ListListSetting.class);
     893        ListListSetting defaults = Utils.cast(mainpref.defaultsMap.get(key), ListListSetting.class);
    934894
    935895        if (existing == null && defaults == null) {
     
    937897            return null;
    938898        }
    939 
    940         return  (existing != null)
    941                 ? new ArrayList<Collection<String>>(existing) : new ArrayList<Collection<String>>(defaults);
     899        if (existing != null)
     900            return new ArrayList<Collection<String>>(existing.getValue());
     901        else
     902            return defaults.getValue() == null ? null : new ArrayList<Collection<String>>(defaults.getValue());
    942903    }
    943904
    944905    private static List<Map<String, String>> getListOfStructs(Preferences mainpref, String key, boolean warnUnknownDefault)  {
    945         Collection<Map<String, String>> existing = mainpref.listOfStructsProperties.get(key);
    946         Collection<Map<String, String>> defaults = mainpref.listOfStructsDefaults.get(key);
     906        MapListSetting existing = Utils.cast(mainpref.settingsMap.get(key), MapListSetting.class);
     907        MapListSetting defaults = Utils.cast(mainpref.settingsMap.get(key), MapListSetting.class);
    947908
    948909        if (existing == null && defaults == null) {
     
    951912        }
    952913
    953         return (existing != null)
    954                 ? new ArrayList<Map<String, String>>(existing) : new ArrayList<Map<String, String>>(defaults);
     914        if (existing != null)
     915            return new ArrayList<Map<String, String>>(existing.getValue());
     916        else
     917            return defaults.getValue() == null ? null : new ArrayList<Map<String, String>>(defaults.getValue());
    955918    }
    956919
     
    965928
    966929    private static void showPrefs(Preferences tmpPref) {
    967         Main.info("properties: " + tmpPref.properties);
    968         Main.info("collections: " + tmpPref.collectionProperties);
    969         Main.info("arrays: " + tmpPref.arrayProperties);
    970         Main.info("maps: " + tmpPref.listOfStructsProperties);
     930        Main.info("properties: " + tmpPref.settingsMap);
    971931    }
    972932
     
    1037997        Map<String, List<Map<String, String>>> listmapMap = (SortedMap<String, List<Map<String,String>>>) engine.get("listmapMap");
    1038998
    1039         tmpPref.properties.clear();
    1040         tmpPref.collectionProperties.clear();
    1041         tmpPref.arrayProperties.clear();
    1042         tmpPref.listOfStructsProperties.clear();
    1043 
     999        tmpPref.settingsMap.clear();
     1000
     1001        Map<String, Setting> tmp = new HashMap<String, Setting>();
    10441002        for (Entry<String, String> e : stringMap.entrySet()) {
    1045             if (e.getValue().equals( tmpPref.defaults.get(e.getKey())) ) continue;
    1046             tmpPref.properties.put(e.getKey(), e.getValue());
    1047         }
    1048 
     1003            tmp.put(e.getKey(), new StringSetting(e.getValue()));
     1004        }
    10491005        for (Entry<String, List<String>> e : listMap.entrySet()) {
    1050             if (Preferences.equalCollection(e.getValue(), tmpPref.collectionDefaults.get(e.getKey()))) continue;
    1051             tmpPref.collectionProperties.put(e.getKey(), e.getValue());
     1006            tmp.put(e.getKey(), new ListSetting(e.getValue()));
    10521007        }
    10531008
    10541009        for (Entry<String, List<Collection<String>>> e : listlistMap.entrySet()) {
    1055             if (Preferences.equalArray(e.getValue(), tmpPref.arrayDefaults.get(e.getKey()))) continue;
    10561010            @SuppressWarnings("unchecked") List<List<String>> value = (List)e.getValue();
    1057             tmpPref.arrayProperties.put(e.getKey(), value);
    1058         }
    1059 
     1011            tmp.put(e.getKey(), new ListListSetting(value));
     1012        }
    10601013        for (Entry<String, List<Map<String, String>>> e : listmapMap.entrySet()) {
    1061             if (Preferences.equalListOfStructs(e.getValue(), tmpPref.listOfStructsDefaults.get(e.getKey()))) continue;
    1062             tmpPref.listOfStructsProperties.put(e.getKey(), e.getValue());
     1014            tmp.put(e.getKey(), new MapListSetting(e.getValue()));
     1015        }
     1016        for (Entry<String, Setting> e : tmp.entrySet()) {
     1017            if (e.getValue().equals(tmpPref.defaultsMap.get(e.getKey()))) continue;
     1018            tmpPref.settingsMap.put(e.getKey(), e.getValue());
    10631019        }
    10641020    }
     
    10791035
    10801036        if (includeDefaults) {
    1081             stringMap.putAll(tmpPref.defaults);
    1082             listMap.putAll(tmpPref.collectionDefaults);
    1083             listlistMap.putAll(tmpPref.arrayDefaults);
    1084             listmapMap.putAll(tmpPref.listOfStructsDefaults);
    1085         }
    1086 
    1087         while (stringMap.values().remove(null));
    1088         while (listMap.values().remove(null));
    1089         while (listlistMap.values().remove(null));
    1090         while (listmapMap.values().remove(null));
    1091 
    1092         stringMap.putAll(tmpPref.properties);
    1093         listMap.putAll(tmpPref.collectionProperties);
    1094         listlistMap.putAll(tmpPref.arrayProperties);
    1095         listmapMap.putAll(tmpPref.listOfStructsProperties);
     1037            for (Map.Entry<String, Setting> e: tmpPref.defaultsMap.entrySet()) {
     1038                Setting setting = e.getValue();
     1039                if (setting instanceof StringSetting) {
     1040                    stringMap.put(e.getKey(), ((StringSetting) setting).getValue());
     1041                } else if (setting instanceof ListSetting) {
     1042                    listMap.put(e.getKey(), ((ListSetting) setting).getValue());
     1043                } else if (setting instanceof ListListSetting) {
     1044                    listlistMap.put(e.getKey(), ((ListListSetting) setting).getValue());
     1045                } else if (setting instanceof MapListSetting) {
     1046                    listmapMap.put(e.getKey(), ((MapListSetting) setting).getValue());
     1047                }
     1048            }
     1049        }
     1050        Iterator<Map.Entry<String, Setting>> it = tmpPref.settingsMap.entrySet().iterator();
     1051        while (it.hasNext()) {
     1052            Map.Entry<String, Setting> e = it.next();
     1053            if (e.getValue().getValue() == null) {
     1054                it.remove();
     1055            }
     1056        }
     1057
     1058        for (Map.Entry<String, Setting> e: tmpPref.settingsMap.entrySet()) {
     1059            Setting setting = e.getValue();
     1060            if (setting instanceof StringSetting) {
     1061                stringMap.put(e.getKey(), ((StringSetting) setting).getValue());
     1062            } else if (setting instanceof ListSetting) {
     1063                listMap.put(e.getKey(), ((ListSetting) setting).getValue());
     1064            } else if (setting instanceof ListListSetting) {
     1065                listlistMap.put(e.getKey(), ((ListListSetting) setting).getValue());
     1066            } else if (setting instanceof MapListSetting) {
     1067                listmapMap.put(e.getKey(), ((MapListSetting) setting).getValue());
     1068            }
     1069        }
    10961070
    10971071        engine.put("stringMap", stringMap);
  • trunk/src/org/openstreetmap/josm/data/Preferences.java

    r6552 r6578  
    5151import org.openstreetmap.josm.io.MirroredInputStream;
    5252import org.openstreetmap.josm.io.XmlWriter;
     53import org.openstreetmap.josm.tools.CheckParameterUtil;
    5354import org.openstreetmap.josm.tools.ColorHelper;
    5455import org.openstreetmap.josm.tools.Utils;
     
    8889
    8990    /**
    90      * Map the property name to strings. Does not contain null or "" values.
    91      */
    92     protected final SortedMap<String, String> properties = new TreeMap<String, String>();
    93     /** Map of defaults, can contain null values */
    94     protected final SortedMap<String, String> defaults = new TreeMap<String, String>();
     91     * Map the setting name to the current value of the setting.
     92     * The map must not contain null as key or value. The mapped setting objects
     93     * must not have a null value.
     94     */
     95    protected final SortedMap<String, Setting> settingsMap = new TreeMap<String, Setting>();
     96    /**
     97     * Map the setting name to the default value of the setting.
     98     * The map must not contain null as key or value. The value of the mapped
     99     * setting objects can be null.
     100     */
     101    protected final SortedMap<String, Setting> defaultsMap = new TreeMap<String, Setting>();
     102    // maps color keys to human readable color name
    95103    protected final SortedMap<String, String> colornames = new TreeMap<String, String>();
    96104
    97     /** Mapping for list settings. Must not contain null values */
    98     protected final SortedMap<String, List<String>> collectionProperties = new TreeMap<String, List<String>>();
    99     /** Defaults, can contain null values */
    100     protected final SortedMap<String, List<String>> collectionDefaults = new TreeMap<String, List<String>>();
    101 
    102     protected final SortedMap<String, List<List<String>>> arrayProperties = new TreeMap<String, List<List<String>>>();
    103     protected final SortedMap<String, List<List<String>>> arrayDefaults = new TreeMap<String, List<List<String>>>();
    104 
    105     protected final SortedMap<String, List<Map<String,String>>> listOfStructsProperties = new TreeMap<String, List<Map<String,String>>>();
    106     protected final SortedMap<String, List<Map<String,String>>> listOfStructsDefaults = new TreeMap<String, List<Map<String,String>>>();
    107 
    108     /**
    109      * Interface for a preference value
     105    /**
     106     * Interface for a preference value.
     107     *
     108     * Implementations must provide a proper <code>equals</code> method.
    110109     *
    111110     * @param <T> the data type for the value
     
    118117         */
    119118        T getValue();
     119
     120        /**
     121         * Check if the value of this Setting object is equal to the given value.
     122         * @param otherVal the other value
     123         * @return true if the values are equal
     124         */
     125        boolean equalVal(T otherVal);
     126
     127        /**
     128         * Clone the current object.
     129         * @return an identical copy of the current object
     130         */
     131        Setting<T> copy();
    120132
    121133        /**
     
    142154     */
    143155    abstract public static class AbstractSetting<T> implements Setting<T> {
    144         private final T value;
     156        protected final T value;
    145157        /**
    146158         * Constructs a new {@code AbstractSetting} with the given value
     
    169181            super(value);
    170182        }
     183        @Override public boolean equalVal(String otherVal) {
     184            if (value == null) return otherVal == null;
     185            return value.equals(otherVal);
     186        }
     187        @Override public StringSetting copy() {
     188            return new StringSetting(value);
     189        }
    171190        @Override public void visit(SettingVisitor visitor) {
    172191            visitor.visit(this);
     
    174193        @Override public StringSetting getNullInstance() {
    175194            return new StringSetting(null);
     195        }
     196        @Override
     197        public boolean equals(Object other) {
     198            if (!(other instanceof StringSetting)) return false;
     199            return equalVal(((StringSetting) other).getValue());
    176200        }
    177201    }
     
    187211        public ListSetting(List<String> value) {
    188212            super(value);
     213            consistencyTest();
     214        }
     215        /**
     216         * Convenience factory method.
     217         * @param value the value
     218         * @return a corresponding ListSetting object
     219         */
     220        public static ListSetting create(Collection<String> value) {
     221            return new ListSetting(value == null ? null : Collections.unmodifiableList(new ArrayList<String>(value)));
     222        }
     223        @Override public boolean equalVal(List<String> otherVal) {
     224            return equalCollection(value, otherVal);
     225        }
     226        public static boolean equalCollection(Collection<String> a, Collection<String> b) {
     227            if (a == null) return b == null;
     228            if (b == null) return false;
     229            if (a.size() != b.size()) return false;
     230            Iterator<String> itA = a.iterator();
     231            Iterator<String> itB = b.iterator();
     232            while (itA.hasNext()) {
     233                String aStr = itA.next();
     234                String bStr = itB.next();
     235                if (!Utils.equal(aStr,bStr)) return false;
     236            }
     237            return true;
     238        }
     239        @Override public ListSetting copy() {
     240            return ListSetting.create(value);
     241        }
     242        private void consistencyTest() {
     243            if (value != null && value.contains(null))
     244                throw new RuntimeException("Error: Null as list element in preference setting");
    189245        }
    190246        @Override public void visit(SettingVisitor visitor) {
     
    193249        @Override public ListSetting getNullInstance() {
    194250            return new ListSetting(null);
     251        }
     252        @Override
     253        public boolean equals(Object other) {
     254            if (!(other instanceof ListSetting)) return false;
     255            return equalVal(((ListSetting) other).getValue());
    195256        }
    196257    }
     
    206267        public ListListSetting(List<List<String>> value) {
    207268            super(value);
     269            consistencyTest();
     270        }
     271        /**
     272         * Convenience factory method.
     273         * @param value the value
     274         * @return a corresponding ListListSetting object
     275         */
     276        public static ListListSetting create(Collection<Collection<String>> value) {
     277            if (value != null) {
     278                List<List<String>> valueList = new ArrayList<List<String>>(value.size());
     279                for (Collection<String> lst : value) {
     280                    valueList.add(new ArrayList<String>(lst));
     281                }
     282                return new ListListSetting(valueList);
     283            }
     284            return new ListListSetting(null);
     285        }
     286        @Override public boolean equalVal(List<List<String>> otherVal) {
     287            if (value == null) return otherVal == null;
     288            if (otherVal == null) return false;
     289            if (value.size() != otherVal.size()) return false;
     290            Iterator<List<String>> itA = value.iterator();
     291            Iterator<List<String>> itB = otherVal.iterator();
     292            while (itA.hasNext()) {
     293                if (!ListSetting.equalCollection(itA.next(), itB.next())) return false;
     294            }
     295            return true;
     296        }
     297        @Override public ListListSetting copy() {
     298            if (value == null) return new ListListSetting(null);
     299
     300            List<List<String>> copy = new ArrayList<List<String>>(value.size());
     301            for (Collection<String> lst : value) {
     302                List<String> lstCopy = new ArrayList<String>(lst);
     303                copy.add(Collections.unmodifiableList(lstCopy));
     304            }
     305            return new ListListSetting(Collections.unmodifiableList(copy));
     306        }
     307        private void consistencyTest() {
     308            if (value == null) return;
     309            if (value.contains(null)) throw new RuntimeException("Error: Null as list element in preference setting");
     310            for (Collection<String> lst : value) {
     311                if (lst.contains(null)) throw new RuntimeException("Error: Null as inner list element in preference setting");
     312            }
    208313        }
    209314        @Override public void visit(SettingVisitor visitor) {
     
    212317        @Override public ListListSetting getNullInstance() {
    213318            return new ListListSetting(null);
     319        }
     320        @Override
     321        public boolean equals(Object other) {
     322            if (!(other instanceof ListListSetting)) return false;
     323            return equalVal(((ListListSetting) other).getValue());
    214324        }
    215325    }
     
    225335        public MapListSetting(List<Map<String, String>> value) {
    226336            super(value);
     337            consistencyTest();
     338        }
     339        @Override public boolean equalVal(List<Map<String, String>> otherVal) {
     340            if (value == null) return otherVal == null;
     341            if (otherVal == null) return false;
     342            if (value.size() != otherVal.size()) return false;
     343            Iterator<Map<String, String>> itA = value.iterator();
     344            Iterator<Map<String, String>> itB = otherVal.iterator();
     345            while (itA.hasNext()) {
     346                if (!equalMap(itA.next(), itB.next())) return false;
     347            }
     348            return true;
     349        }
     350        private static boolean equalMap(Map<String, String> a, Map<String, String> b) {
     351            if (a == null) return b == null;
     352            if (b == null) return false;
     353            if (a.size() != b.size()) return false;
     354            for (Entry<String, String> e : a.entrySet()) {
     355                if (!Utils.equal(e.getValue(), b.get(e.getKey()))) return false;
     356            }
     357            return true;
     358        }
     359        @Override public MapListSetting copy() {
     360            if (value == null) return new MapListSetting(null);
     361            List<Map<String, String>> copy = new ArrayList<Map<String, String>>(value.size());
     362            for (Map<String, String> map : value) {
     363                Map<String, String> mapCopy = new LinkedHashMap<String,String>(map);
     364                copy.add(Collections.unmodifiableMap(mapCopy));
     365            }
     366            return new MapListSetting(Collections.unmodifiableList(copy));
     367        }
     368        private void consistencyTest() {
     369            if (value == null) return;
     370            if (value.contains(null)) throw new RuntimeException("Error: Null as list element in preference setting");
     371            for (Map<String, String> map : value) {
     372                if (map.keySet().contains(null)) throw new RuntimeException("Error: Null as map key in preference setting");
     373                if (map.values().contains(null)) throw new RuntimeException("Error: Null as map value in preference setting");
     374            }
    227375        }
    228376        @Override public void visit(SettingVisitor visitor) {
     
    231379        @Override public MapListSetting getNullInstance() {
    232380            return new MapListSetting(null);
     381        }
     382        @Override
     383        public boolean equals(Object other) {
     384            if (!(other instanceof MapListSetting)) return false;
     385            return equalVal(((MapListSetting) other).getValue());
    233386        }
    234387    }
     
    241394    }
    242395
    243     public interface PreferenceChangeEvent<T> {
     396    public interface PreferenceChangeEvent {
    244397        String getKey();
    245         Setting<T> getOldValue();
    246         Setting<T> getNewValue();
     398        Setting getOldValue();
     399        Setting getNewValue();
    247400    }
    248401
     
    251404    }
    252405
    253     private static class DefaultPreferenceChangeEvent<T> implements PreferenceChangeEvent<T> {
     406    private static class DefaultPreferenceChangeEvent implements PreferenceChangeEvent {
    254407        private final String key;
    255         private final Setting<T> oldValue;
    256         private final Setting<T> newValue;
    257 
    258         public DefaultPreferenceChangeEvent(String key, Setting<T> oldValue, Setting<T> newValue) {
     408        private final Setting oldValue;
     409        private final Setting newValue;
     410
     411        public DefaultPreferenceChangeEvent(String key, Setting oldValue, Setting newValue) {
    259412            this.key = key;
    260413            this.oldValue = oldValue;
     
    267420        }
    268421        @Override
    269         public Setting<T> getOldValue() {
     422        public Setting getOldValue() {
    270423            return oldValue;
    271424        }
    272425        @Override
    273         public Setting<T> getNewValue() {
     426        public Setting getNewValue() {
    274427            return newValue;
    275428        }
     
    294447    }
    295448
    296     protected <T> void firePreferenceChanged(String key, Setting<T> oldValue, Setting<T> newValue) {
    297         PreferenceChangeEvent<T> evt = new DefaultPreferenceChangeEvent<T>(key, oldValue, newValue);
     449    protected void firePreferenceChanged(String key, Setting oldValue, Setting newValue) {
     450        PreferenceChangeEvent evt = new DefaultPreferenceChangeEvent(key, oldValue, newValue);
    298451        for (PreferenceChangedListener l : listeners) {
    299452            l.preferenceChanged(evt);
     
    424577     */
    425578    synchronized public String get(final String key) {
    426         putDefault(key, null);
    427         if (!properties.containsKey(key))
    428             return "";
    429         return properties.get(key);
     579        String value = get(key, null);
     580        return value == null ? "" : value;
    430581    }
    431582
     
    439590     */
    440591    synchronized public String get(final String key, final String def) {
    441         putDefault(key, def);
    442         final String prop = properties.get(key);
    443         if (prop == null || prop.isEmpty())
    444             return def;
    445         return prop;
     592        return getSetting(key, new StringSetting(def), StringSetting.class).getValue();
    446593    }
    447594
    448595    synchronized public Map<String, String> getAllPrefix(final String prefix) {
    449596        final Map<String,String> all = new TreeMap<String,String>();
    450         for (final Entry<String,String> e : properties.entrySet()) {
    451             if (e.getKey().startsWith(prefix)) {
    452                 all.put(e.getKey(), e.getValue());
     597        for (final Entry<String,Setting> e : settingsMap.entrySet()) {
     598            if (e.getKey().startsWith(prefix) && (e.getValue() instanceof StringSetting)) {
     599                all.put(e.getKey(), ((StringSetting) e.getValue()).getValue());
    453600            }
    454601        }
     
    458605    synchronized public List<String> getAllPrefixCollectionKeys(final String prefix) {
    459606        final List<String> all = new LinkedList<String>();
    460         for (final String e : collectionProperties.keySet()) {
    461             if (e.startsWith(prefix)) {
    462                 all.add(e);
     607        for (Map.Entry<String, Setting> entry : settingsMap.entrySet()) {
     608            if (entry.getKey().startsWith(prefix) && entry.getValue() instanceof ListSetting) {
     609                all.add(entry.getKey());
    463610            }
    464611        }
     
    468615    synchronized public Map<String, String> getAllColors() {
    469616        final Map<String,String> all = new TreeMap<String,String>();
    470         for (final Entry<String,String> e : defaults.entrySet()) {
    471             if (e.getKey().startsWith("color.") && e.getValue() != null) {
    472                 all.put(e.getKey().substring(6), e.getValue());
    473             }
    474         }
    475         for (final Entry<String,String> e : properties.entrySet()) {
    476             if (e.getKey().startsWith("color.")) {
    477                 all.put(e.getKey().substring(6), e.getValue());
     617        for (final Entry<String,Setting> e : defaultsMap.entrySet()) {
     618            if (e.getKey().startsWith("color.") && e.getValue() instanceof StringSetting) {
     619                StringSetting d = (StringSetting) e.getValue();
     620                if (d.getValue() != null) {
     621                    all.put(e.getKey().substring(6), d.getValue());
     622                }
     623            }
     624        }
     625        for (final Entry<String,Setting> e : settingsMap.entrySet()) {
     626            if (e.getKey().startsWith("color.") && (e.getValue() instanceof StringSetting)) {
     627                all.put(e.getKey().substring(6), ((StringSetting) e.getValue()).getValue());
    478628            }
    479629        }
     
    481631    }
    482632
    483     synchronized public Map<String, String> getDefaults() {
    484         return defaults;
    485     }
    486 
    487     synchronized public void putDefault(final String key, final String def) {
    488         if(!defaults.containsKey(key) || defaults.get(key) == null) {
    489             defaults.put(key, def);
    490         } else if(def != null && !defaults.get(key).equals(def)) {
    491             Main.info("Defaults for " + key + " differ: " + def + " != " + defaults.get(key));
    492         }
    493     }
    494 
    495633    synchronized public boolean getBoolean(final String key) {
    496         putDefault(key, null);
    497         return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : false;
     634        String s = get(key, null);
     635        return s == null ? false : Boolean.parseBoolean(s);
    498636    }
    499637
    500638    synchronized public boolean getBoolean(final String key, final boolean def) {
    501         putDefault(key, Boolean.toString(def));
    502         return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : def;
     639        return Boolean.parseBoolean(get(key, Boolean.toString(def)));
    503640    }
    504641
    505642    synchronized public boolean getBoolean(final String key, final String specName, final boolean def) {
    506         putDefault(key, Boolean.toString(def));
     643        boolean generic = getBoolean(key, def);
    507644        String skey = key+"."+specName;
    508         if(properties.containsKey(skey))
    509             return Boolean.parseBoolean(properties.get(skey));
    510         return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : def;
    511     }
    512 
    513     /**
    514      * Set a value for a certain setting. The changed setting is saved
    515      * to the preference file immediately. Due to caching mechanisms on modern
    516      * operating systems and hardware, this shouldn't be a performance problem.
     645        Setting prop = settingsMap.get(skey);
     646        if (prop instanceof StringSetting)
     647            return Boolean.parseBoolean(((StringSetting)prop).getValue());
     648        else
     649            return generic;
     650    }
     651
     652    /**
     653     * Set a value for a certain setting.
    517654     * @param key the unique identifier for the setting
    518655     * @param value the value of the setting. Can be null or "" which both removes
    519656     *  the key-value entry.
    520      * @return if true, something has changed (i.e. value is different than before)
     657     * @return true, if something has changed (i.e. value is different than before)
    521658     */
    522659    public boolean put(final String key, String value) {
    523         boolean changed = false;
    524         String oldValue = null;
    525 
    526         synchronized (this) {
    527             oldValue = properties.get(key);
    528             if(value != null && value.length() == 0) {
    529                 value = null;
    530             }
    531             // value is the same as before - no need to save anything
    532             boolean equalValue = oldValue != null && oldValue.equals(value);
    533             // The setting was previously unset and we are supposed to put a
    534             // value that equals the default value. This is not necessary because
    535             // the default value is the same throughout josm. In addition we like
    536             // to have the possibility to change the default value from version
    537             // to version, which would not work if we wrote it to the preference file.
    538             boolean unsetIsDefault = oldValue == null && (value == null || value.equals(defaults.get(key)));
    539 
    540             if (!(equalValue || unsetIsDefault)) {
    541                 if (value == null) {
    542                     properties.remove(key);
    543                 } else {
    544                     properties.put(key, value);
    545                 }
    546                 try {
    547                     save();
    548                 } catch (IOException e) {
    549                     Main.warn(tr("Failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
    550                 }
    551                 changed = true;
    552             }
    553         }
    554         if (changed) {
    555             // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
    556             firePreferenceChanged(key, new StringSetting(oldValue), new StringSetting(value));
    557         }
    558         return changed;
     660        if(value != null && value.length() == 0) {
     661            value = null;
     662        }
     663        return putSetting(key, value == null ? null : new StringSetting(value));
    559664    }
    560665
     
    618723
    619724    public void load() throws Exception {
    620         properties.clear();
     725        settingsMap.clear();
    621726        if (!Main.applet) {
    622727            File pref = getPreferenceFile();
     
    708813
    709814    public final void resetToDefault(){
    710         properties.clear();
     815        settingsMap.clear();
    711816    }
    712817
     
    764869            colornames.put(colKey, colName);
    765870        }
    766         putDefault("color."+colKey, ColorHelper.color2html(def));
    767871        String colStr = specName != null ? get("color."+specName) : "";
    768872        if(colStr.isEmpty()) {
    769             colStr = get("color."+colKey);
     873            colStr = get("color."+colKey, ColorHelper.color2html(def));
    770874        }
    771875        return colStr.isEmpty() ? def : ColorHelper.html2color(colStr);
     
    773877
    774878    synchronized public Color getDefaultColor(String colKey) {
    775         String colStr = defaults.get("color."+colKey);
     879        StringSetting col = Utils.cast(defaultsMap.get("color."+colKey), StringSetting.class);
     880        String colStr = col == null ? null : col.getValue();
    776881        return colStr == null || colStr.isEmpty() ? null : ColorHelper.html2color(colStr);
    777882    }
     
    782887
    783888    synchronized public int getInteger(String key, int def) {
    784         putDefault(key, Integer.toString(def));
    785         String v = get(key);
     889        String v = get(key, Integer.toString(def));
    786890        if(v.isEmpty())
    787891            return def;
     
    796900
    797901    synchronized public int getInteger(String key, String specName, int def) {
    798         putDefault(key, Integer.toString(def));
    799902        String v = get(key+"."+specName);
    800903        if(v.isEmpty())
    801             v = get(key);
     904            v = get(key,Integer.toString(def));
    802905        if(v.isEmpty())
    803906            return def;
     
    812915
    813916    synchronized public long getLong(String key, long def) {
    814         putDefault(key, Long.toString(def));
    815         String v = get(key);
     917        String v = get(key, Long.toString(def));
    816918        if(null == v)
    817919            return def;
     
    826928
    827929    synchronized public double getDouble(String key, double def) {
    828         putDefault(key, Double.toString(def));
    829         String v = get(key);
     930        String v = get(key, Double.toString(def));
    830931        if(null == v)
    831932            return def;
     
    847948     */
    848949    public Collection<String> getCollection(String key, Collection<String> def) {
    849         putCollectionDefault(key, def == null ? null : new ArrayList<String>(def));
    850         Collection<String> prop = collectionProperties.get(key);
    851         if (prop != null)
    852             return prop;
    853         else
    854             return def;
     950        return getSetting(key, ListSetting.create(def), ListSetting.class).getValue();
    855951    }
    856952
     
    862958     */
    863959    public Collection<String> getCollection(String key) {
    864         putCollectionDefault(key, null);
    865         Collection<String> prop = collectionProperties.get(key);
    866         if (prop != null)
    867             return prop;
    868         else
    869             return Collections.emptyList();
     960        Collection<String> val = getCollection(key, null);
     961        return val == null ? Collections.<String>emptyList() : val;
    870962    }
    871963
     
    876968    }
    877969
    878     public boolean putCollection(String key, Collection<String> value) {
    879         List<String> oldValue = null;
    880         List<String> valueCopy = null;
    881 
     970    /**
     971     * Set a value for a certain setting. The changed setting is saved
     972     * to the preference file immediately. Due to caching mechanisms on modern
     973     * operating systems and hardware, this shouldn't be a performance problem.
     974     * @param key the unique identifier for the setting
     975     * @param setting the value of the setting. In case it is null, the key-value
     976     * entry will be removed.
     977     * @return true, if something has changed (i.e. value is different than before)
     978     */
     979    public boolean putSetting(final String key, Setting setting) {
     980        CheckParameterUtil.ensureParameterNotNull(key);
     981        if (setting != null && setting.getValue() == null)
     982            throw new IllegalArgumentException("setting argument must not have null value");
     983        Setting settingOld;
     984        Setting settingCopy = null;
    882985        synchronized (this) {
    883             if (value == null) {
    884                 oldValue = collectionProperties.remove(key);
    885                 boolean changed = oldValue != null;
    886                 changed |= properties.remove(key) != null;
    887                 if (!changed) return false;
     986            if (setting == null) {
     987                settingOld = settingsMap.remove(key);
     988                if (settingOld == null)
     989                    return false;
    888990            } else {
    889                 oldValue = collectionProperties.get(key);
    890                 if (equalCollection(value, oldValue)) return false;
    891                 Collection<String> defValue = collectionDefaults.get(key);
    892                 if (oldValue == null && equalCollection(value, defValue)) return false;
    893 
    894                 valueCopy = new ArrayList<String>(value);
    895                 if (valueCopy.contains(null)) throw new RuntimeException("Error: Null as list element in preference setting (key '"+key+"')");
    896                 collectionProperties.put(key, Collections.unmodifiableList(valueCopy));
     991                settingOld = settingsMap.get(key);
     992                if (setting.equals(settingOld))
     993                    return false;
     994                if (settingOld == null && setting.equals(defaultsMap.get(key)))
     995                    return false;
     996                settingCopy = setting.copy();
     997                settingsMap.put(key, settingCopy);
    897998            }
    898999            try {
     
    9031004        }
    9041005        // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
    905         firePreferenceChanged(key, new ListSetting(oldValue), new ListSetting(valueCopy));
     1006        firePreferenceChanged(key, settingOld, settingCopy);
    9061007        return true;
    9071008    }
    9081009
    909     public static boolean equalCollection(Collection<String> a, Collection<String> b) {
    910         if (a == null) return b == null;
    911         if (b == null) return false;
    912         if (a.size() != b.size()) return false;
    913         Iterator<String> itA = a.iterator();
    914         Iterator<String> itB = b.iterator();
    915         while (itA.hasNext()) {
    916             String aStr = itA.next();
    917             String bStr = itB.next();
    918             if (!Utils.equal(aStr,bStr)) return false;
    919         }
    920         return true;
     1010    synchronized public Setting getSetting(String key, Setting def) {
     1011        return getSetting(key, def, Setting.class);
     1012    }
     1013
     1014    /**
     1015     * Get settings value for a certain key and provide default a value.
     1016     * @param <T> the setting type
     1017     * @param key the identifier for the setting
     1018     * @param def the default value. For each call of getSetting() with a given
     1019     * key, the default value must be the same. <code>def</code> must not be
     1020     * null, but the value of <code>def</code> can be null.
     1021     * @param klass the setting type (same as T)
     1022     * @return the corresponding value if the property has been set before,
     1023     *  def otherwise
     1024     */
     1025    synchronized public <T extends Setting> T getSetting(String key, T def, Class<T> klass) {
     1026        CheckParameterUtil.ensureParameterNotNull(key);
     1027        CheckParameterUtil.ensureParameterNotNull(def);
     1028        if (defaultsMap.containsKey(key) && !def.equals(defaultsMap.get(key))) {
     1029            Main.info("Defaults for " + key + " differ: " + def + " != " + defaultsMap.get(key));
     1030        }
     1031        defaultsMap.put(key, def.copy());
     1032        Setting prop = settingsMap.get(key);
     1033        if (klass.isInstance(prop)) {
     1034            @SuppressWarnings("unchecked")
     1035            T prop_cast = (T) prop;
     1036            return prop_cast;
     1037        } else {
     1038            return def;
     1039        }
     1040    }
     1041
     1042    public boolean putCollection(String key, Collection<String> value) {
     1043        return putSetting(key, value == null ? null : ListSetting.create(value));
    9211044    }
    9221045
     
    9351058    }
    9361059
    937     synchronized private void putCollectionDefault(String key, List<String> val) {
    938         collectionDefaults.put(key, val);
    939     }
    940 
    9411060    /**
    9421061     * Used to read a 2-dimensional array of strings from the preference file.
    943      * If not a single entry could be found, def is returned.
     1062     * If not a single entry could be found, <code>def</code> is returned.
    9441063     */
    9451064    synchronized public Collection<Collection<String>> getArray(String key, Collection<Collection<String>> def) {
    946         if (def != null) {
    947             List<List<String>> defCopy = new ArrayList<List<String>>(def.size());
    948             for (Collection<String> lst : def) {
    949                 defCopy.add(Collections.unmodifiableList(new ArrayList<String>(lst)));
    950             }
    951             putArrayDefault(key, Collections.unmodifiableList(defCopy));
    952         } else {
    953             putArrayDefault(key, null);
    954         }
    955         List<List<String>> prop = arrayProperties.get(key);
    956         if (prop != null) {
    957             @SuppressWarnings({ "unchecked", "rawtypes" })
    958             Collection<Collection<String>> prop_cast = (Collection) prop;
    959             return prop_cast;
    960         } else
    961             return def;
     1065        ListListSetting val = getSetting(key, ListListSetting.create(def), ListListSetting.class);
     1066        @SuppressWarnings({ "unchecked", "rawtypes" })
     1067        Collection<Collection<String>> val_cast = (Collection) val.getValue();
     1068        return val_cast;
    9621069    }
    9631070
    9641071    public Collection<Collection<String>> getArray(String key) {
    965         putArrayDefault(key, null);
    966         List<List<String>> prop = arrayProperties.get(key);
    967         if (prop != null) {
    968             @SuppressWarnings({ "unchecked", "rawtypes" })
    969             Collection<Collection<String>> prop_cast = (Collection) prop;
    970             return prop_cast;
    971         } else
    972             return Collections.emptyList();
     1072        Collection<Collection<String>> res = getArray(key, null);
     1073        return res == null ? Collections.<Collection<String>>emptyList() : res;
    9731074    }
    9741075
    9751076    public boolean putArray(String key, Collection<Collection<String>> value) {
    976         List<List<String>> oldValue = null;
    977         List<List<String>> valueCopy = null;
    978 
    979         synchronized (this) {
    980             oldValue = arrayProperties.get(key);
    981             if (value == null) {
    982                 if (arrayProperties.remove(key) != null) return false;
    983             } else {
    984                 if (equalArray(value, oldValue)) return false;
    985 
    986                 List<List<String>> defValue = arrayDefaults.get(key);
    987                 if (oldValue == null && equalArray(value, defValue)) return false;
    988 
    989                 valueCopy = new ArrayList<List<String>>(value.size());
    990                 if (valueCopy.contains(null)) throw new RuntimeException("Error: Null as list element in preference setting (key '"+key+"')");
    991                 for (Collection<String> lst : value) {
    992                     List<String> lstCopy = new ArrayList<String>(lst);
    993                     if (lstCopy.contains(null)) throw new RuntimeException("Error: Null as inner list element in preference setting (key '"+key+"')");
    994                     valueCopy.add(Collections.unmodifiableList(lstCopy));
    995                 }
    996                 arrayProperties.put(key, Collections.unmodifiableList(valueCopy));
    997             }
    998             try {
    999                 save();
    1000             } catch (IOException e){
    1001                 Main.warn(tr("Failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
    1002             }
    1003         }
    1004         // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
    1005         firePreferenceChanged(key, new ListListSetting(oldValue), new ListListSetting(valueCopy));
    1006         return true;
    1007     }
    1008 
    1009     public static boolean equalArray(Collection<Collection<String>> a, Collection<List<String>> b) {
    1010         if (a == null) return b == null;
    1011         if (b == null) return false;
    1012         if (a.size() != b.size()) return false;
    1013         Iterator<Collection<String>> itA = a.iterator();
    1014         Iterator<List<String>> itB = b.iterator();
    1015         while (itA.hasNext()) {
    1016             if (!equalCollection(itA.next(), itB.next())) return false;
    1017         }
    1018         return true;
    1019     }
    1020 
    1021     synchronized private void putArrayDefault(String key, List<List<String>> val) {
    1022         arrayDefaults.put(key, val);
     1077        return putSetting(key, value == null ? null : ListListSetting.create(value));
    10231078    }
    10241079
    10251080    public Collection<Map<String, String>> getListOfStructs(String key, Collection<Map<String, String>> def) {
    1026         if (def != null) {
    1027             List<Map<String, String>> defCopy = new ArrayList<Map<String, String>>(def.size());
    1028             for (Map<String, String> map : def) {
    1029                 defCopy.add(Collections.unmodifiableMap(new LinkedHashMap<String,String>(map)));
    1030             }
    1031             putListOfStructsDefault(key, Collections.unmodifiableList(defCopy));
    1032         } else {
    1033             putListOfStructsDefault(key, null);
    1034         }
    1035         Collection<Map<String, String>> prop = listOfStructsProperties.get(key);
    1036         if (prop != null)
    1037             return prop;
    1038         else
    1039             return def;
     1081        return getSetting(key, new MapListSetting(def == null ? null : new ArrayList<Map<String, String>>(def)), MapListSetting.class).getValue();
    10401082    }
    10411083
    10421084    public boolean putListOfStructs(String key, Collection<Map<String, String>> value) {
    1043 
    1044         List<Map<String, String>> oldValue;
    1045         List<Map<String, String>> valueCopy = null;
    1046 
    1047         synchronized (this) {
    1048             oldValue = listOfStructsProperties.get(key);
    1049             if (value == null) {
    1050                 if (listOfStructsProperties.remove(key) != null) return false;
    1051             } else {
    1052                 if (equalListOfStructs(oldValue, value)) return false;
    1053 
    1054                 List<Map<String, String>> defValue = listOfStructsDefaults.get(key);
    1055                 if (oldValue == null && equalListOfStructs(value, defValue)) return false;
    1056 
    1057                 valueCopy = new ArrayList<Map<String, String>>(value.size());
    1058                 if (valueCopy.contains(null)) throw new RuntimeException("Error: Null as list element in preference setting (key '"+key+"')");
    1059                 for (Map<String, String> map : value) {
    1060                     Map<String, String> mapCopy = new LinkedHashMap<String,String>(map);
    1061                     if (mapCopy.keySet().contains(null)) throw new RuntimeException("Error: Null as map key in preference setting (key '"+key+"')");
    1062                     if (mapCopy.values().contains(null)) throw new RuntimeException("Error: Null as map value in preference setting (key '"+key+"')");
    1063                     valueCopy.add(Collections.unmodifiableMap(mapCopy));
    1064                 }
    1065                 listOfStructsProperties.put(key, Collections.unmodifiableList(valueCopy));
    1066             }
    1067             try {
    1068                 save();
    1069             } catch (IOException e) {
    1070                 Main.warn(tr("Failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
    1071             }
    1072         }
    1073         // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
    1074         firePreferenceChanged(key, new MapListSetting(oldValue), new MapListSetting(valueCopy));
    1075         return true;
    1076     }
    1077 
    1078     public static boolean equalListOfStructs(Collection<Map<String, String>> a, Collection<Map<String, String>> b) {
    1079         if (a == null) return b == null;
    1080         if (b == null) return false;
    1081         if (a.size() != b.size()) return false;
    1082         Iterator<Map<String, String>> itA = a.iterator();
    1083         Iterator<Map<String, String>> itB = b.iterator();
    1084         while (itA.hasNext()) {
    1085             if (!equalMap(itA.next(), itB.next())) return false;
    1086         }
    1087         return true;
    1088     }
    1089 
    1090     private static boolean equalMap(Map<String, String> a, Map<String, String> b) {
    1091         if (a == null) return b == null;
    1092         if (b == null) return false;
    1093         if (a.size() != b.size()) return false;
    1094         for (Entry<String, String> e : a.entrySet()) {
    1095             if (!Utils.equal(e.getValue(), b.get(e.getKey()))) return false;
    1096         }
    1097         return true;
    1098     }
    1099 
    1100     synchronized private void putListOfStructsDefault(String key, List<Map<String, String>> val) {
    1101         listOfStructsDefaults.put(key, val);
     1085        return putSetting(key, value == null ? null : new MapListSetting(new ArrayList<Map<String, String>>(value)));
    11021086    }
    11031087
     
    12551239    }
    12561240
    1257     public boolean putSetting(final String key, Setting value) {
    1258         if (value == null) return false;
    1259         class PutVisitor implements SettingVisitor {
    1260             public boolean changed;
    1261             @Override
    1262             public void visit(StringSetting setting) {
    1263                 changed = put(key, setting.getValue());
    1264             }
    1265             @Override
    1266             public void visit(ListSetting setting) {
    1267                 changed = putCollection(key, setting.getValue());
    1268             }
    1269             @Override
    1270             public void visit(ListListSetting setting) {
    1271                 @SuppressWarnings("unchecked")
    1272                 boolean changed = putArray(key, (Collection) setting.getValue());
    1273                 this.changed = changed;
    1274             }
    1275             @Override
    1276             public void visit(MapListSetting setting) {
    1277                 changed = putListOfStructs(key, setting.getValue());
    1278             }
    1279         }
    1280         PutVisitor putVisitor = new PutVisitor();
    1281         value.visit(putVisitor);
    1282         return putVisitor.changed;
    1283     }
    1284 
    12851241    public Map<String, Setting> getAllSettings() {
    1286         Map<String, Setting> settings = new TreeMap<String, Setting>();
    1287 
    1288         for (Entry<String, String> e : properties.entrySet()) {
    1289             settings.put(e.getKey(), new StringSetting(e.getValue()));
    1290         }
    1291         for (Entry<String, List<String>> e : collectionProperties.entrySet()) {
    1292             settings.put(e.getKey(), new ListSetting(e.getValue()));
    1293         }
    1294         for (Entry<String, List<List<String>>> e : arrayProperties.entrySet()) {
    1295             settings.put(e.getKey(), new ListListSetting(e.getValue()));
    1296         }
    1297         for (Entry<String, List<Map<String, String>>> e : listOfStructsProperties.entrySet()) {
    1298             settings.put(e.getKey(), new MapListSetting(e.getValue()));
    1299         }
    1300         return settings;
     1242        return new TreeMap<String, Setting>(settingsMap);
    13011243    }
    13021244
    13031245    public Map<String, Setting> getAllDefaults() {
    1304         Map<String, Setting> allDefaults = new TreeMap<String, Setting>();
    1305 
    1306         for (Entry<String, String> e : defaults.entrySet()) {
    1307             allDefaults.put(e.getKey(), new StringSetting(e.getValue()));
    1308         }
    1309         for (Entry<String, List<String>> e : collectionDefaults.entrySet()) {
    1310             allDefaults.put(e.getKey(), new ListSetting(e.getValue()));
    1311         }
    1312         for (Entry<String, List<List<String>>> e : arrayDefaults.entrySet()) {
    1313             allDefaults.put(e.getKey(), new ListListSetting(e.getValue()));
    1314         }
    1315         for (Entry<String, List<Map<String, String>>> e : listOfStructsDefaults.entrySet()) {
    1316             allDefaults.put(e.getKey(), new MapListSetting(e.getValue()));
    1317         }
    1318         return allDefaults;
     1246        return new TreeMap<String, Setting>(defaultsMap);
    13191247    }
    13201248
     
    14141342            if (event == XMLStreamConstants.START_ELEMENT) {
    14151343                if (parser.getLocalName().equals("tag")) {
    1416                     properties.put(parser.getAttributeValue(null, "key"), parser.getAttributeValue(null, "value"));
     1344                    settingsMap.put(parser.getAttributeValue(null, "key"), new StringSetting(parser.getAttributeValue(null, "value")));
    14171345                    jumpToEnd();
    14181346                } else if (parser.getLocalName().equals("list") ||
     
    14761404        }
    14771405        if (entries != null) {
    1478             collectionProperties.put(key, Collections.unmodifiableList(entries));
     1406            settingsMap.put(key, new ListSetting(Collections.unmodifiableList(entries)));
    14791407        } else if (lists != null) {
    1480             arrayProperties.put(key, Collections.unmodifiableList(lists));
     1408            settingsMap.put(key, new ListListSetting(Collections.unmodifiableList(lists)));
    14811409        } else if (maps != null) {
    1482             listOfStructsProperties.put(key, Collections.unmodifiableList(maps));
     1410            settingsMap.put(key, new MapListSetting(Collections.unmodifiableList(maps)));
    14831411        } else {
    14841412            if (name.equals("lists")) {
    1485                 arrayProperties.put(key, Collections.<List<String>>emptyList());
     1413                settingsMap.put(key, new ListListSetting(Collections.<List<String>>emptyList()));
    14861414            } else if (name.equals("maps")) {
    1487                 listOfStructsProperties.put(key, Collections.<Map<String, String>>emptyList());
     1415                settingsMap.put(key, new MapListSetting(Collections.<Map<String, String>>emptyList()));
    14881416            } else {
    1489                 collectionProperties.put(key, Collections.<String>emptyList());
     1417                settingsMap.put(key, new ListSetting(Collections.<String>emptyList()));
    14901418            }
    14911419        }
     
    15501478            if (noPassword && key.equals("osm-server.password"))
    15511479                return; // do not store plain password.
    1552             String r = setting.getValue();
    1553             String s = defaults.get(key);
    15541480            /* don't save default values */
    1555             if(s == null || !s.equals(r)) {
    1556                 b.append("  <tag key='");
    1557                 b.append(XmlWriter.encode(key));
    1558                 b.append("' value='");
    1559                 b.append(XmlWriter.encode(setting.getValue()));
    1560                 b.append("'/>\n");
    1561             }
     1481            if (setting.equals(defaultsMap.get(key)))
     1482                return;
     1483            b.append("  <tag key='");
     1484            b.append(XmlWriter.encode(key));
     1485            b.append("' value='");
     1486            b.append(XmlWriter.encode(setting.getValue()));
     1487            b.append("'/>\n");
    15621488        }
    15631489
     
    16041530                Version.getInstance().getVersion() + "\">\n");
    16051531        SettingToXml toXml = new SettingToXml(b, nopass);
    1606         Map<String, Setting<?>> settings = new TreeMap<String, Setting<?>>();
    1607 
    1608         for (Entry<String, String> e : properties.entrySet()) {
    1609             settings.put(e.getKey(), new StringSetting(e.getValue()));
    1610         }
    1611         for (Entry<String, List<String>> e : collectionProperties.entrySet()) {
    1612             settings.put(e.getKey(), new ListSetting(e.getValue()));
    1613         }
    1614         for (Entry<String, List<List<String>>> e : arrayProperties.entrySet()) {
    1615             settings.put(e.getKey(), new ListListSetting(e.getValue()));
    1616         }
    1617         for (Entry<String, List<Map<String, String>>> e : listOfStructsProperties.entrySet()) {
    1618             settings.put(e.getKey(), new MapListSetting(e.getValue()));
    1619         }
    1620         for (Entry<String, Setting<?>> e : settings.entrySet()) {
     1532        for (Entry<String, Setting> e : settingsMap.entrySet()) {
    16211533            toXml.setKey(e.getKey());
    16221534            e.getValue().visit(toXml);
     
    16481560        };
    16491561        for (String key : obsolete) {
    1650             boolean removed = false;
    1651             if (properties.containsKey(key)) { properties.remove(key); removed = true; }
    1652             if (collectionProperties.containsKey(key)) { collectionProperties.remove(key); removed = true; }
    1653             if (arrayProperties.containsKey(key)) { arrayProperties.remove(key); removed = true; }
    1654             if (listOfStructsProperties.containsKey(key)) { listOfStructsProperties.remove(key); removed = true; }
    1655             if (removed) {
     1562            if (settingsMap.containsKey(key)) {
     1563                settingsMap.remove(key);
    16561564                Main.info(tr("Preference setting {0} has been removed since it is no longer used.", key));
    16571565            }
     
    16591567    }
    16601568
    1661     public static boolean isEqual(Setting<?> a, Setting<?> b) {
    1662         if (a==null && b==null) return true;
    1663         if (a==null) return false;
    1664         if (b==null) return false;
    1665         if (a==b) return true;
    1666 
    1667         if (a instanceof StringSetting)
    1668             return (a.getValue().equals(b.getValue()));
    1669         if (a instanceof ListSetting) {
    1670             @SuppressWarnings("unchecked") Collection<String> aValue = (Collection<String>) a.getValue();
    1671             @SuppressWarnings("unchecked") Collection<String> bValue = (Collection<String>) b.getValue();
    1672             return equalCollection(aValue, bValue);
    1673         }
    1674         if (a instanceof ListListSetting) {
    1675             @SuppressWarnings("unchecked") Collection<Collection<String>> aValue = (Collection<Collection<String>>) a.getValue();
    1676             @SuppressWarnings("unchecked") Collection<List<String>> bValue = (Collection<List<String>>) b.getValue();
    1677             return equalArray(aValue, bValue);
    1678         }
    1679         if (a instanceof MapListSetting) {
    1680             @SuppressWarnings("unchecked") Collection<Map<String, String>> aValue = (Collection<Map<String, String>>) a.getValue();
    1681             @SuppressWarnings("unchecked") Collection<Map<String, String>> bValue = (Collection<Map<String, String>>) b.getValue();
    1682             return equalListOfStructs(aValue, bValue);
    1683         }
     1569    public static boolean isEqual(Setting a, Setting b) {
     1570        if (a == null) return b == null;
    16841571        return a.equals(b);
    16851572    }
  • trunk/src/org/openstreetmap/josm/data/ServerSidePreferences.java

    r6380 r6578  
    151151
    152152    public void download(String userName, String password) {
    153         if (!properties.containsKey("applet.username") && userName != null) {
    154             properties.put("applet.username", userName);
     153        if (!settingsMap.containsKey("applet.username") && userName != null) {
     154            settingsMap.put("applet.username", new StringSetting(userName));
    155155        }
    156         if (!properties.containsKey("applet.password") && password != null) {
    157             properties.put("applet.password", password);
     156        if (!settingsMap.containsKey("applet.password") && password != null) {
     157            settingsMap.put("applet.password", new StringSetting(password));
    158158        }
    159159        try {
  • trunk/src/org/openstreetmap/josm/gui/MainApplication.java

    r6528 r6578  
    398398        Main.MasterWindowListener.setup();
    399399
    400         boolean maximized = Boolean.parseBoolean(Main.pref.get("gui.maximized"));
     400        boolean maximized = Main.pref.getBoolean("gui.maximized", false);
    401401        if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) {
    402402            if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) {
  • trunk/src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java

    r6529 r6578  
    436436        for (PrefEntry e : allData) {
    437437            if (e.isChanged()) {
    438                 Main.pref.putSetting(e.getKey(), e.getValue());
     438                Main.pref.putSetting(e.getKey(), e.getValue().getValue() == null ? null : e.getValue());
    439439            }
    440440        }
  • trunk/src/org/openstreetmap/josm/gui/preferences/advanced/PreferencesTable.java

    r6495 r6578  
    11// License: GPL. See LICENSE file for details.
    22package org.openstreetmap.josm.gui.preferences.advanced;
     3
     4import static org.openstreetmap.josm.tools.I18n.marktr;
     5import static org.openstreetmap.josm.tools.I18n.tr;
    36
    47import java.awt.Color;
     
    1215import java.util.List;
    1316import java.util.Map;
     17
    1418import javax.swing.ButtonGroup;
    1519import javax.swing.DefaultCellEditor;
     
    2226import javax.swing.table.DefaultTableCellRenderer;
    2327import javax.swing.table.DefaultTableModel;
     28
    2429import org.openstreetmap.josm.Main;
    2530import org.openstreetmap.josm.data.Preferences;
     
    2732import org.openstreetmap.josm.gui.widgets.JosmTextField;
    2833import org.openstreetmap.josm.tools.GBC;
    29 import static org.openstreetmap.josm.tools.I18n.marktr;
    30 import static org.openstreetmap.josm.tools.I18n.tr;
    3134import org.openstreetmap.josm.tools.Utils;
    3235
     
    106109            if (lEditor.getValue() == 1) {
    107110                List<String> data = lEditor.getData();
    108                 if (!Preferences.equalCollection(lSetting.getValue(), data)) {
     111                if (!lSetting.equalVal(data)) {
    109112                    e.setValue(new Preferences.ListSetting(data));
    110113                    return true;
     
    112115            }
    113116        } else if (stg instanceof Preferences.ListListSetting) {
    114             ListListEditor llEditor = new ListListEditor(gui, e, (Preferences.ListListSetting) stg);
     117            Preferences.ListListSetting llSetting = (Preferences.ListListSetting) stg;
     118            ListListEditor llEditor = new ListListEditor(gui, e, llSetting);
    115119            llEditor.showDialog();
    116120            if (llEditor.getValue() == 1) {
     
    118122                @SuppressWarnings("unchecked")
    119123                Collection<Collection<String>> stgValue = (Collection<Collection<String>>) stg.getValue();
    120                 if (!Preferences.equalArray(stgValue, data)) {
     124                if (!llSetting.equalVal(data)) {
    121125                    e.setValue(new Preferences.ListListSetting(data));
    122126                    return true;
     
    129133            if (mlEditor.getValue() == 1) {
    130134                List<Map<String, String>> data = mlEditor.getData();
    131                 if (!Preferences.equalListOfStructs(mlSetting.getValue(), data)) {
     135                if (!mlSetting.equalVal(data)) {
    132136                    e.setValue(new Preferences.MapListSetting(data));
    133137                    return true;
     
    196200                if (lEditor.getValue() == 1) {
    197201                    List<String> data = lEditor.getData();
    198                     if (!Preferences.equalCollection(lSetting.getValue(), data)) {
     202                    if (!lSetting.equalVal(data)) {
    199203                        pe.setValue(new Preferences.ListSetting(data));
    200204                        ok = true;
     
    208212                if (llEditor.getValue() == 1) {
    209213                    List<List<String>> data = llEditor.getData();
    210                     @SuppressWarnings("unchecked")
    211                     Collection<Collection<String>> llSettingValue = (Collection) llSetting.getValue();
    212                     if (!Preferences.equalArray(llSettingValue, data)) {
     214                    if (!llSetting.equalVal(data)) {
    213215                        pe.setValue(new Preferences.ListListSetting(data));
    214216                        ok = true;
     
    222224                if (mlEditor.getValue() == 1) {
    223225                    List<Map<String, String>> data = mlEditor.getData();
    224                     if (!Preferences.equalListOfStructs(mlSetting.getValue(), data)) {
     226                    if (!mlSetting.equalVal(data)) {
    225227                        pe.setValue(new Preferences.MapListSetting(data));
    226228                        ok = true;
  • trunk/src/org/openstreetmap/josm/tools/Utils.java

    r6552 r6578  
    857857        }
    858858    }
     859
     860    /**
     861     * Cast an object savely.
     862     * @param <T> the target type
     863     * @param o the object to cast
     864     * @param klass the target class (same as T)
     865     * @return null if <code>o</code> is null or the type <code>o</code> is not
     866     *  a subclass of <code>klass</code>. The casted value otherwise.
     867     */
     868    public static <T> T cast(Object o, Class<T> klass) {
     869        if (klass.isInstance(o)) {
     870            @SuppressWarnings("unchecked")
     871            T ret = (T) o;
     872            return ret;
     873        }
     874        return null;
     875    }
     876
    859877}
Note: See TracChangeset for help on using the changeset viewer.