001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.preferences;
003
004import org.openstreetmap.josm.spi.preferences.PreferenceChangedListener;
005import org.openstreetmap.josm.tools.CheckParameterUtil;
006import org.openstreetmap.josm.tools.Logging;
007import org.openstreetmap.josm.tools.bugreport.BugReport;
008
009/**
010 * This class represents a property that can be represented as String.
011 *
012 * @author Michael Zangl
013 *
014 * @param <T> The property content type.
015 * @since 10824
016 */
017public abstract class AbstractToStringProperty<T> extends AbstractProperty<T> {
018
019    /**
020     * This is a version of this property that attempts to get the property with a more specialized key and - if that fails - uses the property
021     * value as default.
022     *
023     * @author Michael Zangl
024     * @param <T> The content type
025     */
026    public static class ChildProperty<T> extends AbstractToStringProperty<T> {
027        private final AbstractToStringProperty<T> parent;
028
029        ChildProperty(AbstractToStringProperty<T> parent, String key) {
030            super(key, null);
031            CheckParameterUtil.ensureParameterNotNull(parent, "parent");
032            this.parent = parent;
033        }
034
035        @Override
036        protected void storeDefaultValue() {
037            // Default value hidden in preferences.
038        }
039
040        @Override
041        public T getDefaultValue() {
042            return parent.get();
043        }
044
045        @Override
046        protected T fromString(String string) {
047            return parent.fromString(string);
048        }
049
050        @Override
051        protected String toString(T t) {
052            return parent.toString(t);
053        }
054
055        @Override
056        protected void addListenerImpl(PreferenceChangedListener adapter) {
057            super.addListenerImpl(adapter);
058            parent.addListenerImpl(adapter);
059        }
060
061        @Override
062        protected void removeListenerImpl(PreferenceChangedListener adapter) {
063            super.removeListenerImpl(adapter);
064            parent.removeListenerImpl(adapter);
065        }
066
067        @Override
068        public CachingProperty<T> cached() {
069            throw new UnsupportedOperationException("Not implemented yet.");
070        }
071    }
072
073    /**
074     * Create a new property and store the default value.
075     * @param key The key
076     * @param defaultValue The default value.
077     * @see AbstractProperty#AbstractProperty(String, Object)
078     */
079    public AbstractToStringProperty(String key, T defaultValue) {
080        super(key, defaultValue);
081        storeDefaultValue();
082    }
083
084    @Override
085    public T get() {
086        String string = getAsString();
087        if (!string.isEmpty()) {
088            try {
089                return fromString(string);
090            } catch (InvalidPreferenceValueException e) {
091                Logging.warn(BugReport.intercept(e).put("key", key).put("value", string));
092            }
093        }
094        return getDefaultValue();
095    }
096
097    /**
098     * Converts the string to an object of the given type.
099     * @param string The string
100     * @return The object.
101     * @throws InvalidPreferenceValueException If the value could not be converted.
102     */
103    protected abstract T fromString(String string);
104
105    @Override
106    public boolean put(T value) {
107        String string = value == null ? null : toString(value);
108        return getPreferences().put(getKey(), string);
109    }
110
111    /**
112     * Converts the string to an object of the given type.
113     * @param t The object.
114     * @return The string representing the object
115     * @throws InvalidPreferenceValueException If the value could not be converted.
116     */
117    protected abstract String toString(T t);
118
119    /**
120     * Gets the preference value as String.
121     * @return The string preference value.
122     */
123    protected String getAsString() {
124        T def = getDefaultValue();
125        String sdef = def == null ? "" : toString(def);
126        return getPreferences() != null ? getPreferences().get(key, sdef) : sdef;
127    }
128
129    /**
130     * Gets a specialized setting value that has the current value as default
131     * <p>
132     * The key will be getKey().spec
133     * @param spec The key specialization
134     * @return The property
135     */
136    public AbstractToStringProperty<T> getSpecialized(String spec) {
137        return getChildProperty(getKey() + "." + spec);
138    }
139
140    /**
141     * Gets a setting that defaults to this setting if the key is not set.
142     * @param key The more specialized key.
143     * @return The new setting.
144     */
145    protected AbstractToStringProperty<T> getChildProperty(String key) {
146        return new ChildProperty<>(this, key);
147    }
148
149}