001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.tagging.presets;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005import static org.openstreetmap.josm.tools.I18n.trc;
006
007import java.io.File;
008import java.util.Arrays;
009import java.util.Collection;
010import java.util.Collections;
011import java.util.EnumSet;
012import java.util.List;
013import java.util.Map;
014import java.util.Set;
015
016import javax.swing.ImageIcon;
017import javax.swing.JPanel;
018
019import org.openstreetmap.josm.data.osm.DataSet;
020import org.openstreetmap.josm.data.osm.OsmDataManager;
021import org.openstreetmap.josm.data.osm.OsmPrimitive;
022import org.openstreetmap.josm.data.osm.Tag;
023import org.openstreetmap.josm.data.preferences.BooleanProperty;
024import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem;
025import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
026import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
027import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
028import org.openstreetmap.josm.gui.util.LruCache;
029import org.openstreetmap.josm.tools.ImageProvider;
030import org.openstreetmap.josm.tools.Logging;
031import org.openstreetmap.josm.tools.Utils;
032import org.xml.sax.SAXException;
033
034/**
035 * Class that represents single part of a preset - one field or text label that is shown to user
036 * @since 6068
037 */
038public abstract class TaggingPresetItem {
039
040    // cache the parsing of types using a LRU cache
041    private static final Map<String, Set<TaggingPresetType>> TYPE_CACHE = new LruCache<>(16);
042    /**
043     * Display OSM keys as {@linkplain org.openstreetmap.josm.gui.widgets.OsmIdTextField#setHint hint}
044     */
045    protected static final BooleanProperty DISPLAY_KEYS_AS_HINT = new BooleanProperty("taggingpreset.display-keys-as-hint", true);
046
047    protected void initAutoCompletionField(AutoCompletingTextField field, String... key) {
048        initAutoCompletionField(field, Arrays.asList(key));
049    }
050
051    protected void initAutoCompletionField(AutoCompletingTextField field, List<String> keys) {
052        DataSet data = OsmDataManager.getInstance().getEditDataSet();
053        if (data == null) {
054            return;
055        }
056        AutoCompletionList list = new AutoCompletionList();
057        AutoCompletionManager.of(data).populateWithTagValues(list, keys);
058        field.setAutoCompletionList(list);
059    }
060
061    /**
062     * Returns all cached {@link AutoCompletionItem}s for given keys.
063     *
064     * @param keys retrieve the items for these keys
065     * @return the currently cached items, sorted by priority and alphabet
066     * @since 18221
067     */
068    protected List<AutoCompletionItem> getAllForKeys(List<String> keys) {
069        DataSet data = OsmDataManager.getInstance().getEditDataSet();
070        if (data == null) {
071            return Collections.emptyList();
072        }
073        return AutoCompletionManager.of(data).getAllForKeys(keys);
074    }
075
076    /**
077     * Called by {@link TaggingPreset#createPanel} during tagging preset panel creation.
078     * All components defining this tagging preset item must be added to given panel.
079     *
080     * @param p The panel where components must be added
081     * @param support supporting class for creating the GUI
082     * @return {@code true} if this item adds semantic tagging elements, {@code false} otherwise.
083     */
084    protected abstract boolean addToPanel(JPanel p, TaggingPresetItemGuiSupport support);
085
086    /**
087     * Adds the new tags to apply to selected OSM primitives when the preset holding this item is applied.
088     * @param changedTags The list of changed tags to modify if needed
089     */
090    protected abstract void addCommands(List<Tag> changedTags);
091
092    /**
093     * Tests whether the tags match this item.
094     * Note that for a match, at least one positive and no negative is required.
095     * @param tags the tags of an {@link OsmPrimitive}
096     * @return {@code true} if matches (positive), {@code null} if neutral, {@code false} if mismatches (negative).
097     */
098    public Boolean matches(Map<String, String> tags) {
099        return null; // NOSONAR
100    }
101
102    protected static Set<TaggingPresetType> getType(String types) throws SAXException {
103        if (Utils.isEmpty(types)) {
104            throw new SAXException(tr("Unknown type: {0}", types));
105        }
106        if (TYPE_CACHE.containsKey(types))
107            return TYPE_CACHE.get(types);
108        Set<TaggingPresetType> result = EnumSet.noneOf(TaggingPresetType.class);
109        for (String type : types.split(",", -1)) {
110            try {
111                TaggingPresetType presetType = TaggingPresetType.fromString(type);
112                if (presetType != null) {
113                    result.add(presetType);
114                }
115            } catch (IllegalArgumentException e) {
116                throw new SAXException(tr("Unknown type: {0}", type), e);
117            }
118        }
119        TYPE_CACHE.put(types, result);
120        return result;
121    }
122
123    protected static String fixPresetString(String s) {
124        return s == null ? s : s.replace("'", "''");
125    }
126
127    protected static String getLocaleText(String text, String textContext, String defaultText) {
128        if (text == null) {
129            return defaultText;
130        } else if (textContext != null) {
131            return trc(textContext, fixPresetString(text));
132        } else {
133            return tr(fixPresetString(text));
134        }
135    }
136
137    protected static Integer parseInteger(String str) {
138        if (Utils.isEmpty(str))
139            return null;
140        try {
141            return Integer.valueOf(str);
142        } catch (NumberFormatException e) {
143            Logging.trace(e);
144        }
145        return null;
146    }
147
148    /**
149     * Loads a tagging preset icon
150     * @param iconName the icon name
151     * @param zipIcons zip file where the image is located
152     * @param maxSize maximum image size (or null)
153     * @return the requested image or null if the request failed
154     */
155    public static ImageIcon loadImageIcon(String iconName, File zipIcons, Integer maxSize) {
156        final Collection<String> s = TaggingPresets.ICON_SOURCES.get();
157        ImageProvider imgProv = new ImageProvider(iconName).setDirs(s).setId("presets").setArchive(zipIcons).setOptional(true);
158        if (maxSize != null && maxSize > 0) {
159            imgProv.setMaxSize(maxSize);
160        }
161        return imgProv.get();
162    }
163
164    /**
165     * Determine whether the given preset items match the tags
166     * @param data the preset items
167     * @param tags the tags to match
168     * @return whether the given preset items match the tags
169     * @since 9932
170     */
171    public static boolean matches(Iterable<? extends TaggingPresetItem> data, Map<String, String> tags) {
172        boolean atLeastOnePositiveMatch = false;
173        for (TaggingPresetItem item : data) {
174            Boolean m = item.matches(tags);
175            if (m != null && !m)
176                return false;
177            else if (m != null) {
178                atLeastOnePositiveMatch = true;
179            }
180        }
181        return atLeastOnePositiveMatch;
182    }
183}