source: josm/trunk/src/org/openstreetmap/josm/data/Preferences.java@ 13076

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

see #15560 - fix javadoc warnings with recent JDKs

  • Property svn:eol-style set to native
File size: 59.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Color;
8import java.io.File;
9import java.io.IOException;
10import java.io.PrintWriter;
11import java.io.Reader;
12import java.io.StringWriter;
13import java.lang.annotation.Retention;
14import java.lang.annotation.RetentionPolicy;
15import java.nio.charset.StandardCharsets;
16import java.util.ArrayList;
17import java.util.Collection;
18import java.util.Collections;
19import java.util.HashMap;
20import java.util.HashSet;
21import java.util.Iterator;
22import java.util.LinkedList;
23import java.util.List;
24import java.util.Map;
25import java.util.Map.Entry;
26import java.util.Optional;
27import java.util.Set;
28import java.util.SortedMap;
29import java.util.TreeMap;
30import java.util.concurrent.TimeUnit;
31import java.util.function.Predicate;
32import java.util.regex.Matcher;
33import java.util.regex.Pattern;
34import java.util.stream.Stream;
35
36import javax.swing.JOptionPane;
37import javax.xml.stream.XMLStreamException;
38
39import org.openstreetmap.josm.Main;
40import org.openstreetmap.josm.data.preferences.BooleanProperty;
41import org.openstreetmap.josm.data.preferences.ColorInfo;
42import org.openstreetmap.josm.data.preferences.DoubleProperty;
43import org.openstreetmap.josm.data.preferences.IntegerProperty;
44import org.openstreetmap.josm.data.preferences.LongProperty;
45import org.openstreetmap.josm.data.preferences.NamedColorProperty;
46import org.openstreetmap.josm.data.preferences.PreferencesReader;
47import org.openstreetmap.josm.data.preferences.PreferencesWriter;
48import org.openstreetmap.josm.io.OfflineAccessException;
49import org.openstreetmap.josm.io.OnlineResource;
50import org.openstreetmap.josm.spi.preferences.AbstractPreferences;
51import org.openstreetmap.josm.spi.preferences.Config;
52import org.openstreetmap.josm.spi.preferences.IBaseDirectories;
53import org.openstreetmap.josm.spi.preferences.IPreferences;
54import org.openstreetmap.josm.spi.preferences.ListListSetting;
55import org.openstreetmap.josm.spi.preferences.ListSetting;
56import org.openstreetmap.josm.spi.preferences.MapListSetting;
57import org.openstreetmap.josm.spi.preferences.Setting;
58import org.openstreetmap.josm.spi.preferences.StringSetting;
59import org.openstreetmap.josm.tools.CheckParameterUtil;
60import org.openstreetmap.josm.tools.ColorHelper;
61import org.openstreetmap.josm.tools.I18n;
62import org.openstreetmap.josm.tools.ListenerList;
63import org.openstreetmap.josm.tools.Logging;
64import org.openstreetmap.josm.tools.Utils;
65import org.xml.sax.SAXException;
66
67/**
68 * This class holds all preferences for JOSM.
69 *
70 * Other classes can register their beloved properties here. All properties will be
71 * saved upon set-access.
72 *
73 * Each property is a key=setting pair, where key is a String and setting can be one of
74 * 4 types:
75 * string, list, list of lists and list of maps.
76 * In addition, each key has a unique default value that is set when the value is first
77 * accessed using one of the get...() methods. You can use the same preference
78 * key in different parts of the code, but the default value must be the same
79 * everywhere. A default value of null means, the setting has been requested, but
80 * no default value was set. This is used in advanced preferences to present a list
81 * off all possible settings.
82 *
83 * At the moment, you cannot put the empty string for string properties.
84 * put(key, "") means, the property is removed.
85 *
86 * @author imi
87 * @since 74
88 */
89public class Preferences extends AbstractPreferences {
90
91 private static final String COLOR_PREFIX = "color.";
92 private static final Pattern COLOR_LAYER_PATTERN = Pattern.compile("layer\\.(.+)");
93 private static final Pattern COLOR_MAPPAINT_PATTERN = Pattern.compile("mappaint\\.(.+?)\\.(.+)");
94
95 private static final String[] OBSOLETE_PREF_KEYS = {
96 "projection", /* remove entry after Nov. 2017 */
97 "projection.sub", /* remove entry after Nov. 2017 */
98 };
99
100 private static final long MAX_AGE_DEFAULT_PREFERENCES = TimeUnit.DAYS.toSeconds(50);
101
102 private final IBaseDirectories dirs;
103
104 /**
105 * Determines if preferences file is saved each time a property is changed.
106 */
107 private boolean saveOnPut = true;
108
109 /**
110 * Maps the setting name to the current value of the setting.
111 * The map must not contain null as key or value. The mapped setting objects
112 * must not have a null value.
113 */
114 protected final SortedMap<String, Setting<?>> settingsMap = new TreeMap<>();
115
116 /**
117 * Maps the setting name to the default value of the setting.
118 * The map must not contain null as key or value. The value of the mapped
119 * setting objects can be null.
120 */
121 protected final SortedMap<String, Setting<?>> defaultsMap = new TreeMap<>();
122
123 private final Predicate<Entry<String, Setting<?>>> NO_DEFAULT_SETTINGS_ENTRY =
124 e -> !e.getValue().equals(defaultsMap.get(e.getKey()));
125
126 /**
127 * Maps color keys to human readable color name
128 * @deprecated (since 12987) no longer supported
129 */
130 @Deprecated
131 protected final SortedMap<String, String> colornames = new TreeMap<>();
132
133 /**
134 * Indicates whether {@link #init(boolean)} completed successfully.
135 * Used to decide whether to write backup preference file in {@link #save()}
136 */
137 protected boolean initSuccessful;
138
139 /**
140 * Event triggered when a preference entry value changes.
141 * @deprecated use {@link org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent}
142 */
143 @Deprecated
144 public interface PreferenceChangeEvent {
145 /**
146 * Returns the preference key.
147 * @return the preference key
148 */
149 String getKey();
150
151 /**
152 * Returns the old preference value.
153 * @return the old preference value
154 */
155 Setting<?> getOldValue();
156
157 /**
158 * Returns the new preference value.
159 * @return the new preference value
160 */
161 Setting<?> getNewValue();
162 }
163
164 /**
165 * Listener to preference change events.
166 * @since 10600 (functional interface)
167 * @deprecated use {@link org.openstreetmap.josm.spi.preferences.PreferenceChangedListener}
168 */
169 @FunctionalInterface
170 @Deprecated
171 public interface PreferenceChangedListener {
172 /**
173 * Trigerred when a preference entry value changes.
174 * @param e the preference change event
175 */
176 void preferenceChanged(PreferenceChangeEvent e);
177 }
178
179 /**
180 * @deprecated private class is deprecated
181 */
182 @Deprecated
183 private static class DefaultPreferenceChangeEvent implements PreferenceChangeEvent {
184 private final String key;
185 private final Setting<?> oldValue;
186 private final Setting<?> newValue;
187
188 DefaultPreferenceChangeEvent(String key, Setting<?> oldValue, Setting<?> newValue) {
189 this.key = key;
190 this.oldValue = oldValue;
191 this.newValue = newValue;
192 }
193
194 @Override
195 public String getKey() {
196 return key;
197 }
198
199 @Override
200 public Setting<?> getOldValue() {
201 return oldValue;
202 }
203
204 @Override
205 public Setting<?> getNewValue() {
206 return newValue;
207 }
208 }
209
210 private final ListenerList<org.openstreetmap.josm.spi.preferences.PreferenceChangedListener> listeners = ListenerList.create();
211
212 private final HashMap<String, ListenerList<org.openstreetmap.josm.spi.preferences.PreferenceChangedListener>> keyListeners = new HashMap<>();
213
214 /**
215 * @deprecated deprecated private field
216 */
217 @Deprecated
218 private final ListenerList<Preferences.PreferenceChangedListener> listenersDeprecated = ListenerList.create();
219
220 /**
221 * @deprecated deprecated private field
222 */
223 @Deprecated
224 private final HashMap<String, ListenerList<Preferences.PreferenceChangedListener>> keyListenersDeprecated = new HashMap<>();
225
226 /**
227 * Constructs a new {@code Preferences}.
228 */
229 public Preferences() {
230 this.dirs = Config.getDirs();
231 }
232
233 /**
234 * Constructs a new {@code Preferences}.
235 *
236 * @param dirs the directories to use for saving the preferences
237 */
238 public Preferences(IBaseDirectories dirs) {
239 this.dirs = dirs;
240 }
241
242 /**
243 * Constructs a new {@code Preferences} from an existing instance.
244 * @param pref existing preferences to copy
245 * @since 12634
246 */
247 @SuppressWarnings("deprecation")
248 public Preferences(Preferences pref) {
249 this(pref.dirs);
250 settingsMap.putAll(pref.settingsMap);
251 defaultsMap.putAll(pref.defaultsMap);
252 colornames.putAll(pref.colornames);
253 }
254
255 /**
256 * Adds a new preferences listener.
257 * @param listener The listener to add
258 * @since 12881
259 */
260 @Override
261 public void addPreferenceChangeListener(org.openstreetmap.josm.spi.preferences.PreferenceChangedListener listener) {
262 if (listener != null) {
263 listeners.addListener(listener);
264 }
265 }
266
267 /**
268 * Adds a new preferences listener.
269 * @param listener The listener to add
270 * @deprecated use {@link #addPreferenceChangeListener(org.openstreetmap.josm.spi.preferences.PreferenceChangedListener)}
271 */
272 @Deprecated
273 public void addPreferenceChangeListener(Preferences.PreferenceChangedListener listener) {
274 if (listener != null) {
275 listenersDeprecated.addListener(listener);
276 }
277 }
278
279 /**
280 * Removes a preferences listener.
281 * @param listener The listener to remove
282 * @since 12881
283 */
284 @Override
285 public void removePreferenceChangeListener(org.openstreetmap.josm.spi.preferences.PreferenceChangedListener listener) {
286 listeners.removeListener(listener);
287 }
288
289 /**
290 * Removes a preferences listener.
291 * @param listener The listener to remove
292 * @deprecated use {@link #removePreferenceChangeListener(org.openstreetmap.josm.spi.preferences.PreferenceChangedListener)}
293 */
294 @Deprecated
295 public void removePreferenceChangeListener(Preferences.PreferenceChangedListener listener) {
296 listenersDeprecated.removeListener(listener);
297 }
298
299 /**
300 * Adds a listener that only listens to changes in one preference
301 * @param key The preference key to listen to
302 * @param listener The listener to add.
303 * @since 12881
304 */
305 @Override
306 public void addKeyPreferenceChangeListener(String key, org.openstreetmap.josm.spi.preferences.PreferenceChangedListener listener) {
307 listenersForKey(key).addListener(listener);
308 }
309
310 /**
311 * Adds a listener that only listens to changes in one preference
312 * @param key The preference key to listen to
313 * @param listener The listener to add.
314 * @since 10824
315 * @deprecated use
316 * {@link #addKeyPreferenceChangeListener(java.lang.String, org.openstreetmap.josm.spi.preferences.PreferenceChangedListener)}
317 */
318 @Deprecated
319 public void addKeyPreferenceChangeListener(String key, Preferences.PreferenceChangedListener listener) {
320 listenersForKeyDeprecated(key).addListener(listener);
321 }
322
323 /**
324 * Adds a weak listener that only listens to changes in one preference
325 * @param key The preference key to listen to
326 * @param listener The listener to add.
327 * @since 10824
328 */
329 public void addWeakKeyPreferenceChangeListener(String key, org.openstreetmap.josm.spi.preferences.PreferenceChangedListener listener) {
330 listenersForKey(key).addWeakListener(listener);
331 }
332
333 private ListenerList<org.openstreetmap.josm.spi.preferences.PreferenceChangedListener> listenersForKey(String key) {
334 return keyListeners.computeIfAbsent(key, k -> ListenerList.create());
335 }
336
337 /**
338 * @param key preference key
339 * @return listener list for this key
340 * @deprecated deprecated private method
341 */
342 @Deprecated
343 private ListenerList<Preferences.PreferenceChangedListener> listenersForKeyDeprecated(String key) {
344 return keyListenersDeprecated.computeIfAbsent(key, k -> ListenerList.create());
345 }
346
347 /**
348 * Removes a listener that only listens to changes in one preference
349 * @param key The preference key to listen to
350 * @param listener The listener to add.
351 * @since 12881
352 */
353 @Override
354 public void removeKeyPreferenceChangeListener(String key, org.openstreetmap.josm.spi.preferences.PreferenceChangedListener listener) {
355 Optional.ofNullable(keyListeners.get(key)).orElseThrow(
356 () -> new IllegalArgumentException("There are no listeners registered for " + key))
357 .removeListener(listener);
358 }
359
360 /**
361 * Removes a listener that only listens to changes in one preference
362 * @param key The preference key to listen to
363 * @param listener The listener to add.
364 * @deprecated use
365 * {@link #removeKeyPreferenceChangeListener(java.lang.String, org.openstreetmap.josm.spi.preferences.PreferenceChangedListener)}
366 */
367 @Deprecated
368 public void removeKeyPreferenceChangeListener(String key, Preferences.PreferenceChangedListener listener) {
369 Optional.ofNullable(keyListenersDeprecated.get(key)).orElseThrow(
370 () -> new IllegalArgumentException("There are no listeners registered for " + key))
371 .removeListener(listener);
372 }
373
374 protected void firePreferenceChanged(String key, Setting<?> oldValue, Setting<?> newValue) {
375 final org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent evt =
376 new org.openstreetmap.josm.spi.preferences.DefaultPreferenceChangeEvent(key, oldValue, newValue);
377 listeners.fireEvent(listener -> listener.preferenceChanged(evt));
378
379 ListenerList<org.openstreetmap.josm.spi.preferences.PreferenceChangedListener> forKey = keyListeners.get(key);
380 if (forKey != null) {
381 forKey.fireEvent(listener -> listener.preferenceChanged(evt));
382 }
383 firePreferenceChangedDeprecated(key, oldValue, newValue);
384 }
385
386 /**
387 * @param key preference key
388 * @param oldValue old value
389 * @param newValue new value
390 * @deprecated deprecated private method
391 */
392 @Deprecated
393 private void firePreferenceChangedDeprecated(String key, Setting<?> oldValue, Setting<?> newValue) {
394 final Preferences.PreferenceChangeEvent evtDeprecated = new Preferences.DefaultPreferenceChangeEvent(key, oldValue, newValue);
395 listenersDeprecated.fireEvent(listener -> listener.preferenceChanged(evtDeprecated));
396
397 ListenerList<Preferences.PreferenceChangedListener> forKeyDeprecated = keyListenersDeprecated.get(key);
398 if (forKeyDeprecated != null) {
399 forKeyDeprecated.fireEvent(listener -> listener.preferenceChanged(evtDeprecated));
400 }
401 }
402
403 /**
404 * Get the base name of the JOSM directories for preferences, cache and
405 * user data.
406 * Default value is "JOSM", unless overridden by system property "josm.dir.name".
407 * @return the base name of the JOSM directories for preferences, cache and
408 * user data
409 */
410 public String getJOSMDirectoryBaseName() {
411 String name = System.getProperty("josm.dir.name");
412 if (name != null)
413 return name;
414 else
415 return "JOSM";
416 }
417
418 /**
419 * Get the base directories associated with this preference instance.
420 * @return the base directories
421 */
422 public IBaseDirectories getDirs() {
423 return dirs;
424 }
425
426 /**
427 * Returns the user defined preferences directory, containing the preferences.xml file
428 * @return The user defined preferences directory, containing the preferences.xml file
429 * @since 7834
430 * @deprecated use {@link #getPreferencesDirectory(boolean)}
431 */
432 @Deprecated
433 public File getPreferencesDirectory() {
434 return getPreferencesDirectory(false);
435 }
436
437 /**
438 * @param createIfMissing if true, automatically creates this directory,
439 * in case it is missing
440 * @return the preferences directory
441 * @deprecated use {@link #getDirs()} or (more generally) {@link Config#getDirs()}
442 */
443 @Deprecated
444 public File getPreferencesDirectory(boolean createIfMissing) {
445 return dirs.getPreferencesDirectory(createIfMissing);
446 }
447
448 /**
449 * Returns the user data directory, containing autosave, plugins, etc.
450 * Depending on the OS it may be the same directory as preferences directory.
451 * @return The user data directory, containing autosave, plugins, etc.
452 * @since 7834
453 * @deprecated use {@link #getUserDataDirectory(boolean)}
454 */
455 @Deprecated
456 public File getUserDataDirectory() {
457 return getUserDataDirectory(false);
458 }
459
460 /**
461 * @param createIfMissing if true, automatically creates this directory,
462 * in case it is missing
463 * @return the user data directory
464 * @deprecated use {@link #getDirs()} or (more generally) {@link Config#getDirs()}
465 */
466 @Deprecated
467 public File getUserDataDirectory(boolean createIfMissing) {
468 return dirs.getUserDataDirectory(createIfMissing);
469 }
470
471 /**
472 * Returns the user preferences file (preferences.xml).
473 * @return The user preferences file (preferences.xml)
474 */
475 public File getPreferenceFile() {
476 return new File(dirs.getPreferencesDirectory(false), "preferences.xml");
477 }
478
479 /**
480 * Returns the cache file for default preferences.
481 * @return the cache file for default preferences
482 */
483 public File getDefaultsCacheFile() {
484 return new File(dirs.getCacheDirectory(true), "default_preferences.xml");
485 }
486
487 /**
488 * Returns the user plugin directory.
489 * @return The user plugin directory
490 */
491 public File getPluginsDirectory() {
492 return new File(dirs.getUserDataDirectory(false), "plugins");
493 }
494
495 /**
496 * Get the directory where cached content of any kind should be stored.
497 *
498 * If the directory doesn't exist on the file system, it will be created by this method.
499 *
500 * @return the cache directory
501 * @deprecated use {@link #getCacheDirectory(boolean)}
502 */
503 @Deprecated
504 public File getCacheDirectory() {
505 return getCacheDirectory(true);
506 }
507
508 /**
509 * @param createIfMissing if true, automatically creates this directory,
510 * in case it is missing
511 * @return the cache directory
512 * @deprecated use {@link #getDirs()} or (more generally) {@link Config#getDirs()}
513 */
514 @Deprecated
515 public File getCacheDirectory(boolean createIfMissing) {
516 return dirs.getCacheDirectory(createIfMissing);
517 }
518
519 private static void addPossibleResourceDir(Set<String> locations, String s) {
520 if (s != null) {
521 if (!s.endsWith(File.separator)) {
522 s += File.separator;
523 }
524 locations.add(s);
525 }
526 }
527
528 /**
529 * Returns a set of all existing directories where resources could be stored.
530 * @return A set of all existing directories where resources could be stored.
531 */
532 public Collection<String> getAllPossiblePreferenceDirs() {
533 Set<String> locations = new HashSet<>();
534 addPossibleResourceDir(locations, dirs.getPreferencesDirectory(false).getPath());
535 addPossibleResourceDir(locations, dirs.getUserDataDirectory(false).getPath());
536 addPossibleResourceDir(locations, System.getenv("JOSM_RESOURCES"));
537 addPossibleResourceDir(locations, System.getProperty("josm.resources"));
538 if (Main.isPlatformWindows()) {
539 String appdata = System.getenv("APPDATA");
540 if (appdata != null && System.getenv("ALLUSERSPROFILE") != null
541 && appdata.lastIndexOf(File.separator) != -1) {
542 appdata = appdata.substring(appdata.lastIndexOf(File.separator));
543 locations.add(new File(new File(System.getenv("ALLUSERSPROFILE"),
544 appdata), "JOSM").getPath());
545 }
546 } else {
547 locations.add("/usr/local/share/josm/");
548 locations.add("/usr/local/lib/josm/");
549 locations.add("/usr/share/josm/");
550 locations.add("/usr/lib/josm/");
551 }
552 return locations;
553 }
554
555 /**
556 * Gets all normal (string) settings that have a key starting with the prefix
557 * @param prefix The start of the key
558 * @return The key names of the settings
559 */
560 public synchronized Map<String, String> getAllPrefix(final String prefix) {
561 final Map<String, String> all = new TreeMap<>();
562 for (final Entry<String, Setting<?>> e : settingsMap.entrySet()) {
563 if (e.getKey().startsWith(prefix) && (e.getValue() instanceof StringSetting)) {
564 all.put(e.getKey(), ((StringSetting) e.getValue()).getValue());
565 }
566 }
567 return all;
568 }
569
570 /**
571 * Gets all list settings that have a key starting with the prefix
572 * @param prefix The start of the key
573 * @return The key names of the list settings
574 */
575 public synchronized List<String> getAllPrefixCollectionKeys(final String prefix) {
576 final List<String> all = new LinkedList<>();
577 for (Map.Entry<String, Setting<?>> entry : settingsMap.entrySet()) {
578 if (entry.getKey().startsWith(prefix) && entry.getValue() instanceof ListSetting) {
579 all.add(entry.getKey());
580 }
581 }
582 return all;
583 }
584
585 /**
586 * Get all named colors, including customized and the default ones.
587 * @return a map of all named colors (maps preference key to {@link ColorInfo})
588 */
589 public synchronized Map<String, ColorInfo> getAllNamedColors() {
590 final Map<String, ColorInfo> all = new TreeMap<>();
591 for (final Entry<String, Setting<?>> e : settingsMap.entrySet()) {
592 if (!e.getKey().startsWith(NamedColorProperty.NAMED_COLOR_PREFIX))
593 continue;
594 Utils.instanceOfAndCast(e.getValue(), ListSetting.class)
595 .map(d -> d.getValue())
596 .map(lst -> ColorInfo.fromPref(lst, false))
597 .ifPresent(info -> all.put(e.getKey(), info));
598 }
599 for (final Entry<String, Setting<?>> e : defaultsMap.entrySet()) {
600 if (!e.getKey().startsWith(NamedColorProperty.NAMED_COLOR_PREFIX))
601 continue;
602 Utils.instanceOfAndCast(e.getValue(), ListSetting.class)
603 .map(d -> d.getValue())
604 .map(lst -> ColorInfo.fromPref(lst, true))
605 .ifPresent(infoDef -> {
606 ColorInfo info = all.get(e.getKey());
607 if (info == null) {
608 all.put(e.getKey(), infoDef);
609 } else {
610 info.setDefaultValue(infoDef.getDefaultValue());
611 }
612 });
613 }
614 return all;
615 }
616
617 /**
618 * Gets all known colors (preferences starting with the color prefix)
619 * @return All colors
620 * @deprecated (since 12987) replaced by {@link #getAllNamedColors()}
621 */
622 @Deprecated
623 public synchronized Map<String, String> getAllColors() {
624 final Map<String, String> all = new TreeMap<>();
625 for (final Entry<String, Setting<?>> e : defaultsMap.entrySet()) {
626 if (e.getKey().startsWith(COLOR_PREFIX) && e.getValue() instanceof StringSetting) {
627 if (e.getKey().startsWith(COLOR_PREFIX+"layer."))
628 continue; // do not add unchanged layer colors
629 StringSetting d = (StringSetting) e.getValue();
630 if (d.getValue() != null) {
631 all.put(e.getKey().substring(6), d.getValue());
632 }
633 }
634 }
635 for (final Entry<String, Setting<?>> e : settingsMap.entrySet()) {
636 if (e.getKey().startsWith(COLOR_PREFIX) && (e.getValue() instanceof StringSetting)) {
637 all.put(e.getKey().substring(6), ((StringSetting) e.getValue()).getValue());
638 }
639 }
640 return all;
641 }
642
643 /**
644 * Gets an boolean that may be specialized
645 * @param key The basic key
646 * @param specName The sub-key to append to the key
647 * @param def The default value
648 * @return The boolean value or the default value if it could not be parsed
649 * @deprecated use {@link PreferencesUtils#getBoolean(IPreferences, String, String, boolean)}
650 */
651 @Deprecated
652 public synchronized boolean getBoolean(final String key, final String specName, final boolean def) {
653 boolean generic = getBoolean(key, def);
654 String skey = key+'.'+specName;
655 Setting<?> prop = settingsMap.get(skey);
656 if (prop instanceof StringSetting)
657 return Boolean.parseBoolean(((StringSetting) prop).getValue());
658 else
659 return generic;
660 }
661
662 /**
663 * Set a boolean value for a certain setting.
664 * @param key the unique identifier for the setting
665 * @param value The new value
666 * @return {@code true}, if something has changed (i.e. value is different than before)
667 * @see BooleanProperty
668 * @deprecated use {@link IPreferences#putBoolean(String, boolean)}
669 */
670 @Deprecated
671 public boolean put(final String key, final boolean value) {
672 return put(key, Boolean.toString(value));
673 }
674
675 /**
676 * Set a boolean value for a certain setting.
677 * @param key the unique identifier for the setting
678 * @param value The new value
679 * @return {@code true}, if something has changed (i.e. value is different than before)
680 * @see IntegerProperty#put(Integer)
681 * @deprecated use {@link IPreferences#putInt(String, int)}
682 */
683 @Deprecated
684 public boolean putInteger(final String key, final Integer value) {
685 return put(key, Integer.toString(value));
686 }
687
688 /**
689 * Set a boolean value for a certain setting.
690 * @param key the unique identifier for the setting
691 * @param value The new value
692 * @return {@code true}, if something has changed (i.e. value is different than before)
693 * @see DoubleProperty#put(Double)
694 * @deprecated use {@link IPreferences#putDouble(java.lang.String, double)}
695 */
696 @Deprecated
697 public boolean putDouble(final String key, final Double value) {
698 return put(key, Double.toString(value));
699 }
700
701 /**
702 * Set a boolean value for a certain setting.
703 * @param key the unique identifier for the setting
704 * @param value The new value
705 * @return {@code true}, if something has changed (i.e. value is different than before)
706 * @see LongProperty#put(Long)
707 * @deprecated use {@link IPreferences#putLong(java.lang.String, long)}
708 */
709 @Deprecated
710 public boolean putLong(final String key, final Long value) {
711 return put(key, Long.toString(value));
712 }
713
714 /**
715 * Called after every put. In case of a problem, do nothing but output the error in log.
716 * @throws IOException if any I/O error occurs
717 */
718 public synchronized void save() throws IOException {
719 save(getPreferenceFile(), settingsMap.entrySet().stream().filter(NO_DEFAULT_SETTINGS_ENTRY), false);
720 }
721
722 /**
723 * Stores the defaults to the defaults file
724 * @throws IOException If the file could not be saved
725 */
726 public synchronized void saveDefaults() throws IOException {
727 save(getDefaultsCacheFile(), defaultsMap.entrySet().stream(), true);
728 }
729
730 protected void save(File prefFile, Stream<Entry<String, Setting<?>>> settings, boolean defaults) throws IOException {
731 if (!defaults) {
732 /* currently unused, but may help to fix configuration issues in future */
733 putInt("josm.version", Version.getInstance().getVersion());
734 }
735
736 File backupFile = new File(prefFile + "_backup");
737
738 // Backup old preferences if there are old preferences
739 if (initSuccessful && prefFile.exists() && prefFile.length() > 0) {
740 Utils.copyFile(prefFile, backupFile);
741 }
742
743 try (PreferencesWriter writer = new PreferencesWriter(
744 new PrintWriter(new File(prefFile + "_tmp"), StandardCharsets.UTF_8.name()), false, defaults)) {
745 writer.write(settings);
746 }
747
748 File tmpFile = new File(prefFile + "_tmp");
749 Utils.copyFile(tmpFile, prefFile);
750 Utils.deleteFile(tmpFile, marktr("Unable to delete temporary file {0}"));
751
752 setCorrectPermissions(prefFile);
753 setCorrectPermissions(backupFile);
754 }
755
756 private static void setCorrectPermissions(File file) {
757 if (!file.setReadable(false, false) && Logging.isTraceEnabled()) {
758 Logging.trace(tr("Unable to set file non-readable {0}", file.getAbsolutePath()));
759 }
760 if (!file.setWritable(false, false) && Logging.isTraceEnabled()) {
761 Logging.trace(tr("Unable to set file non-writable {0}", file.getAbsolutePath()));
762 }
763 if (!file.setExecutable(false, false) && Logging.isTraceEnabled()) {
764 Logging.trace(tr("Unable to set file non-executable {0}", file.getAbsolutePath()));
765 }
766 if (!file.setReadable(true, true) && Logging.isTraceEnabled()) {
767 Logging.trace(tr("Unable to set file readable {0}", file.getAbsolutePath()));
768 }
769 if (!file.setWritable(true, true) && Logging.isTraceEnabled()) {
770 Logging.trace(tr("Unable to set file writable {0}", file.getAbsolutePath()));
771 }
772 }
773
774 /**
775 * Loads preferences from settings file.
776 * @throws IOException if any I/O error occurs while reading the file
777 * @throws SAXException if the settings file does not contain valid XML
778 * @throws XMLStreamException if an XML error occurs while parsing the file (after validation)
779 */
780 protected void load() throws IOException, SAXException, XMLStreamException {
781 File pref = getPreferenceFile();
782 PreferencesReader.validateXML(pref);
783 PreferencesReader reader = new PreferencesReader(pref, false);
784 reader.parse();
785 settingsMap.clear();
786 settingsMap.putAll(reader.getSettings());
787 removeObsolete(reader.getVersion());
788 }
789
790 /**
791 * Loads default preferences from default settings cache file.
792 *
793 * Discards entries older than {@link #MAX_AGE_DEFAULT_PREFERENCES}.
794 *
795 * @throws IOException if any I/O error occurs while reading the file
796 * @throws SAXException if the settings file does not contain valid XML
797 * @throws XMLStreamException if an XML error occurs while parsing the file (after validation)
798 */
799 protected void loadDefaults() throws IOException, XMLStreamException, SAXException {
800 File def = getDefaultsCacheFile();
801 PreferencesReader.validateXML(def);
802 PreferencesReader reader = new PreferencesReader(def, true);
803 reader.parse();
804 defaultsMap.clear();
805 long minTime = System.currentTimeMillis() / 1000 - MAX_AGE_DEFAULT_PREFERENCES;
806 for (Entry<String, Setting<?>> e : reader.getSettings().entrySet()) {
807 if (e.getValue().getTime() >= minTime) {
808 defaultsMap.put(e.getKey(), e.getValue());
809 }
810 }
811 }
812
813 /**
814 * Loads preferences from XML reader.
815 * @param in XML reader
816 * @throws XMLStreamException if any XML stream error occurs
817 * @throws IOException if any I/O error occurs
818 */
819 public void fromXML(Reader in) throws XMLStreamException, IOException {
820 PreferencesReader reader = new PreferencesReader(in, false);
821 reader.parse();
822 settingsMap.clear();
823 settingsMap.putAll(reader.getSettings());
824 }
825
826 /**
827 * Initializes preferences.
828 * @param reset if {@code true}, current settings file is replaced by the default one
829 */
830 public void init(boolean reset) {
831 initSuccessful = false;
832 // get the preferences.
833 File prefDir = dirs.getPreferencesDirectory(false);
834 if (prefDir.exists()) {
835 if (!prefDir.isDirectory()) {
836 Logging.warn(tr("Failed to initialize preferences. Preference directory ''{0}'' is not a directory.",
837 prefDir.getAbsoluteFile()));
838 JOptionPane.showMessageDialog(
839 Main.parent,
840 tr("<html>Failed to initialize preferences.<br>Preference directory ''{0}'' is not a directory.</html>",
841 prefDir.getAbsoluteFile()),
842 tr("Error"),
843 JOptionPane.ERROR_MESSAGE
844 );
845 return;
846 }
847 } else {
848 if (!prefDir.mkdirs()) {
849 Logging.warn(tr("Failed to initialize preferences. Failed to create missing preference directory: {0}",
850 prefDir.getAbsoluteFile()));
851 JOptionPane.showMessageDialog(
852 Main.parent,
853 tr("<html>Failed to initialize preferences.<br>Failed to create missing preference directory: {0}</html>",
854 prefDir.getAbsoluteFile()),
855 tr("Error"),
856 JOptionPane.ERROR_MESSAGE
857 );
858 return;
859 }
860 }
861
862 File preferenceFile = getPreferenceFile();
863 try {
864 if (!preferenceFile.exists()) {
865 Logging.info(tr("Missing preference file ''{0}''. Creating a default preference file.", preferenceFile.getAbsoluteFile()));
866 resetToDefault();
867 save();
868 } else if (reset) {
869 File backupFile = new File(prefDir, "preferences.xml.bak");
870 Main.platform.rename(preferenceFile, backupFile);
871 Logging.warn(tr("Replacing existing preference file ''{0}'' with default preference file.", preferenceFile.getAbsoluteFile()));
872 resetToDefault();
873 save();
874 }
875 } catch (IOException e) {
876 Logging.error(e);
877 JOptionPane.showMessageDialog(
878 Main.parent,
879 tr("<html>Failed to initialize preferences.<br>Failed to reset preference file to default: {0}</html>",
880 getPreferenceFile().getAbsoluteFile()),
881 tr("Error"),
882 JOptionPane.ERROR_MESSAGE
883 );
884 return;
885 }
886 try {
887 load();
888 initSuccessful = true;
889 } catch (IOException | SAXException | XMLStreamException e) {
890 Logging.error(e);
891 File backupFile = new File(prefDir, "preferences.xml.bak");
892 JOptionPane.showMessageDialog(
893 Main.parent,
894 tr("<html>Preferences file had errors.<br> Making backup of old one to <br>{0}<br> " +
895 "and creating a new default preference file.</html>",
896 backupFile.getAbsoluteFile()),
897 tr("Error"),
898 JOptionPane.ERROR_MESSAGE
899 );
900 Main.platform.rename(preferenceFile, backupFile);
901 try {
902 resetToDefault();
903 save();
904 } catch (IOException e1) {
905 Logging.error(e1);
906 Logging.warn(tr("Failed to initialize preferences. Failed to reset preference file to default: {0}", getPreferenceFile()));
907 }
908 }
909 File def = getDefaultsCacheFile();
910 if (def.exists()) {
911 try {
912 loadDefaults();
913 } catch (IOException | XMLStreamException | SAXException e) {
914 Logging.error(e);
915 Logging.warn(tr("Failed to load defaults cache file: {0}", def));
916 defaultsMap.clear();
917 if (!def.delete()) {
918 Logging.warn(tr("Failed to delete faulty defaults cache file: {0}", def));
919 }
920 }
921 }
922 }
923
924 /**
925 * Resets the preferences to their initial state. This resets all values and file associations.
926 * The default values and listeners are not removed.
927 * <p>
928 * It is meant to be called before {@link #init(boolean)}
929 * @since 10876
930 */
931 public void resetToInitialState() {
932 resetToDefault();
933 saveOnPut = true;
934 initSuccessful = false;
935 }
936
937 /**
938 * Reset all values stored in this map to the default values. This clears the preferences.
939 */
940 public final void resetToDefault() {
941 settingsMap.clear();
942 }
943
944 /**
945 * Convenience method for accessing colour preferences.
946 * <p>
947 * To be removed: end of 2016
948 *
949 * @param colName name of the colour
950 * @param def default value
951 * @return a Color object for the configured colour, or the default value if none configured.
952 * @deprecated Use a {@link NamedColorProperty} instead.
953 */
954 @Deprecated
955 public synchronized Color getColor(String colName, Color def) {
956 return getColor(colName, null, def);
957 }
958
959 /**
960 * only for preferences
961 * @param o color key
962 * @return translated color name
963 * @deprecated (since 12987) no longer supported
964 */
965 @Deprecated
966 public synchronized String getColorName(String o) {
967 Matcher m = COLOR_LAYER_PATTERN.matcher(o);
968 if (m.matches()) {
969 return tr("Layer: {0}", tr(I18n.escape(m.group(1))));
970 }
971 String fullKey = COLOR_PREFIX + o;
972 if (colornames.containsKey(fullKey)) {
973 String name = colornames.get(fullKey);
974 Matcher m2 = COLOR_MAPPAINT_PATTERN.matcher(name);
975 if (m2.matches()) {
976 return tr("Paint style {0}: {1}", tr(I18n.escape(m2.group(1))), tr(I18n.escape(m2.group(2))));
977 } else {
978 return tr(I18n.escape(colornames.get(fullKey)));
979 }
980 } else {
981 return fullKey;
982 }
983 }
984
985 /**
986 * Convenience method for accessing colour preferences.
987 * <p>
988 * To be removed: end of 2016
989 * @param colName name of the colour
990 * @param specName name of the special colour settings
991 * @param def default value
992 * @return a Color object for the configured colour, or the default value if none configured.
993 * @deprecated Use a {@link NamedColorProperty} instead.
994 * You can replace this by: <code>new NamedColorProperty(colName, def).getChildColor(specName)</code>
995 */
996 @Deprecated
997 public synchronized Color getColor(String colName, String specName, Color def) {
998 String colKey = org.openstreetmap.josm.data.preferences.ColorProperty.getColorKey(colName);
999 registerColor(colKey, colName);
1000 String colStr = specName != null ? get(COLOR_PREFIX+specName) : "";
1001 if (colStr.isEmpty()) {
1002 colStr = get(colKey, ColorHelper.color2html(def, true));
1003 }
1004 if (colStr != null && !colStr.isEmpty()) {
1005 return ColorHelper.html2color(colStr);
1006 } else {
1007 return def;
1008 }
1009 }
1010
1011 /**
1012 * Registers a color name conversion for the global color registry.
1013 * @param colKey The key
1014 * @param colName The name of the color.
1015 * @since 10824
1016 * @deprecated (since 12987) no longer supported
1017 */
1018 @Deprecated
1019 public void registerColor(String colKey, String colName) {
1020 if (!colKey.equals(colName)) {
1021 colornames.put(colKey, colName);
1022 }
1023 }
1024
1025 /**
1026 * Gets the default color that was registered with the preference
1027 * @param colKey The color name
1028 * @return The color
1029 * @deprecated (since 12989) no longer supported
1030 */
1031 @Deprecated
1032 public synchronized Color getDefaultColor(String colKey) {
1033 StringSetting col = Utils.cast(defaultsMap.get(COLOR_PREFIX+colKey), StringSetting.class);
1034 String colStr = col == null ? null : col.getValue();
1035 return colStr == null || colStr.isEmpty() ? null : ColorHelper.html2color(colStr);
1036 }
1037
1038 /**
1039 * Stores a color
1040 * @param colKey The color name
1041 * @param val The color
1042 * @return true if the setting was modified
1043 * @see NamedColorProperty#put(Color)
1044 * @deprecated (since 12987) no longer supported (see {@link NamedColorProperty})
1045 */
1046 @Deprecated
1047 public synchronized boolean putColor(String colKey, Color val) {
1048 return put(COLOR_PREFIX+colKey, val != null ? ColorHelper.color2html(val, true) : null);
1049 }
1050
1051 /**
1052 * Gets an integer preference
1053 * @param key The preference key
1054 * @param def The default value to use
1055 * @return The integer
1056 * @see IntegerProperty#get()
1057 * @deprecated use {@link IPreferences#getInt(String, int)}
1058 */
1059 @Deprecated
1060 public synchronized int getInteger(String key, int def) {
1061 String v = get(key, Integer.toString(def));
1062 if (v.isEmpty())
1063 return def;
1064
1065 try {
1066 return Integer.parseInt(v);
1067 } catch (NumberFormatException e) {
1068 // fall out
1069 Logging.trace(e);
1070 }
1071 return def;
1072 }
1073
1074 /**
1075 * Gets an integer that may be specialized
1076 * @param key The basic key
1077 * @param specName The sub-key to append to the key
1078 * @param def The default value
1079 * @return The integer value or the default value if it could not be parsed
1080 * @deprecated use {@link PreferencesUtils#getInteger(IPreferences, String, String, int)}
1081 */
1082 @Deprecated
1083 public synchronized int getInteger(String key, String specName, int def) {
1084 String v = get(key+'.'+specName);
1085 if (v.isEmpty())
1086 v = get(key, Integer.toString(def));
1087 if (v.isEmpty())
1088 return def;
1089
1090 try {
1091 return Integer.parseInt(v);
1092 } catch (NumberFormatException e) {
1093 // fall out
1094 Logging.trace(e);
1095 }
1096 return def;
1097 }
1098
1099 /**
1100 * Get a list of values for a certain key
1101 * @param key the identifier for the setting
1102 * @param def the default value.
1103 * @return the corresponding value if the property has been set before, {@code def} otherwise
1104 * @deprecated use {@link IPreferences#getList(java.lang.String, java.util.List)}
1105 */
1106 @Deprecated
1107 public Collection<String> getCollection(String key, Collection<String> def) {
1108 return getSetting(key, ListSetting.create(def), ListSetting.class).getValue();
1109 }
1110
1111 /**
1112 * Get a list of values for a certain key
1113 * @param key the identifier for the setting
1114 * @return the corresponding value if the property has been set before, an empty collection otherwise.
1115 * @deprecated use {@link IPreferences#getList(java.lang.String)}
1116 */
1117 @Deprecated
1118 public Collection<String> getCollection(String key) {
1119 Collection<String> val = getList(key, null);
1120 return val == null ? Collections.<String>emptyList() : val;
1121 }
1122
1123 /**
1124 * Removes a value from a given String collection
1125 * @param key The preference key the collection is stored with
1126 * @param value The value that should be removed in the collection
1127 * @see #getList(String)
1128 * @deprecated use {@link PreferencesUtils#removeFromList(IPreferences, String, String)}
1129 */
1130 @Deprecated
1131 public synchronized void removeFromCollection(String key, String value) {
1132 List<String> a = new ArrayList<>(getList(key, Collections.<String>emptyList()));
1133 a.remove(value);
1134 putList(key, a);
1135 }
1136
1137 /**
1138 * Set a value for a certain setting. The changed setting is saved to the preference file immediately.
1139 * Due to caching mechanisms on modern operating systems and hardware, this shouldn't be a performance problem.
1140 * @param key the unique identifier for the setting
1141 * @param setting the value of the setting. In case it is null, the key-value entry will be removed.
1142 * @return {@code true}, if something has changed (i.e. value is different than before)
1143 */
1144 @Override
1145 public boolean putSetting(final String key, Setting<?> setting) {
1146 CheckParameterUtil.ensureParameterNotNull(key);
1147 if (setting != null && setting.getValue() == null)
1148 throw new IllegalArgumentException("setting argument must not have null value");
1149 Setting<?> settingOld;
1150 Setting<?> settingCopy = null;
1151 synchronized (this) {
1152 if (setting == null) {
1153 settingOld = settingsMap.remove(key);
1154 if (settingOld == null)
1155 return false;
1156 } else {
1157 settingOld = settingsMap.get(key);
1158 if (setting.equals(settingOld))
1159 return false;
1160 if (settingOld == null && setting.equals(defaultsMap.get(key)))
1161 return false;
1162 settingCopy = setting.copy();
1163 settingsMap.put(key, settingCopy);
1164 }
1165 if (saveOnPut) {
1166 try {
1167 save();
1168 } catch (IOException e) {
1169 Logging.log(Logging.LEVEL_WARN, tr("Failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()), e);
1170 }
1171 }
1172 }
1173 // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
1174 firePreferenceChanged(key, settingOld, settingCopy);
1175 return true;
1176 }
1177
1178 /**
1179 * Get a setting of any type
1180 * @param key The key for the setting
1181 * @param def The default value to use if it was not found
1182 * @return The setting
1183 */
1184 public synchronized Setting<?> getSetting(String key, Setting<?> def) {
1185 return getSetting(key, def, Setting.class);
1186 }
1187
1188 /**
1189 * Get settings value for a certain key and provide default a value.
1190 * @param <T> the setting type
1191 * @param key the identifier for the setting
1192 * @param def the default value. For each call of getSetting() with a given key, the default value must be the same.
1193 * <code>def</code> must not be null, but the value of <code>def</code> can be null.
1194 * @param klass the setting type (same as T)
1195 * @return the corresponding value if the property has been set before, {@code def} otherwise
1196 */
1197 @SuppressWarnings("unchecked")
1198 @Override
1199 public synchronized <T extends Setting<?>> T getSetting(String key, T def, Class<T> klass) {
1200 CheckParameterUtil.ensureParameterNotNull(key);
1201 CheckParameterUtil.ensureParameterNotNull(def);
1202 Setting<?> oldDef = defaultsMap.get(key);
1203 if (oldDef != null && oldDef.isNew() && oldDef.getValue() != null && def.getValue() != null && !def.equals(oldDef)) {
1204 Logging.info("Defaults for " + key + " differ: " + def + " != " + defaultsMap.get(key));
1205 }
1206 if (def.getValue() != null || oldDef == null) {
1207 Setting<?> defCopy = def.copy();
1208 defCopy.setTime(System.currentTimeMillis() / 1000);
1209 defCopy.setNew(true);
1210 defaultsMap.put(key, defCopy);
1211 }
1212 Setting<?> prop = settingsMap.get(key);
1213 if (klass.isInstance(prop)) {
1214 return (T) prop;
1215 } else {
1216 return def;
1217 }
1218 }
1219
1220 /**
1221 * Put a collection.
1222 * @param key key
1223 * @param value value
1224 * @return {@code true}, if something has changed (i.e. value is different than before)
1225 * @deprecated use {@link IPreferences#putList(java.lang.String, java.util.List)}
1226 */
1227 @Deprecated
1228 public boolean putCollection(String key, Collection<String> value) {
1229 return putSetting(key, value == null ? null : ListSetting.create(value));
1230 }
1231
1232 /**
1233 * Saves at most {@code maxsize} items of collection {@code val}.
1234 * @param key key
1235 * @param maxsize max number of items to save
1236 * @param val value
1237 * @return {@code true}, if something has changed (i.e. value is different than before)
1238 * @deprecated use {@link PreferencesUtils#putListBounded(IPreferences, String, int, List)}
1239 */
1240 @Deprecated
1241 public boolean putCollectionBounded(String key, int maxsize, Collection<String> val) {
1242 List<String> newCollection = new ArrayList<>(Math.min(maxsize, val.size()));
1243 for (String i : val) {
1244 if (newCollection.size() >= maxsize) {
1245 break;
1246 }
1247 newCollection.add(i);
1248 }
1249 return putList(key, newCollection);
1250 }
1251
1252 /**
1253 * Used to read a 2-dimensional array of strings from the preference file.
1254 * If not a single entry could be found, <code>def</code> is returned.
1255 * @param key preference key
1256 * @param def default array value
1257 * @return array value
1258 * @deprecated use {@link #getListOfLists(java.lang.String, java.util.List)}
1259 */
1260 @Deprecated
1261 @SuppressWarnings({ "unchecked", "rawtypes" })
1262 public synchronized Collection<Collection<String>> getArray(String key, Collection<Collection<String>> def) {
1263 ListListSetting val = getSetting(key, ListListSetting.create(def), ListListSetting.class);
1264 return (Collection) val.getValue();
1265 }
1266
1267 /**
1268 * Gets a collection of string collections for the given key
1269 * @param key The key
1270 * @return The collection of string collections or an empty collection as default
1271 * @deprecated use {@link IPreferences#getListOfLists(java.lang.String)}
1272 */
1273 @Deprecated
1274 public Collection<Collection<String>> getArray(String key) {
1275 Collection<Collection<String>> res = getArray(key, null);
1276 return res == null ? Collections.<Collection<String>>emptyList() : res;
1277 }
1278
1279 /**
1280 * Put an array.
1281 * @param key key
1282 * @param value value
1283 * @return {@code true}, if something has changed (i.e. value is different than before)
1284 * @deprecated use {@link IPreferences#putListOfLists(java.lang.String, java.util.List)}
1285 */
1286 @Deprecated
1287 public boolean putArray(String key, Collection<Collection<String>> value) {
1288 return putSetting(key, value == null ? null : ListListSetting.create(value));
1289 }
1290
1291 /**
1292 * Gets a collection of key/value maps.
1293 * @param key The key to search at
1294 * @param def The default value to use
1295 * @return The stored value or the default one if it could not be parsed
1296 * @deprecated use {@link IPreferences#getListOfMaps(java.lang.String, java.util.List)}
1297 */
1298 @Deprecated
1299 public Collection<Map<String, String>> getListOfStructs(String key, Collection<Map<String, String>> def) {
1300 return getSetting(key, new MapListSetting(def == null ? null : new ArrayList<>(def)), MapListSetting.class).getValue();
1301 }
1302
1303 /**
1304 * Stores a list of structs
1305 * @param key The key to store the list in
1306 * @param value A list of key/value maps
1307 * @return <code>true</code> if the value was changed
1308 * @see #getListOfMaps(java.lang.String, java.util.List)
1309 * @deprecated use {@link IPreferences#putListOfMaps(java.lang.String, java.util.List)}
1310 */
1311 @Deprecated
1312 public boolean putListOfStructs(String key, Collection<Map<String, String>> value) {
1313 return putSetting(key, value == null ? null : new MapListSetting(new ArrayList<>(value)));
1314 }
1315
1316 /**
1317 * Annotation used for converting objects to String Maps and vice versa.
1318 * Indicates that a certain field should be considered in the conversion process. Otherwise it is ignored.
1319 *
1320 * @see #serializeStruct(java.lang.Object, java.lang.Class)
1321 * @see #deserializeStruct(java.util.Map, java.lang.Class)
1322 * @deprecated use {@link StructUtils.StructEntry}
1323 */
1324 @Deprecated
1325 @Retention(RetentionPolicy.RUNTIME) // keep annotation at runtime
1326 public @interface pref { }
1327
1328 /**
1329 * Annotation used for converting objects to String Maps.
1330 * Indicates that a certain field should be written to the map, even if the value is the same as the default value.
1331 *
1332 * @see #serializeStruct(java.lang.Object, java.lang.Class)
1333 * @deprecated use {@link StructUtils.WriteExplicitly}
1334 */
1335 @Deprecated
1336 @Retention(RetentionPolicy.RUNTIME) // keep annotation at runtime
1337 public @interface writeExplicitly { }
1338
1339 /**
1340 * Get a list of hashes which are represented by a struct-like class.
1341 * Possible properties are given by fields of the class klass that have the @pref annotation.
1342 * Default constructor is used to initialize the struct objects, properties then override some of these default values.
1343 * @param <T> klass type
1344 * @param key main preference key
1345 * @param klass The struct class
1346 * @return a list of objects of type T or an empty list if nothing was found
1347 * @deprecated use {@link StructUtils#getListOfStructs(IPreferences, String, Class)}
1348 */
1349 @Deprecated
1350 public <T> List<T> getListOfStructs(String key, Class<T> klass) {
1351 return StructUtils.getListOfStructs(this, key, klass);
1352 }
1353
1354 /**
1355 * same as above, but returns def if nothing was found
1356 * @param <T> klass type
1357 * @param key main preference key
1358 * @param def default value
1359 * @param klass The struct class
1360 * @return a list of objects of type T or {@code def} if nothing was found
1361 * @deprecated use {@link StructUtils#getListOfStructs(IPreferences, String, Collection, Class)}
1362 */
1363 @Deprecated
1364 public <T> List<T> getListOfStructs(String key, Collection<T> def, Class<T> klass) {
1365 return StructUtils.getListOfStructs(this, key, def, klass);
1366 }
1367
1368 /**
1369 * Convenience method that saves a MapListSetting which is provided as a collection of objects.
1370 *
1371 * Each object is converted to a <code>Map&lt;String, String&gt;</code> using the fields with {@link pref} annotation.
1372 * The field name is the key and the value will be converted to a string.
1373 *
1374 * Considers only fields that have the @pref annotation.
1375 * In addition it does not write fields with null values. (Thus they are cleared)
1376 * Default values are given by the field values after default constructor has been called.
1377 * Fields equal to the default value are not written unless the field has the @writeExplicitly annotation.
1378 * @param <T> the class,
1379 * @param key main preference key
1380 * @param val the list that is supposed to be saved
1381 * @param klass The struct class
1382 * @return true if something has changed
1383 * @deprecated use {@link StructUtils#putListOfStructs(IPreferences, String, Collection, Class)}
1384 */
1385 @Deprecated
1386 public <T> boolean putListOfStructs(String key, Collection<T> val, Class<T> klass) {
1387 return StructUtils.putListOfStructs(this, key, val, klass);
1388 }
1389
1390 /**
1391 * Convert an object to a String Map, by using field names and values as map key and value.
1392 *
1393 * The field value is converted to a String.
1394 *
1395 * Only fields with annotation {@link pref} are taken into account.
1396 *
1397 * Fields will not be written to the map if the value is null or unchanged
1398 * (compared to an object created with the no-arg-constructor).
1399 * The {@link writeExplicitly} annotation overrides this behavior, i.e. the default value will also be written.
1400 *
1401 * @param <T> the class of the object <code>struct</code>
1402 * @param struct the object to be converted
1403 * @param klass the class T
1404 * @return the resulting map (same data content as <code>struct</code>)
1405 * @deprecated use {@link StructUtils#serializeStruct(java.lang.Object, java.lang.Class)}
1406 */
1407 @Deprecated
1408 public static <T> Map<String, String> serializeStruct(T struct, Class<T> klass) {
1409 return StructUtils.serializeStruct(struct, klass);
1410 }
1411
1412 /**
1413 * Converts a String-Map to an object of a certain class, by comparing map keys to field names of the class and assigning
1414 * map values to the corresponding fields.
1415 *
1416 * The map value (a String) is converted to the field type. Supported types are: boolean, Boolean, int, Integer, double,
1417 * Double, String, Map&lt;String, String&gt; and Map&lt;String, List&lt;String&gt;&gt;.
1418 *
1419 * Only fields with annotation {@link pref} are taken into account.
1420 * @param <T> the class
1421 * @param hash the string map with initial values
1422 * @param klass the class T
1423 * @return an object of class T, initialized as described above
1424 * @deprecated use {@link StructUtils#deserializeStruct(java.util.Map, java.lang.Class)}
1425 */
1426 @Deprecated
1427 public static <T> T deserializeStruct(Map<String, String> hash, Class<T> klass) {
1428 return StructUtils.deserializeStruct(hash, klass);
1429 }
1430
1431 @Override
1432 public Set<String> getKeySet() {
1433 return Collections.unmodifiableSet(settingsMap.keySet());
1434 }
1435
1436 /**
1437 * Gets a map of all settings that are currently stored
1438 * @return The settings
1439 */
1440 public Map<String, Setting<?>> getAllSettings() {
1441 return new TreeMap<>(settingsMap);
1442 }
1443
1444 /**
1445 * Gets a map of all currently known defaults
1446 * @return The map (key/setting)
1447 */
1448 public Map<String, Setting<?>> getAllDefaults() {
1449 return new TreeMap<>(defaultsMap);
1450 }
1451
1452 /**
1453 * Replies the collection of plugin site URLs from where plugin lists can be downloaded.
1454 * @return the collection of plugin site URLs
1455 * @see #getOnlinePluginSites
1456 */
1457 public Collection<String> getPluginSites() {
1458 return getList("pluginmanager.sites", Collections.singletonList(Main.getJOSMWebsite()+"/pluginicons%<?plugins=>"));
1459 }
1460
1461 /**
1462 * Returns the list of plugin sites available according to offline mode settings.
1463 * @return the list of available plugin sites
1464 * @since 8471
1465 */
1466 public Collection<String> getOnlinePluginSites() {
1467 Collection<String> pluginSites = new ArrayList<>(getPluginSites());
1468 for (Iterator<String> it = pluginSites.iterator(); it.hasNext();) {
1469 try {
1470 OnlineResource.JOSM_WEBSITE.checkOfflineAccess(it.next(), Main.getJOSMWebsite());
1471 } catch (OfflineAccessException ex) {
1472 Logging.log(Logging.LEVEL_WARN, ex);
1473 it.remove();
1474 }
1475 }
1476 return pluginSites;
1477 }
1478
1479 /**
1480 * Sets the collection of plugin site URLs.
1481 *
1482 * @param sites the site URLs
1483 */
1484 public void setPluginSites(Collection<String> sites) {
1485 putList("pluginmanager.sites", new ArrayList<>(sites));
1486 }
1487
1488 /**
1489 * Returns XML describing these preferences.
1490 * @param nopass if password must be excluded
1491 * @return XML
1492 */
1493 public String toXML(boolean nopass) {
1494 return toXML(settingsMap.entrySet(), nopass, false);
1495 }
1496
1497 /**
1498 * Returns XML describing the given preferences.
1499 * @param settings preferences settings
1500 * @param nopass if password must be excluded
1501 * @param defaults true, if default values are converted to XML, false for
1502 * regular preferences
1503 * @return XML
1504 */
1505 public String toXML(Collection<Entry<String, Setting<?>>> settings, boolean nopass, boolean defaults) {
1506 try (
1507 StringWriter sw = new StringWriter();
1508 PreferencesWriter prefWriter = new PreferencesWriter(new PrintWriter(sw), nopass, defaults)
1509 ) {
1510 prefWriter.write(settings);
1511 sw.flush();
1512 return sw.toString();
1513 } catch (IOException e) {
1514 Logging.error(e);
1515 return null;
1516 }
1517 }
1518
1519 /**
1520 * Removes obsolete preference settings. If you throw out a once-used preference
1521 * setting, add it to the list here with an expiry date (written as comment). If you
1522 * see something with an expiry date in the past, remove it from the list.
1523 * @param loadedVersion JOSM version when the preferences file was written
1524 */
1525 private void removeObsolete(int loadedVersion) {
1526 for (String key : OBSOLETE_PREF_KEYS) {
1527 if (settingsMap.containsKey(key)) {
1528 settingsMap.remove(key);
1529 Logging.info(tr("Preference setting {0} has been removed since it is no longer used.", key));
1530 }
1531 }
1532 }
1533
1534 /**
1535 * Enables or not the preferences file auto-save mechanism (save each time a setting is changed).
1536 * This behaviour is enabled by default.
1537 * @param enable if {@code true}, makes JOSM save preferences file each time a setting is changed
1538 * @since 7085
1539 */
1540 public final void enableSaveOnPut(boolean enable) {
1541 synchronized (this) {
1542 saveOnPut = enable;
1543 }
1544 }
1545}
Note: See TracBrowser for help on using the repository browser.