Changeset 3908 in josm for trunk/src/org/openstreetmap/josm


Ignore:
Timestamp:
2011-02-16T16:39:09+01:00 (13 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.