source: josm/trunk/src/org/openstreetmap/josm/data/PreferencesUtils.java @ 12841

Last change on this file since 12841 was 12841, checked in by bastiK, 3 months ago

see #15229 - fix deprecations caused by [12840]

File size: 20.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.HashMap;
9import java.util.Iterator;
10import java.util.List;
11import java.util.Map;
12import java.util.Map.Entry;
13import java.util.TreeMap;
14
15import javax.script.ScriptEngine;
16import javax.script.ScriptException;
17import javax.swing.JOptionPane;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.data.preferences.ListListSetting;
21import org.openstreetmap.josm.data.preferences.ListSetting;
22import org.openstreetmap.josm.data.preferences.MapListSetting;
23import org.openstreetmap.josm.data.preferences.Setting;
24import org.openstreetmap.josm.data.preferences.StringSetting;
25import org.openstreetmap.josm.tools.Logging;
26import org.openstreetmap.josm.tools.Utils;
27
28/**
29 * Helper class to do specific Preferences operation - appending, replacing, deletion by key and by value
30 * Also contains functions that convert preferences object to JavaScript object and back
31 * @since 12634 (extracted from {@code CustomConfigurator})
32 */
33public final class PreferencesUtils {
34
35    private static StringBuilder summary = new StringBuilder();
36
37    private PreferencesUtils() {
38        // Hide implicit public constructor for utility class
39    }
40
41    /**
42     * Log a formatted message.
43     * @param fmt format
44     * @param vars arguments
45     * @see String#format
46     * @since 12826
47     */
48    public static void log(String fmt, Object... vars) {
49        summary.append(String.format(fmt, vars));
50    }
51
52    /**
53     * Log a message.
54     * @param s message to log
55     * @since 12826
56     */
57    public static void log(String s) {
58        summary.append(s).append('\n');
59    }
60
61    /**
62     * Log an exception.
63     * @param e exception to log
64     * @param s message prefix
65     * @since 12826
66     */
67    public static void log(Exception e, String s) {
68        summary.append(s).append(' ').append(Logging.getErrorMessage(e)).append('\n');
69    }
70
71    /**
72     * Returns the log.
73     * @return the log
74     * @since 12826
75     */
76    public static String getLog() {
77        return summary.toString();
78    }
79
80    /**
81     * Resets the log.
82     * @since 12826
83     */
84    public static void resetLog() {
85        summary = new StringBuilder();
86    }
87
88    public static void replacePreferences(Preferences fragment, Preferences mainpref) {
89        for (Entry<String, Setting<?>> entry: fragment.settingsMap.entrySet()) {
90            mainpref.putSetting(entry.getKey(), entry.getValue());
91        }
92    }
93
94    public static void appendPreferences(Preferences fragment, Preferences mainpref) {
95        for (Entry<String, Setting<?>> entry: fragment.settingsMap.entrySet()) {
96            String key = entry.getKey();
97            if (entry.getValue() instanceof StringSetting) {
98                mainpref.putSetting(key, entry.getValue());
99            } else if (entry.getValue() instanceof ListSetting) {
100                ListSetting lSetting = (ListSetting) entry.getValue();
101                List<String> newItems = getList(mainpref, key, true);
102                if (newItems == null) continue;
103                for (String item : lSetting.getValue()) {
104                    // add nonexisting elements to then list
105                    if (!newItems.contains(item)) {
106                        newItems.add(item);
107                    }
108                }
109                mainpref.putList(key, newItems);
110            } else if (entry.getValue() instanceof ListListSetting) {
111                ListListSetting llSetting = (ListListSetting) entry.getValue();
112                List<List<String>> newLists = getListOfLists(mainpref, key, true);
113                if (newLists == null) continue;
114
115                for (List<String> list : llSetting.getValue()) {
116                    // add nonexisting list (equals comparison for lists is used implicitly)
117                    if (!newLists.contains(list)) {
118                        newLists.add(list);
119                    }
120                }
121                mainpref.putListOfLists(key, newLists);
122            } else if (entry.getValue() instanceof MapListSetting) {
123                MapListSetting mlSetting = (MapListSetting) entry.getValue();
124                List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
125                if (newMaps == null) continue;
126
127                // get existing properties as list of maps
128
129                for (Map<String, String> map : mlSetting.getValue()) {
130                    // add nonexisting map (equals comparison for maps is used implicitly)
131                    if (!newMaps.contains(map)) {
132                        newMaps.add(map);
133                    }
134                }
135                mainpref.putListOfMaps(entry.getKey(), newMaps);
136            }
137        }
138    }
139
140    /**
141     * Delete items from {@code mainpref} collections that match items from {@code fragment} collections.
142     * @param fragment preferences
143     * @param mainpref main preferences
144     */
145    public static void deletePreferenceValues(Preferences fragment, Preferences mainpref) {
146
147        for (Entry<String, Setting<?>> entry : fragment.settingsMap.entrySet()) {
148            String key = entry.getKey();
149            if (entry.getValue() instanceof StringSetting) {
150                StringSetting sSetting = (StringSetting) entry.getValue();
151                // if mentioned value found, delete it
152                if (sSetting.equals(mainpref.settingsMap.get(key))) {
153                    mainpref.put(key, null);
154                }
155            } else if (entry.getValue() instanceof ListSetting) {
156                ListSetting lSetting = (ListSetting) entry.getValue();
157                List<String> newItems = getList(mainpref, key, true);
158                if (newItems == null) continue;
159
160                // remove mentioned items from collection
161                for (String item : lSetting.getValue()) {
162                    log("Deleting preferences: from list %s: %s\n", key, item);
163                    newItems.remove(item);
164                }
165                mainpref.putList(entry.getKey(), newItems);
166            } else if (entry.getValue() instanceof ListListSetting) {
167                ListListSetting llSetting = (ListListSetting) entry.getValue();
168                List<List<String>> newLists = getListOfLists(mainpref, key, true);
169                if (newLists == null) continue;
170
171                // if items are found in one of lists, remove that list!
172                Iterator<List<String>> listIterator = newLists.iterator();
173                while (listIterator.hasNext()) {
174                    Collection<String> list = listIterator.next();
175                    for (Collection<String> removeList : llSetting.getValue()) {
176                        if (list.containsAll(removeList)) {
177                            // remove current list, because it matches search criteria
178                            log("Deleting preferences: list from lists %s: %s\n", key, list);
179                            listIterator.remove();
180                        }
181                    }
182                }
183
184                mainpref.putListOfLists(key, newLists);
185            } else if (entry.getValue() instanceof MapListSetting) {
186                MapListSetting mlSetting = (MapListSetting) entry.getValue();
187                List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
188                if (newMaps == null) continue;
189
190                Iterator<Map<String, String>> mapIterator = newMaps.iterator();
191                while (mapIterator.hasNext()) {
192                    Map<String, String> map = mapIterator.next();
193                    for (Map<String, String> removeMap : mlSetting.getValue()) {
194                        if (map.entrySet().containsAll(removeMap.entrySet())) {
195                            // the map contain all mentioned key-value pair, so it should be deleted from "maps"
196                            log("Deleting preferences: deleting map from maps %s: %s\n", key, map);
197                            mapIterator.remove();
198                        }
199                    }
200                }
201                mainpref.putListOfMaps(entry.getKey(), newMaps);
202            }
203        }
204    }
205
206    public static void deletePreferenceKeyByPattern(String pattern, Preferences pref) {
207        Map<String, Setting<?>> allSettings = pref.getAllSettings();
208        for (Entry<String, Setting<?>> entry : allSettings.entrySet()) {
209            String key = entry.getKey();
210            if (key.matches(pattern)) {
211                log("Deleting preferences: deleting key from preferences: " + key);
212                pref.putSetting(key, null);
213            }
214        }
215    }
216
217    public static void deletePreferenceKey(String key, Preferences pref) {
218        Map<String, Setting<?>> allSettings = pref.getAllSettings();
219        if (allSettings.containsKey(key)) {
220            log("Deleting preferences: deleting key from preferences: " + key);
221            pref.putSetting(key, null);
222        }
223    }
224
225    private static List<String> getList(Preferences mainpref, String key, boolean warnUnknownDefault) {
226        ListSetting existing = Utils.cast(mainpref.settingsMap.get(key), ListSetting.class);
227        ListSetting defaults = Utils.cast(mainpref.defaultsMap.get(key), ListSetting.class);
228        if (existing == null && defaults == null) {
229            if (warnUnknownDefault) defaultUnknownWarning(key);
230            return null;
231        }
232        if (existing != null)
233            return new ArrayList<>(existing.getValue());
234        else
235            return defaults.getValue() == null ? null : new ArrayList<>(defaults.getValue());
236    }
237
238    private static List<List<String>> getListOfLists(Preferences mainpref, String key, boolean warnUnknownDefault) {
239        ListListSetting existing = Utils.cast(mainpref.settingsMap.get(key), ListListSetting.class);
240        ListListSetting defaults = Utils.cast(mainpref.defaultsMap.get(key), ListListSetting.class);
241
242        if (existing == null && defaults == null) {
243            if (warnUnknownDefault) defaultUnknownWarning(key);
244            return null;
245        }
246        if (existing != null)
247            return new ArrayList<>(existing.getValue());
248        else
249            return defaults.getValue() == null ? null : new ArrayList<>(defaults.getValue());
250    }
251
252    private static List<Map<String, String>> getListOfStructs(Preferences mainpref, String key, boolean warnUnknownDefault) {
253        MapListSetting existing = Utils.cast(mainpref.settingsMap.get(key), MapListSetting.class);
254        MapListSetting defaults = Utils.cast(mainpref.settingsMap.get(key), MapListSetting.class);
255
256        if (existing == null && defaults == null) {
257            if (warnUnknownDefault) defaultUnknownWarning(key);
258            return null;
259        }
260
261        if (existing != null)
262            return new ArrayList<>(existing.getValue());
263        else
264            return defaults.getValue() == null ? null : new ArrayList<>(defaults.getValue());
265    }
266
267    private static void defaultUnknownWarning(String key) {
268        log("Warning: Unknown default value of %s , skipped\n", key);
269        JOptionPane.showMessageDialog(
270                Main.parent,
271                tr("<html>Settings file asks to append preferences to <b>{0}</b>,<br/> "+
272                        "but its default value is unknown at this moment.<br/> " +
273                        "Please activate corresponding function manually and retry importing.", key),
274                tr("Warning"),
275                JOptionPane.WARNING_MESSAGE);
276    }
277
278    public static void showPrefs(Preferences tmpPref) {
279        Logging.info("properties: " + tmpPref.settingsMap);
280    }
281
282    public static void modifyPreferencesByScript(ScriptEngine engine, Preferences tmpPref, String js) throws ScriptException {
283        loadPrefsToJS(engine, tmpPref, "API.pref", true);
284        engine.eval(js);
285        readPrefsFromJS(engine, tmpPref, "API.pref");
286    }
287
288    /**
289     * Convert JavaScript preferences object to preferences data structures
290     * @param engine - JS engine to put object
291     * @param tmpPref - preferences to fill from JS
292     * @param varInJS - JS variable name, where preferences are stored
293     * @throws ScriptException if the evaluation fails
294     */
295    public static void readPrefsFromJS(ScriptEngine engine, Preferences tmpPref, String varInJS) throws ScriptException {
296        String finish =
297            "stringMap = new java.util.TreeMap ;"+
298            "listMap =  new java.util.TreeMap ;"+
299            "listlistMap = new java.util.TreeMap ;"+
300            "listmapMap =  new java.util.TreeMap ;"+
301            "for (key in "+varInJS+") {"+
302            "  val = "+varInJS+"[key];"+
303            "  type = typeof val == 'string' ? 'string' : val.type;"+
304            "  if (type == 'string') {"+
305            "    stringMap.put(key, val);"+
306            "  } else if (type == 'list') {"+
307            "    l = new java.util.ArrayList;"+
308            "    for (i=0; i<val.length; i++) {"+
309            "      l.add(java.lang.String.valueOf(val[i]));"+
310            "    }"+
311            "    listMap.put(key, l);"+
312            "  } else if (type == 'listlist') {"+
313            "    l = new java.util.ArrayList;"+
314            "    for (i=0; i<val.length; i++) {"+
315            "      list=val[i];"+
316            "      jlist=new java.util.ArrayList;"+
317            "      for (j=0; j<list.length; j++) {"+
318            "         jlist.add(java.lang.String.valueOf(list[j]));"+
319            "      }"+
320            "      l.add(jlist);"+
321            "    }"+
322            "    listlistMap.put(key, l);"+
323            "  } else if (type == 'listmap') {"+
324            "    l = new java.util.ArrayList;"+
325            "    for (i=0; i<val.length; i++) {"+
326            "      map=val[i];"+
327            "      jmap=new java.util.TreeMap;"+
328            "      for (var key2 in map) {"+
329            "         jmap.put(key2,java.lang.String.valueOf(map[key2]));"+
330            "      }"+
331            "      l.add(jmap);"+
332            "    }"+
333            "    listmapMap.put(key, l);"+
334            "  }  else {" +
335            "   " + PreferencesUtils.class.getName() + ".log('Unknown type:'+val.type+ '- use list, listlist or listmap'); }"+
336            "  }";
337        engine.eval(finish);
338
339        @SuppressWarnings("unchecked")
340        Map<String, String> stringMap = (Map<String, String>) engine.get("stringMap");
341        @SuppressWarnings("unchecked")
342        Map<String, List<String>> listMap = (Map<String, List<String>>) engine.get("listMap");
343        @SuppressWarnings("unchecked")
344        Map<String, List<Collection<String>>> listlistMap = (Map<String, List<Collection<String>>>) engine.get("listlistMap");
345        @SuppressWarnings("unchecked")
346        Map<String, List<Map<String, String>>> listmapMap = (Map<String, List<Map<String, String>>>) engine.get("listmapMap");
347
348        tmpPref.settingsMap.clear();
349
350        Map<String, Setting<?>> tmp = new HashMap<>();
351        for (Entry<String, String> e : stringMap.entrySet()) {
352            tmp.put(e.getKey(), new StringSetting(e.getValue()));
353        }
354        for (Entry<String, List<String>> e : listMap.entrySet()) {
355            tmp.put(e.getKey(), new ListSetting(e.getValue()));
356        }
357
358        for (Entry<String, List<Collection<String>>> e : listlistMap.entrySet()) {
359            @SuppressWarnings({ "unchecked", "rawtypes" })
360            List<List<String>> value = (List) e.getValue();
361            tmp.put(e.getKey(), new ListListSetting(value));
362        }
363        for (Entry<String, List<Map<String, String>>> e : listmapMap.entrySet()) {
364            tmp.put(e.getKey(), new MapListSetting(e.getValue()));
365        }
366        for (Entry<String, Setting<?>> e : tmp.entrySet()) {
367            if (e.getValue().equals(tmpPref.defaultsMap.get(e.getKey()))) continue;
368            tmpPref.settingsMap.put(e.getKey(), e.getValue());
369        }
370    }
371
372    /**
373     * Convert preferences data structures to JavaScript object
374     * @param engine - JS engine to put object
375     * @param tmpPref - preferences to convert
376     * @param whereToPutInJS - variable name to store preferences in JS
377     * @param includeDefaults - include known default values to JS objects
378     * @throws ScriptException if the evaluation fails
379     */
380    public static void loadPrefsToJS(ScriptEngine engine, Preferences tmpPref, String whereToPutInJS, boolean includeDefaults)
381            throws ScriptException {
382        Map<String, String> stringMap = new TreeMap<>();
383        Map<String, List<String>> listMap = new TreeMap<>();
384        Map<String, List<List<String>>> listlistMap = new TreeMap<>();
385        Map<String, List<Map<String, String>>> listmapMap = new TreeMap<>();
386
387        if (includeDefaults) {
388            for (Map.Entry<String, Setting<?>> e: tmpPref.defaultsMap.entrySet()) {
389                Setting<?> setting = e.getValue();
390                if (setting instanceof StringSetting) {
391                    stringMap.put(e.getKey(), ((StringSetting) setting).getValue());
392                } else if (setting instanceof ListSetting) {
393                    listMap.put(e.getKey(), ((ListSetting) setting).getValue());
394                } else if (setting instanceof ListListSetting) {
395                    listlistMap.put(e.getKey(), ((ListListSetting) setting).getValue());
396                } else if (setting instanceof MapListSetting) {
397                    listmapMap.put(e.getKey(), ((MapListSetting) setting).getValue());
398                }
399            }
400        }
401        tmpPref.settingsMap.entrySet().removeIf(e -> e.getValue().getValue() == null);
402
403        for (Map.Entry<String, Setting<?>> e: tmpPref.settingsMap.entrySet()) {
404            Setting<?> setting = e.getValue();
405            if (setting instanceof StringSetting) {
406                stringMap.put(e.getKey(), ((StringSetting) setting).getValue());
407            } else if (setting instanceof ListSetting) {
408                listMap.put(e.getKey(), ((ListSetting) setting).getValue());
409            } else if (setting instanceof ListListSetting) {
410                listlistMap.put(e.getKey(), ((ListListSetting) setting).getValue());
411            } else if (setting instanceof MapListSetting) {
412                listmapMap.put(e.getKey(), ((MapListSetting) setting).getValue());
413            }
414        }
415
416        engine.put("stringMap", stringMap);
417        engine.put("listMap", listMap);
418        engine.put("listlistMap", listlistMap);
419        engine.put("listmapMap", listmapMap);
420
421        String init =
422            "function getJSList( javaList ) {"+
423            " var jsList; var i; "+
424            " if (javaList == null) return null;"+
425            "jsList = [];"+
426            "  for (i = 0; i < javaList.size(); i++) {"+
427            "    jsList.push(String(list.get(i)));"+
428            "  }"+
429            "return jsList;"+
430            "}"+
431            "function getJSMap( javaMap ) {"+
432            " var jsMap; var it; var e; "+
433            " if (javaMap == null) return null;"+
434            " jsMap = {};"+
435            " for (it = javaMap.entrySet().iterator(); it.hasNext();) {"+
436            "    e = it.next();"+
437            "    jsMap[ String(e.getKey()) ] = String(e.getValue()); "+
438            "  }"+
439            "  return jsMap;"+
440            "}"+
441            "for (it = stringMap.entrySet().iterator(); it.hasNext();) {"+
442            "  e = it.next();"+
443            whereToPutInJS+"[String(e.getKey())] = String(e.getValue());"+
444            "}\n"+
445            "for (it = listMap.entrySet().iterator(); it.hasNext();) {"+
446            "  e = it.next();"+
447            "  list = e.getValue();"+
448            "  jslist = getJSList(list);"+
449            "  jslist.type = 'list';"+
450            whereToPutInJS+"[String(e.getKey())] = jslist;"+
451            "}\n"+
452            "for (it = listlistMap.entrySet().iterator(); it.hasNext(); ) {"+
453            "  e = it.next();"+
454            "  listlist = e.getValue();"+
455            "  jslistlist = [];"+
456            "  for (it2 = listlist.iterator(); it2.hasNext(); ) {"+
457            "    list = it2.next(); "+
458            "    jslistlist.push(getJSList(list));"+
459            "    }"+
460            "  jslistlist.type = 'listlist';"+
461            whereToPutInJS+"[String(e.getKey())] = jslistlist;"+
462            "}\n"+
463            "for (it = listmapMap.entrySet().iterator(); it.hasNext();) {"+
464            "  e = it.next();"+
465            "  listmap = e.getValue();"+
466            "  jslistmap = [];"+
467            "  for (it2 = listmap.iterator(); it2.hasNext();) {"+
468            "    map = it2.next();"+
469            "    jslistmap.push(getJSMap(map));"+
470            "    }"+
471            "  jslistmap.type = 'listmap';"+
472            whereToPutInJS+"[String(e.getKey())] = jslistmap;"+
473            "}\n";
474
475        // Execute conversion script
476        engine.eval(init);
477    }
478}
Note: See TracBrowser for help on using the repository browser.