001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.spi.preferences;
003
004import java.util.LinkedList;
005import java.util.List;
006import java.util.Map;
007import java.util.Map.Entry;
008import java.util.TreeMap;
009import java.util.stream.Collectors;
010
011import org.openstreetmap.josm.tools.Logging;
012import org.openstreetmap.josm.tools.Utils;
013
014/**
015 * Abstract implementation of the {@link IPreferences} interface.
016 * @since 12847
017 */
018public abstract class AbstractPreferences implements IPreferences {
019
020    @Override
021    public synchronized String get(final String key, final String def) {
022        return getSetting(key, new StringSetting(def), StringSetting.class).getValue();
023    }
024
025    @Override
026    public boolean put(final String key, String value) {
027        return putSetting(key, Utils.isEmpty(value) ? null : new StringSetting(value));
028    }
029
030    @Override
031    public boolean getBoolean(final String key, final boolean def) {
032        return Boolean.parseBoolean(get(key, Boolean.toString(def)));
033    }
034
035    @Override
036    public boolean putBoolean(final String key, final boolean value) {
037        return put(key, Boolean.toString(value));
038    }
039
040    @Override
041    public synchronized int getInt(String key, int def) {
042        String v = get(key, Integer.toString(def));
043        if (v.isEmpty())
044            return def;
045
046        try {
047            return Integer.parseInt(v);
048        } catch (NumberFormatException e) {
049            // fall out
050            Logging.trace(e);
051        }
052        return def;
053    }
054
055    @Override
056    public boolean putInt(String key, int value) {
057        return put(key, Integer.toString(value));
058    }
059
060    @Override
061    public long getLong(String key, long def) {
062        String v = get(key, Long.toString(def));
063        if (null == v)
064            return def;
065
066        try {
067            return Long.parseLong(v);
068        } catch (NumberFormatException e) {
069            // fall out
070            Logging.trace(e);
071        }
072        return def;
073    }
074
075    @Override
076    public boolean putLong(final String key, final long value) {
077        return put(key, Long.toString(value));
078    }
079
080    @Override
081    public synchronized double getDouble(String key, double def) {
082        String v = get(key, Double.toString(def));
083        if (null == v)
084            return def;
085
086        try {
087            return Double.parseDouble(v);
088        } catch (NumberFormatException e) {
089            // fall out
090            Logging.trace(e);
091        }
092        return def;
093    }
094
095    @Override
096    public boolean putDouble(final String key, final double value) {
097        return put(key, Double.toString(value));
098    }
099
100    @Override
101    public List<String> getList(String key, List<String> def) {
102        return getSetting(key, new ListSetting(def), ListSetting.class).getValue();
103    }
104
105    @Override
106    public boolean putList(String key, List<String> value) {
107        return putSetting(key, value == null ? null : new ListSetting(value));
108    }
109
110    @Override
111    public List<List<String>> getListOfLists(String key, List<List<String>> def) {
112        return getSetting(key, new ListListSetting(def), ListListSetting.class).getValue();
113    }
114
115    @Override
116    public boolean putListOfLists(String key, List<List<String>> value) {
117        return putSetting(key, value == null ? null : new ListListSetting(value));
118    }
119
120    @Override
121    public List<Map<String, String>> getListOfMaps(String key, List<Map<String, String>> def) {
122        return getSetting(key, new MapListSetting(def), MapListSetting.class).getValue();
123    }
124
125    @Override
126    public boolean putListOfMaps(String key, List<Map<String, String>> value) {
127        return putSetting(key, value == null ? null : new MapListSetting(value));
128    }
129
130    /**
131     * Gets a map of all settings that are currently stored
132     * @return The settings
133     */
134    public abstract Map<String, Setting<?>> getAllSettings();
135
136    /**
137     * Set a value for a certain setting. The changed setting is saved to the preference file immediately.
138     * Due to caching mechanisms on modern operating systems and hardware, this shouldn't be a performance problem.
139     * @param key the unique identifier for the setting
140     * @param setting the value of the setting. In case it is null, the key-value entry will be removed.
141     * @return {@code true}, if something has changed (i.e. value is different than before)
142     */
143    public abstract boolean putSetting(String key, Setting<?> setting);
144
145    /**
146     * Get settings value for a certain key and provide default a value.
147     * @param <T> the setting type
148     * @param key the identifier for the setting
149     * @param def the default value. For each call of getSetting() with a given key, the default value must be the same.
150     * <code>def</code> must not be null, but the value of <code>def</code> can be null.
151     * @param klass the setting type (same as T)
152     * @return the corresponding value if the property has been set before, {@code def} otherwise
153     */
154    public abstract <T extends Setting<?>> T getSetting(String key, T def, Class<T> klass);
155
156    /**
157     * Gets all normal (string) settings that have a key starting with the prefix
158     * @param prefix The start of the key
159     * @return The key names of the settings
160     */
161    public Map<String, String> getAllPrefix(String prefix) {
162        return getAllSettings().entrySet().stream()
163                .filter(e -> e.getKey().startsWith(prefix) && (e.getValue() instanceof StringSetting))
164                .collect(Collectors.toMap(Entry::getKey, e -> ((StringSetting) e.getValue()).getValue(), (a, b) -> b, TreeMap::new));
165    }
166
167    /**
168     * Gets all list settings that have a key starting with the prefix
169     * @param prefix The start of the key
170     * @return The key names of the list settings
171     */
172    public List<String> getAllPrefixCollectionKeys(String prefix) {
173        return getAllSettings().entrySet().stream()
174                .filter(entry -> entry.getKey().startsWith(prefix) && entry.getValue() instanceof ListSetting)
175                .map(Entry::getKey)
176                .collect(Collectors.toCollection(LinkedList::new));
177    }
178}