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

Last change on this file since 14153 was 14153, checked in by Don-vip, 6 years ago

see #15229 - deprecate Main.parent and Main itself

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