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}