001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.mappaint;
003
004import java.awt.Color;
005import java.util.function.BiFunction;
006
007import org.openstreetmap.josm.data.preferences.BooleanProperty;
008import org.openstreetmap.josm.data.preferences.DoubleProperty;
009import org.openstreetmap.josm.data.preferences.NamedColorProperty;
010import org.openstreetmap.josm.data.preferences.StringProperty;
011import org.openstreetmap.josm.tools.Logging;
012
013/**
014 * Factory to create matching {@link StyleSetting} instances.
015 * @since 15731
016 */
017public final class StyleSettingFactory {
018
019    private StyleSettingFactory() {
020        // private constructor for factory classes
021    }
022
023    /**
024     * Creates a new {@code StyleSetting} based on the specified type by {@code c}.
025     * The type must be supported by {@link Cascade#convertTo} as well as {@link org.openstreetmap.josm.data.preferences.AbstractProperty}.
026     * @param c cascade
027     * @param parentStyle parent style source
028     * @param key setting identifier
029     * @return newly created {@code StyleSetting}
030     */
031    public static StyleSetting create(Cascade c, StyleSource parentStyle, String key) {
032        final String type = c.get("type", null, String.class);
033        final String qualifiedKey = String.join(":", parentStyle.url, type, key);
034        switch (type) {
035            case "boolean":
036                return forLabelAndDefault(c, Boolean.class, (label, defaultValue) -> {
037                    final BooleanProperty property = new BooleanProperty(qualifiedKey, defaultValue);
038                    return new StyleSetting.BooleanStyleSetting(parentStyle, label, property);
039                });
040            case "double":
041                return forLabelAndDefault(c, Double.class, (label, defaultValue) -> {
042                    final DoubleProperty property = new DoubleProperty(qualifiedKey, defaultValue);
043                    return new StyleSetting.PropertyStyleSetting<>(parentStyle, label, Double.class, property);
044                });
045            case "string":
046                return forLabelAndDefault(c, String.class, (label, defaultValue) -> {
047                    final StringProperty property = new StringProperty(qualifiedKey, defaultValue);
048                    return new StyleSetting.PropertyStyleSetting<>(parentStyle, label, String.class, property);
049                });
050            case "color":
051                return forLabelAndDefault(c, Color.class, (label, defaultValue) -> {
052                    final NamedColorProperty property = new NamedColorProperty(NamedColorProperty.COLOR_CATEGORY_MAPPAINT,
053                            parentStyle.title == null ? "MapCSS" : parentStyle.title, label, defaultValue);
054                    return new StyleSetting.ColorStyleSetting(parentStyle, label, property);
055                });
056            default:
057                Logging.warn("Unknown setting type {0} for style {1}", type, parentStyle.url);
058                return null;
059        }
060    }
061
062    private static <T> StyleSetting forLabelAndDefault(Cascade c, final Class<T> type, BiFunction<String, T, StyleSetting> function) {
063        String label = c.get("label", null, String.class);
064        if (label == null) {
065            Logging.warn("property 'label' required for style setting of type " + type);
066            return null;
067        }
068        T defaultValue = c.get("default", null, type);
069        if (defaultValue == null) {
070            Logging.warn("property 'default' required for style setting of type " + type);
071            return null;
072        }
073        return function.apply(label, defaultValue);
074    }
075}