Changeset 3908 in josm


Ignore:
Timestamp:
Feb 16, 2011 4:39:09 PM (2 years ago)
Author:
bastiK
Message:

new preference type (list of structs). Should be more flexible when preference options are added and dropped for a list like features. (Not sure how far we get with this key=value approach, maybe it's time for xml preferences.) Fixes #5850 - Filter entries are mixed up

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

Legend:

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

    r3848 r3908  
    1313import java.io.OutputStreamWriter; 
    1414import java.io.PrintWriter; 
     15import java.lang.annotation.Retention; 
     16import java.lang.annotation.RetentionPolicy; 
     17import java.lang.reflect.Field; 
    1518import java.nio.channels.FileChannel; 
    1619import java.util.ArrayList; 
     
    736739    } 
    737740 
     741    @Retention(RetentionPolicy.RUNTIME) public @interface pref { } 
     742    @Retention(RetentionPolicy.RUNTIME) public @interface writeExplicitly { } 
     743 
     744    /** 
     745     * Get a list of hashes which are represented by a struct-like class. 
     746     * It reads lines of the form 
     747     *  > key.0=prop:val \u001e prop:val \u001e ... \u001e prop:val 
     748     *  > ... 
     749     *  > key.N=prop:val \u001e prop:val \u001e ... \u001e prop:val 
     750     * Possible properties are given by fields of the class klass that have 
     751     * the @pref annotation. 
     752     * Default constructor is used to initialize the struct objects, properties 
     753     * then override some of these default values. 
     754     * @param key main preference key 
     755     * @param klass The struct class 
     756     * @return a list of objects of type T or an empty list if nothing was found 
     757     */ 
     758    public <T> List<T> getListOfStructs(String key, Class<T> klass) { 
     759        List<T> r = getListOfStructs(key, null, klass); 
     760        if (r == null) 
     761            return Collections.emptyList(); 
     762        else 
     763            return r; 
     764    } 
     765 
     766    /** 
     767     * same as above, but returns def if nothing was found 
     768     */ 
     769    public <T> List<T> getListOfStructs(String key, Collection<T> def, Class<T> klass) { 
     770        Collection<Collection<String>> array = 
     771                getArray(key, def == null ? null : serializeListOfStructs(def, klass)); 
     772        if (array == null) 
     773            return def == null ? null : new ArrayList<T>(def); 
     774        List<T> lst = new ArrayList<T>(); 
     775        for (Collection<String> entries : array) { 
     776            T struct = deserializeStruct(entries, klass); 
     777            lst.add(struct); 
     778        } 
     779        return lst; 
     780    } 
     781 
     782    /** 
     783     * Save a list of hashes represented by a struct-like class. 
     784     * Considers only fields that have the @pref annotation. 
     785     * In addition it does not write fields with null values. (Thus they are cleared) 
     786     * Default values are given by the field values after default constructor has 
     787     * been called. 
     788     * Fields equal to the default value are not written unless the field has 
     789     * the @writeExplicitly annotation. 
     790     * @param key main preference key 
     791     * @param val the list that is supposed to be saved 
     792     * @param klass The struct class 
     793     * @return true if something has changed 
     794     */ 
     795    public <T> boolean putListOfStructs(String key, Collection<T> val, Class<T> klass) { 
     796        return putArray(key, serializeListOfStructs(val, klass)); 
     797    } 
     798 
     799    private <T> Collection<Collection<String>> serializeListOfStructs(Collection<T> l, Class<T> klass) { 
     800        if (l == null) 
     801            return null; 
     802        Collection<Collection<String>> vals = new ArrayList<Collection<String>>(); 
     803        for (T struct : l) { 
     804            if (struct == null) 
     805                continue; 
     806            vals.add(serializeStruct(struct, klass)); 
     807        } 
     808        return vals; 
     809    } 
     810 
     811    private <T> Collection<String> serializeStruct(T struct, Class<T> klass) { 
     812        T structPrototype; 
     813        try { 
     814            structPrototype = klass.newInstance(); 
     815        } catch (InstantiationException ex) { 
     816            throw new RuntimeException(); 
     817        } catch (IllegalAccessException ex) { 
     818            throw new RuntimeException(); 
     819        } 
     820 
     821        Collection<String> hash = new ArrayList<String>(); 
     822        for (Field f : klass.getDeclaredFields()) { 
     823            if (f.getAnnotation(pref.class) == null) { 
     824                continue; 
     825            } 
     826            f.setAccessible(true); 
     827            try { 
     828                Object fieldValue = f.get(struct); 
     829                Object defaultFieldValue = f.get(structPrototype); 
     830                if (fieldValue != null) { 
     831                    if (f.getAnnotation(writeExplicitly.class) != null || !Utils.equal(fieldValue, defaultFieldValue)) { 
     832                        hash.add(String.format("%s:%s", f.getName().replace("_", "-"), fieldValue.toString())); 
     833                    } 
     834                } 
     835            } catch (IllegalArgumentException ex) { 
     836                throw new RuntimeException(); 
     837            } catch (IllegalAccessException ex) { 
     838                throw new RuntimeException(); 
     839            } 
     840        } 
     841        return hash; 
     842    } 
     843 
     844    private <T> T deserializeStruct(Collection<String> hash, Class<T> klass) { 
     845        T struct = null; 
     846        try { 
     847            struct = klass.newInstance(); 
     848        } catch (InstantiationException ex) { 
     849            throw new RuntimeException(); 
     850        } catch (IllegalAccessException ex) { 
     851            throw new RuntimeException(); 
     852        } 
     853        for (String key_value : hash) { 
     854            final int i = key_value.indexOf(':'); 
     855            if (i == -1 || i == 0) { 
     856                continue; 
     857            } 
     858            String key = key_value.substring(0,i); 
     859            String valueString = key_value.substring(i+1); 
     860 
     861            Object value = null; 
     862            Field f; 
     863            try { 
     864                f = klass.getDeclaredField(key.replace("-", "_")); 
     865            } catch (NoSuchFieldException ex) { 
     866                continue; 
     867            } catch (SecurityException ex) { 
     868                throw new RuntimeException(); 
     869            } 
     870            if (f.getAnnotation(pref.class) == null) { 
     871                continue; 
     872            } 
     873            f.setAccessible(true); 
     874            if (f.getType() == Boolean.class || f.getType() == boolean.class) { 
     875                value = Boolean.parseBoolean(valueString); 
     876            } else if (f.getType() == Integer.class || f.getType() == int.class) { 
     877                try { 
     878                    value = Integer.parseInt(valueString); 
     879                } catch (NumberFormatException nfe) { 
     880                    continue; 
     881                } 
     882            } else  if (f.getType() == String.class) { 
     883                value = valueString; 
     884            } else 
     885                throw new RuntimeException("unsupported preference primitive type"); 
     886             
     887            try { 
     888                f.set(struct, value); 
     889            } catch (IllegalArgumentException ex) { 
     890                throw new AssertionError(); 
     891            } catch (IllegalAccessException ex) { 
     892                throw new RuntimeException(); 
     893            } 
     894        } 
     895        return struct; 
     896    } 
     897 
    738898    /** 
    739899     * Updates system properties with the current values in the preferences. 
  • trunk/src/org/openstreetmap/josm/data/osm/Filter.java

    r3719 r3908  
    22package org.openstreetmap.josm.data.osm; 
    33 
     4import static org.openstreetmap.josm.tools.Utils.equal; 
     5 
    46import org.openstreetmap.josm.actions.search.SearchAction.SearchMode; 
    57import org.openstreetmap.josm.actions.search.SearchAction.SearchSetting; 
     8import org.openstreetmap.josm.data.Preferences.pref; 
     9import org.openstreetmap.josm.data.Preferences.writeExplicitly; 
     10import org.openstreetmap.josm.tools.Utils; 
    611 
    712/** 
     
    2429    } 
    2530 
     31    @Deprecated 
    2632    public Filter(String prefText) { 
    2733        super("", SearchMode.add, false, false, false); 
     
    4955    } 
    5056 
    51     public String getPrefString(){ 
    52         return version + ";" + 
    53         text + ";" + mode + ";" + caseSensitive + ";" + regexSearch + ";" + 
    54         "legacy" + ";" + enable + ";" + hiding + ";" + 
    55         inverted + ";" + 
    56         "false"; // last parameter is not used any more (was: applyForChildren) 
     57    public Filter(FilterPreferenceEntry e) { 
     58        super(e.text, SearchMode.add, false, false, false); 
     59        if (equal(e.mode, "replace")) { 
     60            mode = SearchMode.replace; 
     61        } else if (equal(e.mode, "add")) { 
     62            mode = SearchMode.add; 
     63        } else if (equal(e.mode, "remove")) { 
     64            mode = SearchMode.remove; 
     65        } else  if (equal(e.mode, "in_selection")) { 
     66            mode = SearchMode.in_selection; 
     67        } 
     68        caseSensitive = e.case_sensitive; 
     69        regexSearch = e.regex_search; 
     70        enable = e.enable; 
     71        hiding = e.hiding; 
     72        inverted = e.inverted; 
     73    } 
     74 
     75    public static class FilterPreferenceEntry { 
     76        @pref @writeExplicitly public String version = "1"; 
     77        @pref public String text = null; 
     78        @pref @writeExplicitly public String mode = "add"; 
     79        @pref public boolean case_sensitive = false; 
     80        @pref public boolean regex_search = false; 
     81        @pref @writeExplicitly public boolean enable = true; 
     82        @pref @writeExplicitly public boolean hiding = false; 
     83        @pref @writeExplicitly public boolean inverted = false; 
     84    } 
     85 
     86    public FilterPreferenceEntry getPreferenceEntry() { 
     87        FilterPreferenceEntry e = new FilterPreferenceEntry(); 
     88        e.version = version; 
     89        e.text = text; 
     90        e.mode = mode.toString(); 
     91        e.case_sensitive = caseSensitive; 
     92        e.regex_search = regexSearch; 
     93        e.enable = enable; 
     94        e.hiding = hiding; 
     95        e.inverted = inverted; 
     96        return e; 
    5797    } 
    5898} 
  • trunk/src/org/openstreetmap/josm/gui/dialogs/FilterTableModel.java

    r3719 r3908  
    2525import org.openstreetmap.josm.data.osm.DataSet; 
    2626import org.openstreetmap.josm.data.osm.Filter; 
     27import org.openstreetmap.josm.data.osm.Filter.FilterPreferenceEntry; 
    2728import org.openstreetmap.josm.data.osm.FilterMatcher; 
    2829import org.openstreetmap.josm.data.osm.FilterWorker; 
     
    6061        } 
    6162    } 
    62  
    63  
    6463 
    6564    public void executeFilters() { 
     
    110109    } 
    111110 
    112  
    113111    public void executeFilters(Collection<? extends OsmPrimitive> primitives) { 
    114112        DataSet ds = Main.main.getCurrentDataSet(); 
     
    174172 
    175173    private void loadPrefs() { 
     174        if (!loadPrefsImpl()) { 
     175            loadPrefsOld(); 
     176            savePrefs(); 
     177        } 
     178    } 
     179 
     180    private boolean loadPrefsImpl() { 
     181        List<FilterPreferenceEntry> entries = Main.pref.getListOfStructs("filters.entries", null, FilterPreferenceEntry.class); 
     182        if (entries == null) 
     183            return false; 
     184        for (FilterPreferenceEntry e : entries) { 
     185            filters.add(new Filter(e)); 
     186        } 
     187        return true; 
     188    } 
     189 
     190    @Deprecated 
     191    private void loadPrefsOld() { 
    176192        Map<String, String> prefs = Main.pref.getAllPrefix("filters.filter"); 
    177193        for (String value : prefs.values()) { 
     
    182198 
    183199    private void savePrefs() { 
    184         Map<String, String> prefs = Main.pref.getAllPrefix("filters.filter"); 
    185         for (String key : prefs.keySet()) { 
    186             String[] sts = key.split("\\."); 
    187             if (sts.length != 3) 
    188                 throw new Error("Incompatible filter preferences"); 
    189             Main.pref.put("filters.filter." + sts[2], null); 
    190         } 
    191  
    192         int i = 0; 
     200        Collection<FilterPreferenceEntry> entries = new ArrayList<FilterPreferenceEntry>(); 
    193201        for (Filter flt : filters) { 
    194             Main.pref.put("filters.filter." + i++, flt.getPrefString()); 
    195         } 
    196     } 
    197  
    198     private void savePref(int i) { 
    199         if (i >= filters.size()) { 
    200             Main.pref.put("filters.filter." + i, null); 
    201         } else { 
    202             Main.pref.put("filters.filter." + i, filters.get(i).getPrefString()); 
    203         } 
     202            entries.add(flt.getPreferenceEntry()); 
     203        } 
     204        Main.pref.putListOfStructs("filters.entries", entries, FilterPreferenceEntry.class); 
    204205    } 
    205206 
    206207    public void addFilter(Filter f) { 
    207208        filters.add(f); 
    208         savePref(filters.size() - 1); 
     209        savePrefs(); 
    209210        updateFilters(); 
    210211        fireTableRowsInserted(filters.size() - 1, filters.size() - 1); 
     
    215216            return; 
    216217        filters.add(i + 1, filters.remove(i)); 
    217         savePref(i); 
    218         savePref(i + 1); 
     218        savePrefs(); 
    219219        updateFilters(); 
    220220        fireTableRowsUpdated(i, i + 1); 
     
    225225            return; 
    226226        filters.add(i - 1, filters.remove(i)); 
    227         savePref(i); 
    228         savePref(i - 1); 
     227        savePrefs(); 
    229228        updateFilters(); 
    230229        fireTableRowsUpdated(i - 1, i); 
     
    240239    public void setFilter(int i, Filter f) { 
    241240        filters.set(i, f); 
    242         savePref(i); 
     241        savePrefs(); 
    243242        updateFilters(); 
    244243        fireTableRowsUpdated(i, i); 
     
    295294        case 0: 
    296295            f.enable = (Boolean) aValue; 
    297             savePref(row); 
     296            savePrefs(); 
    298297            updateFilters(); 
    299298            fireTableRowsUpdated(row, row); 
     
    301300        case 1: 
    302301            f.hiding = (Boolean) aValue; 
    303             savePref(row); 
     302            savePrefs(); 
    304303            updateFilters(); 
    305304            break; 
    306305        case 2: 
    307306            f.text = (String) aValue; 
    308             savePref(row); 
     307            savePrefs(); 
    309308            break; 
    310309        case 3: 
    311310            f.inverted = (Boolean) aValue; 
    312             savePref(row); 
     311            savePrefs(); 
    313312            updateFilters(); 
    314313            break; 
Note: See TracChangeset for help on using the changeset viewer.