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

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

see #15229 - code refactoring - move getAllPrefix* methods from Preferences to AbstractPreferences

  • Property svn:eol-style set to native
File size: 29.6 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;
6import static org.openstreetmap.josm.tools.Utils.getSystemEnv;
7import static org.openstreetmap.josm.tools.Utils.getSystemProperty;
8
9import java.awt.GraphicsEnvironment;
10import java.io.File;
11import java.io.IOException;
12import java.io.PrintWriter;
13import java.io.Reader;
14import java.io.StringWriter;
15import java.nio.charset.StandardCharsets;
16import java.nio.file.InvalidPathException;
17import java.util.ArrayList;
18import java.util.Collection;
19import java.util.Collections;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.Iterator;
23import java.util.Map;
24import java.util.Map.Entry;
25import java.util.Optional;
26import java.util.Set;
27import java.util.SortedMap;
28import java.util.TreeMap;
29import java.util.concurrent.TimeUnit;
30import java.util.function.Predicate;
31import java.util.stream.Stream;
32
33import javax.swing.JOptionPane;
34import javax.xml.stream.XMLStreamException;
35
36import org.openstreetmap.josm.Main;
37import org.openstreetmap.josm.data.preferences.ColorInfo;
38import org.openstreetmap.josm.data.preferences.NamedColorProperty;
39import org.openstreetmap.josm.data.preferences.PreferencesReader;
40import org.openstreetmap.josm.data.preferences.PreferencesWriter;
41import org.openstreetmap.josm.io.OfflineAccessException;
42import org.openstreetmap.josm.io.OnlineResource;
43import org.openstreetmap.josm.spi.preferences.AbstractPreferences;
44import org.openstreetmap.josm.spi.preferences.Config;
45import org.openstreetmap.josm.spi.preferences.IBaseDirectories;
46import org.openstreetmap.josm.spi.preferences.ListSetting;
47import org.openstreetmap.josm.spi.preferences.Setting;
48import org.openstreetmap.josm.tools.CheckParameterUtil;
49import org.openstreetmap.josm.tools.ListenerList;
50import org.openstreetmap.josm.tools.Logging;
51import org.openstreetmap.josm.tools.PlatformManager;
52import org.openstreetmap.josm.tools.Utils;
53import org.xml.sax.SAXException;
54
55/**
56 * This class holds all preferences for JOSM.
57 *
58 * Other classes can register their beloved properties here. All properties will be
59 * saved upon set-access.
60 *
61 * Each property is a key=setting pair, where key is a String and setting can be one of
62 * 4 types:
63 * string, list, list of lists and list of maps.
64 * In addition, each key has a unique default value that is set when the value is first
65 * accessed using one of the get...() methods. You can use the same preference
66 * key in different parts of the code, but the default value must be the same
67 * everywhere. A default value of null means, the setting has been requested, but
68 * no default value was set. This is used in advanced preferences to present a list
69 * off all possible settings.
70 *
71 * At the moment, you cannot put the empty string for string properties.
72 * put(key, "") means, the property is removed.
73 *
74 * @author imi
75 * @since 74
76 */
77public class Preferences extends AbstractPreferences {
78
79 private static final String[] OBSOLETE_PREF_KEYS = {
80 };
81
82 private static final long MAX_AGE_DEFAULT_PREFERENCES = TimeUnit.DAYS.toSeconds(50);
83
84 private final IBaseDirectories dirs;
85
86 /**
87 * Determines if preferences file is saved each time a property is changed.
88 */
89 private boolean saveOnPut = true;
90
91 /**
92 * Maps the setting name to the current value of the setting.
93 * The map must not contain null as key or value. The mapped setting objects
94 * must not have a null value.
95 */
96 protected final SortedMap<String, Setting<?>> settingsMap = new TreeMap<>();
97
98 /**
99 * Maps the setting name to the default value of the setting.
100 * The map must not contain null as key or value. The value of the mapped
101 * setting objects can be null.
102 */
103 protected final SortedMap<String, Setting<?>> defaultsMap = new TreeMap<>();
104
105 private final Predicate<Entry<String, Setting<?>>> NO_DEFAULT_SETTINGS_ENTRY =
106 e -> !e.getValue().equals(defaultsMap.get(e.getKey()));
107
108 /**
109 * Indicates whether {@link #init(boolean)} completed successfully.
110 * Used to decide whether to write backup preference file in {@link #save()}
111 */
112 protected boolean initSuccessful;
113
114 private final ListenerList<org.openstreetmap.josm.spi.preferences.PreferenceChangedListener> listeners = ListenerList.create();
115
116 private final HashMap<String, ListenerList<org.openstreetmap.josm.spi.preferences.PreferenceChangedListener>> keyListeners = new HashMap<>();
117
118 /**
119 * Constructs a new {@code Preferences}.
120 */
121 public Preferences() {
122 this.dirs = Config.getDirs();
123 }
124
125 /**
126 * Constructs a new {@code Preferences}.
127 *
128 * @param dirs the directories to use for saving the preferences
129 */
130 public Preferences(IBaseDirectories dirs) {
131 this.dirs = dirs;
132 }
133
134 /**
135 * Constructs a new {@code Preferences} from an existing instance.
136 * @param pref existing preferences to copy
137 * @since 12634
138 */
139 public Preferences(Preferences pref) {
140 this(pref.dirs);
141 settingsMap.putAll(pref.settingsMap);
142 defaultsMap.putAll(pref.defaultsMap);
143 }
144
145 /**
146 * Adds a new preferences listener.
147 * @param listener The listener to add
148 * @since 12881
149 */
150 @Override
151 public void addPreferenceChangeListener(org.openstreetmap.josm.spi.preferences.PreferenceChangedListener listener) {
152 if (listener != null) {
153 listeners.addListener(listener);
154 }
155 }
156
157 /**
158 * Removes a preferences listener.
159 * @param listener The listener to remove
160 * @since 12881
161 */
162 @Override
163 public void removePreferenceChangeListener(org.openstreetmap.josm.spi.preferences.PreferenceChangedListener listener) {
164 listeners.removeListener(listener);
165 }
166
167 /**
168 * Adds a listener that only listens to changes in one preference
169 * @param key The preference key to listen to
170 * @param listener The listener to add.
171 * @since 12881
172 */
173 @Override
174 public void addKeyPreferenceChangeListener(String key, org.openstreetmap.josm.spi.preferences.PreferenceChangedListener listener) {
175 listenersForKey(key).addListener(listener);
176 }
177
178 /**
179 * Adds a weak listener that only listens to changes in one preference
180 * @param key The preference key to listen to
181 * @param listener The listener to add.
182 * @since 10824
183 */
184 public void addWeakKeyPreferenceChangeListener(String key, org.openstreetmap.josm.spi.preferences.PreferenceChangedListener listener) {
185 listenersForKey(key).addWeakListener(listener);
186 }
187
188 private ListenerList<org.openstreetmap.josm.spi.preferences.PreferenceChangedListener> listenersForKey(String key) {
189 return keyListeners.computeIfAbsent(key, k -> ListenerList.create());
190 }
191
192 /**
193 * Removes a listener that only listens to changes in one preference
194 * @param key The preference key to listen to
195 * @param listener The listener to add.
196 * @since 12881
197 */
198 @Override
199 public void removeKeyPreferenceChangeListener(String key, org.openstreetmap.josm.spi.preferences.PreferenceChangedListener listener) {
200 Optional.ofNullable(keyListeners.get(key)).orElseThrow(
201 () -> new IllegalArgumentException("There are no listeners registered for " + key))
202 .removeListener(listener);
203 }
204
205 protected void firePreferenceChanged(String key, Setting<?> oldValue, Setting<?> newValue) {
206 final org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent evt =
207 new org.openstreetmap.josm.spi.preferences.DefaultPreferenceChangeEvent(key, oldValue, newValue);
208 listeners.fireEvent(listener -> listener.preferenceChanged(evt));
209
210 ListenerList<org.openstreetmap.josm.spi.preferences.PreferenceChangedListener> forKey = keyListeners.get(key);
211 if (forKey != null) {
212 forKey.fireEvent(listener -> listener.preferenceChanged(evt));
213 }
214 }
215
216 /**
217 * Get the base name of the JOSM directories for preferences, cache and user data.
218 * Default value is "JOSM", unless overridden by system property "josm.dir.name".
219 * @return the base name of the JOSM directories for preferences, cache and user data
220 */
221 public String getJOSMDirectoryBaseName() {
222 String name = getSystemProperty("josm.dir.name");
223 if (name != null)
224 return name;
225 else
226 return "JOSM";
227 }
228
229 /**
230 * Get the base directories associated with this preference instance.
231 * @return the base directories
232 */
233 public IBaseDirectories getDirs() {
234 return dirs;
235 }
236
237 /**
238 * Returns the user preferences file (preferences.xml).
239 * @return The user preferences file (preferences.xml)
240 */
241 public File getPreferenceFile() {
242 return new File(dirs.getPreferencesDirectory(false), "preferences.xml");
243 }
244
245 /**
246 * Returns the cache file for default preferences.
247 * @return the cache file for default preferences
248 */
249 public File getDefaultsCacheFile() {
250 return new File(dirs.getCacheDirectory(true), "default_preferences.xml");
251 }
252
253 /**
254 * Returns the user plugin directory.
255 * @return The user plugin directory
256 */
257 public File getPluginsDirectory() {
258 return new File(dirs.getUserDataDirectory(false), "plugins");
259 }
260
261 private static void addPossibleResourceDir(Set<String> locations, String s) {
262 if (s != null) {
263 if (!s.endsWith(File.separator)) {
264 s += File.separator;
265 }
266 locations.add(s);
267 }
268 }
269
270 /**
271 * Returns a set of all existing directories where resources could be stored.
272 * @return A set of all existing directories where resources could be stored.
273 */
274 public static Collection<String> getAllPossiblePreferenceDirs() {
275 Set<String> locations = new HashSet<>();
276 addPossibleResourceDir(locations, Config.getDirs().getPreferencesDirectory(false).getPath());
277 addPossibleResourceDir(locations, Config.getDirs().getUserDataDirectory(false).getPath());
278 addPossibleResourceDir(locations, getSystemEnv("JOSM_RESOURCES"));
279 addPossibleResourceDir(locations, getSystemProperty("josm.resources"));
280 locations.addAll(PlatformManager.getPlatform().getPossiblePreferenceDirs());
281 return locations;
282 }
283
284 /**
285 * Get all named colors, including customized and the default ones.
286 * @return a map of all named colors (maps preference key to {@link ColorInfo})
287 */
288 public synchronized Map<String, ColorInfo> getAllNamedColors() {
289 final Map<String, ColorInfo> all = new TreeMap<>();
290 for (final Entry<String, Setting<?>> e : settingsMap.entrySet()) {
291 if (!e.getKey().startsWith(NamedColorProperty.NAMED_COLOR_PREFIX))
292 continue;
293 Utils.instanceOfAndCast(e.getValue(), ListSetting.class)
294 .map(ListSetting::getValue)
295 .map(lst -> ColorInfo.fromPref(lst, false))
296 .ifPresent(info -> all.put(e.getKey(), info));
297 }
298 for (final Entry<String, Setting<?>> e : defaultsMap.entrySet()) {
299 if (!e.getKey().startsWith(NamedColorProperty.NAMED_COLOR_PREFIX))
300 continue;
301 Utils.instanceOfAndCast(e.getValue(), ListSetting.class)
302 .map(ListSetting::getValue)
303 .map(lst -> ColorInfo.fromPref(lst, true))
304 .ifPresent(infoDef -> {
305 ColorInfo info = all.get(e.getKey());
306 if (info == null) {
307 all.put(e.getKey(), infoDef);
308 } else {
309 info.setDefaultValue(infoDef.getDefaultValue());
310 }
311 });
312 }
313 return all;
314 }
315
316 /**
317 * Called after every put. In case of a problem, do nothing but output the error in log.
318 * @throws IOException if any I/O error occurs
319 */
320 public synchronized void save() throws IOException {
321 save(getPreferenceFile(), settingsMap.entrySet().stream().filter(NO_DEFAULT_SETTINGS_ENTRY), false);
322 }
323
324 /**
325 * Stores the defaults to the defaults file
326 * @throws IOException If the file could not be saved
327 */
328 public synchronized void saveDefaults() throws IOException {
329 save(getDefaultsCacheFile(), defaultsMap.entrySet().stream(), true);
330 }
331
332 protected void save(File prefFile, Stream<Entry<String, Setting<?>>> settings, boolean defaults) throws IOException {
333 if (!defaults) {
334 /* currently unused, but may help to fix configuration issues in future */
335 putInt("josm.version", Version.getInstance().getVersion());
336 }
337
338 File backupFile = new File(prefFile + "_backup");
339
340 // Backup old preferences if there are old preferences
341 if (initSuccessful && prefFile.exists() && prefFile.length() > 0) {
342 Utils.copyFile(prefFile, backupFile);
343 }
344
345 try (PreferencesWriter writer = new PreferencesWriter(
346 new PrintWriter(new File(prefFile + "_tmp"), StandardCharsets.UTF_8.name()), false, defaults)) {
347 writer.write(settings);
348 } catch (SecurityException e) {
349 throw new IOException(e);
350 }
351
352 File tmpFile = new File(prefFile + "_tmp");
353 Utils.copyFile(tmpFile, prefFile);
354 Utils.deleteFile(tmpFile, marktr("Unable to delete temporary file {0}"));
355
356 setCorrectPermissions(prefFile);
357 setCorrectPermissions(backupFile);
358 }
359
360 private static void setCorrectPermissions(File file) {
361 if (!file.setReadable(false, false) && Logging.isTraceEnabled()) {
362 Logging.trace(tr("Unable to set file non-readable {0}", file.getAbsolutePath()));
363 }
364 if (!file.setWritable(false, false) && Logging.isTraceEnabled()) {
365 Logging.trace(tr("Unable to set file non-writable {0}", file.getAbsolutePath()));
366 }
367 if (!file.setExecutable(false, false) && Logging.isTraceEnabled()) {
368 Logging.trace(tr("Unable to set file non-executable {0}", file.getAbsolutePath()));
369 }
370 if (!file.setReadable(true, true) && Logging.isTraceEnabled()) {
371 Logging.trace(tr("Unable to set file readable {0}", file.getAbsolutePath()));
372 }
373 if (!file.setWritable(true, true) && Logging.isTraceEnabled()) {
374 Logging.trace(tr("Unable to set file writable {0}", file.getAbsolutePath()));
375 }
376 }
377
378 /**
379 * Loads preferences from settings file.
380 * @throws IOException if any I/O error occurs while reading the file
381 * @throws SAXException if the settings file does not contain valid XML
382 * @throws XMLStreamException if an XML error occurs while parsing the file (after validation)
383 */
384 protected void load() throws IOException, SAXException, XMLStreamException {
385 File pref = getPreferenceFile();
386 PreferencesReader.validateXML(pref);
387 PreferencesReader reader = new PreferencesReader(pref, false);
388 reader.parse();
389 settingsMap.clear();
390 settingsMap.putAll(reader.getSettings());
391 removeObsolete(reader.getVersion());
392 }
393
394 /**
395 * Loads default preferences from default settings cache file.
396 *
397 * Discards entries older than {@link #MAX_AGE_DEFAULT_PREFERENCES}.
398 *
399 * @throws IOException if any I/O error occurs while reading the file
400 * @throws SAXException if the settings file does not contain valid XML
401 * @throws XMLStreamException if an XML error occurs while parsing the file (after validation)
402 */
403 protected void loadDefaults() throws IOException, XMLStreamException, SAXException {
404 File def = getDefaultsCacheFile();
405 PreferencesReader.validateXML(def);
406 PreferencesReader reader = new PreferencesReader(def, true);
407 reader.parse();
408 defaultsMap.clear();
409 long minTime = System.currentTimeMillis() / 1000 - MAX_AGE_DEFAULT_PREFERENCES;
410 for (Entry<String, Setting<?>> e : reader.getSettings().entrySet()) {
411 if (e.getValue().getTime() >= minTime) {
412 defaultsMap.put(e.getKey(), e.getValue());
413 }
414 }
415 }
416
417 /**
418 * Loads preferences from XML reader.
419 * @param in XML reader
420 * @throws XMLStreamException if any XML stream error occurs
421 * @throws IOException if any I/O error occurs
422 */
423 public void fromXML(Reader in) throws XMLStreamException, IOException {
424 PreferencesReader reader = new PreferencesReader(in, false);
425 reader.parse();
426 settingsMap.clear();
427 settingsMap.putAll(reader.getSettings());
428 }
429
430 /**
431 * Initializes preferences.
432 * @param reset if {@code true}, current settings file is replaced by the default one
433 */
434 public void init(boolean reset) {
435 initSuccessful = false;
436 // get the preferences.
437 File prefDir = dirs.getPreferencesDirectory(false);
438 if (prefDir.exists()) {
439 if (!prefDir.isDirectory()) {
440 Logging.warn(tr("Failed to initialize preferences. Preference directory ''{0}'' is not a directory.",
441 prefDir.getAbsoluteFile()));
442 if (!GraphicsEnvironment.isHeadless()) {
443 JOptionPane.showMessageDialog(
444 Main.parent,
445 tr("<html>Failed to initialize preferences.<br>Preference directory ''{0}'' is not a directory.</html>",
446 prefDir.getAbsoluteFile()),
447 tr("Error"),
448 JOptionPane.ERROR_MESSAGE
449 );
450 }
451 return;
452 }
453 } else {
454 if (!prefDir.mkdirs()) {
455 Logging.warn(tr("Failed to initialize preferences. Failed to create missing preference directory: {0}",
456 prefDir.getAbsoluteFile()));
457 if (!GraphicsEnvironment.isHeadless()) {
458 JOptionPane.showMessageDialog(
459 Main.parent,
460 tr("<html>Failed to initialize preferences.<br>Failed to create missing preference directory: {0}</html>",
461 prefDir.getAbsoluteFile()),
462 tr("Error"),
463 JOptionPane.ERROR_MESSAGE
464 );
465 }
466 return;
467 }
468 }
469
470 File preferenceFile = getPreferenceFile();
471 try {
472 if (!preferenceFile.exists()) {
473 Logging.info(tr("Missing preference file ''{0}''. Creating a default preference file.", preferenceFile.getAbsoluteFile()));
474 resetToDefault();
475 save();
476 } else if (reset) {
477 File backupFile = new File(prefDir, "preferences.xml.bak");
478 PlatformManager.getPlatform().rename(preferenceFile, backupFile);
479 Logging.warn(tr("Replacing existing preference file ''{0}'' with default preference file.", preferenceFile.getAbsoluteFile()));
480 resetToDefault();
481 save();
482 }
483 } catch (IOException | InvalidPathException e) {
484 Logging.error(e);
485 if (!GraphicsEnvironment.isHeadless()) {
486 JOptionPane.showMessageDialog(
487 Main.parent,
488 tr("<html>Failed to initialize preferences.<br>Failed to reset preference file to default: {0}</html>",
489 getPreferenceFile().getAbsoluteFile()),
490 tr("Error"),
491 JOptionPane.ERROR_MESSAGE
492 );
493 }
494 return;
495 }
496 try {
497 load();
498 initSuccessful = true;
499 } catch (IOException | SAXException | XMLStreamException e) {
500 Logging.error(e);
501 File backupFile = new File(prefDir, "preferences.xml.bak");
502 if (!GraphicsEnvironment.isHeadless()) {
503 JOptionPane.showMessageDialog(
504 Main.parent,
505 tr("<html>Preferences file had errors.<br> Making backup of old one to <br>{0}<br> " +
506 "and creating a new default preference file.</html>",
507 backupFile.getAbsoluteFile()),
508 tr("Error"),
509 JOptionPane.ERROR_MESSAGE
510 );
511 }
512 PlatformManager.getPlatform().rename(preferenceFile, backupFile);
513 try {
514 resetToDefault();
515 save();
516 } catch (IOException e1) {
517 Logging.error(e1);
518 Logging.warn(tr("Failed to initialize preferences. Failed to reset preference file to default: {0}", getPreferenceFile()));
519 }
520 }
521 File def = getDefaultsCacheFile();
522 if (def.exists()) {
523 try {
524 loadDefaults();
525 } catch (IOException | XMLStreamException | SAXException e) {
526 Logging.error(e);
527 Logging.warn(tr("Failed to load defaults cache file: {0}", def));
528 defaultsMap.clear();
529 if (!def.delete()) {
530 Logging.warn(tr("Failed to delete faulty defaults cache file: {0}", def));
531 }
532 }
533 }
534 }
535
536 /**
537 * Resets the preferences to their initial state. This resets all values and file associations.
538 * The default values and listeners are not removed.
539 * <p>
540 * It is meant to be called before {@link #init(boolean)}
541 * @since 10876
542 */
543 public void resetToInitialState() {
544 resetToDefault();
545 saveOnPut = true;
546 initSuccessful = false;
547 }
548
549 /**
550 * Reset all values stored in this map to the default values. This clears the preferences.
551 */
552 public final void resetToDefault() {
553 settingsMap.clear();
554 }
555
556 /**
557 * Set a value for a certain setting. The changed setting is saved to the preference file immediately.
558 * Due to caching mechanisms on modern operating systems and hardware, this shouldn't be a performance problem.
559 * @param key the unique identifier for the setting
560 * @param setting the value of the setting. In case it is null, the key-value entry will be removed.
561 * @return {@code true}, if something has changed (i.e. value is different than before)
562 */
563 @Override
564 public boolean putSetting(final String key, Setting<?> setting) {
565 CheckParameterUtil.ensureParameterNotNull(key);
566 if (setting != null && setting.getValue() == null)
567 throw new IllegalArgumentException("setting argument must not have null value");
568 Setting<?> settingOld;
569 Setting<?> settingCopy = null;
570 synchronized (this) {
571 if (setting == null) {
572 settingOld = settingsMap.remove(key);
573 if (settingOld == null)
574 return false;
575 } else {
576 settingOld = settingsMap.get(key);
577 if (setting.equals(settingOld))
578 return false;
579 if (settingOld == null && setting.equals(defaultsMap.get(key)))
580 return false;
581 settingCopy = setting.copy();
582 settingsMap.put(key, settingCopy);
583 }
584 if (saveOnPut) {
585 try {
586 save();
587 } catch (IOException | InvalidPathException e) {
588 File file = getPreferenceFile();
589 try {
590 file = file.getAbsoluteFile();
591 } catch (SecurityException ex) {
592 Logging.trace(ex);
593 }
594 Logging.log(Logging.LEVEL_WARN, tr("Failed to persist preferences to ''{0}''", file), e);
595 }
596 }
597 }
598 // Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
599 firePreferenceChanged(key, settingOld, settingCopy);
600 return true;
601 }
602
603 /**
604 * Get a setting of any type
605 * @param key The key for the setting
606 * @param def The default value to use if it was not found
607 * @return The setting
608 */
609 public synchronized Setting<?> getSetting(String key, Setting<?> def) {
610 return getSetting(key, def, Setting.class);
611 }
612
613 /**
614 * Get settings value for a certain key and provide default a value.
615 * @param <T> the setting type
616 * @param key the identifier for the setting
617 * @param def the default value. For each call of getSetting() with a given key, the default value must be the same.
618 * <code>def</code> must not be null, but the value of <code>def</code> can be null.
619 * @param klass the setting type (same as T)
620 * @return the corresponding value if the property has been set before, {@code def} otherwise
621 */
622 @SuppressWarnings("unchecked")
623 @Override
624 public synchronized <T extends Setting<?>> T getSetting(String key, T def, Class<T> klass) {
625 CheckParameterUtil.ensureParameterNotNull(key);
626 CheckParameterUtil.ensureParameterNotNull(def);
627 Setting<?> oldDef = defaultsMap.get(key);
628 if (oldDef != null && oldDef.isNew() && oldDef.getValue() != null && def.getValue() != null && !def.equals(oldDef)) {
629 Logging.info("Defaults for " + key + " differ: " + def + " != " + defaultsMap.get(key));
630 }
631 if (def.getValue() != null || oldDef == null) {
632 Setting<?> defCopy = def.copy();
633 defCopy.setTime(System.currentTimeMillis() / 1000);
634 defCopy.setNew(true);
635 defaultsMap.put(key, defCopy);
636 }
637 Setting<?> prop = settingsMap.get(key);
638 if (klass.isInstance(prop)) {
639 return (T) prop;
640 } else {
641 return def;
642 }
643 }
644
645 @Override
646 public Set<String> getKeySet() {
647 return Collections.unmodifiableSet(settingsMap.keySet());
648 }
649
650 @Override
651 public Map<String, Setting<?>> getAllSettings() {
652 return new TreeMap<>(settingsMap);
653 }
654
655 /**
656 * Gets a map of all currently known defaults
657 * @return The map (key/setting)
658 */
659 public Map<String, Setting<?>> getAllDefaults() {
660 return new TreeMap<>(defaultsMap);
661 }
662
663 /**
664 * Replies the collection of plugin site URLs from where plugin lists can be downloaded.
665 * @return the collection of plugin site URLs
666 * @see #getOnlinePluginSites
667 */
668 public Collection<String> getPluginSites() {
669 return getList("pluginmanager.sites", Collections.singletonList(Config.getUrls().getJOSMWebsite()+"/pluginicons%<?plugins=>"));
670 }
671
672 /**
673 * Returns the list of plugin sites available according to offline mode settings.
674 * @return the list of available plugin sites
675 * @since 8471
676 */
677 public Collection<String> getOnlinePluginSites() {
678 Collection<String> pluginSites = new ArrayList<>(getPluginSites());
679 for (Iterator<String> it = pluginSites.iterator(); it.hasNext();) {
680 try {
681 OnlineResource.JOSM_WEBSITE.checkOfflineAccess(it.next(), Config.getUrls().getJOSMWebsite());
682 } catch (OfflineAccessException ex) {
683 Logging.log(Logging.LEVEL_WARN, ex);
684 it.remove();
685 }
686 }
687 return pluginSites;
688 }
689
690 /**
691 * Sets the collection of plugin site URLs.
692 *
693 * @param sites the site URLs
694 */
695 public void setPluginSites(Collection<String> sites) {
696 putList("pluginmanager.sites", new ArrayList<>(sites));
697 }
698
699 /**
700 * Returns XML describing these preferences.
701 * @param nopass if password must be excluded
702 * @return XML
703 */
704 public String toXML(boolean nopass) {
705 return toXML(settingsMap.entrySet(), nopass, false);
706 }
707
708 /**
709 * Returns XML describing the given preferences.
710 * @param settings preferences settings
711 * @param nopass if password must be excluded
712 * @param defaults true, if default values are converted to XML, false for
713 * regular preferences
714 * @return XML
715 */
716 public String toXML(Collection<Entry<String, Setting<?>>> settings, boolean nopass, boolean defaults) {
717 try (
718 StringWriter sw = new StringWriter();
719 PreferencesWriter prefWriter = new PreferencesWriter(new PrintWriter(sw), nopass, defaults)
720 ) {
721 prefWriter.write(settings);
722 sw.flush();
723 return sw.toString();
724 } catch (IOException e) {
725 Logging.error(e);
726 return null;
727 }
728 }
729
730 /**
731 * Removes obsolete preference settings. If you throw out a once-used preference
732 * setting, add it to the list here with an expiry date (written as comment). If you
733 * see something with an expiry date in the past, remove it from the list.
734 * @param loadedVersion JOSM version when the preferences file was written
735 */
736 private void removeObsolete(int loadedVersion) {
737 Logging.trace("Remove obsolete preferences for version {0}", Integer.toString(loadedVersion));
738 for (String key : OBSOLETE_PREF_KEYS) {
739 if (settingsMap.containsKey(key)) {
740 settingsMap.remove(key);
741 Logging.info(tr("Preference setting {0} has been removed since it is no longer used.", key));
742 }
743 }
744 }
745
746 /**
747 * Enables or not the preferences file auto-save mechanism (save each time a setting is changed).
748 * This behaviour is enabled by default.
749 * @param enable if {@code true}, makes JOSM save preferences file each time a setting is changed
750 * @since 7085
751 */
752 public final void enableSaveOnPut(boolean enable) {
753 synchronized (this) {
754 saveOnPut = enable;
755 }
756 }
757}
Note: See TracBrowser for help on using the repository browser.