001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.tagging.presets; 003 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.Collections; 007import java.util.HashMap; 008import java.util.HashSet; 009import java.util.Map; 010import java.util.Set; 011 012import javax.swing.JMenu; 013import javax.swing.JMenuItem; 014import javax.swing.JSeparator; 015 016import org.openstreetmap.josm.data.osm.OsmPrimitive; 017import org.openstreetmap.josm.gui.MainApplication; 018import org.openstreetmap.josm.gui.MenuScroller; 019import org.openstreetmap.josm.gui.tagging.presets.items.CheckGroup; 020import org.openstreetmap.josm.gui.tagging.presets.items.KeyedItem; 021import org.openstreetmap.josm.gui.tagging.presets.items.Roles; 022import org.openstreetmap.josm.gui.tagging.presets.items.Roles.Role; 023import org.openstreetmap.josm.spi.preferences.Config; 024import org.openstreetmap.josm.tools.Logging; 025import org.openstreetmap.josm.tools.MultiMap; 026import org.openstreetmap.josm.tools.SubclassFilteredCollection; 027 028/** 029 * Class holding Tagging Presets and allowing to manage them. 030 * @since 7100 031 */ 032public final class TaggingPresets { 033 034 /** The collection of tagging presets */ 035 private static final Collection<TaggingPreset> taggingPresets = new ArrayList<>(); 036 037 /** cache for key/value pairs found in the preset */ 038 private static final MultiMap<String, String> PRESET_TAG_CACHE = new MultiMap<>(); 039 /** cache for roles found in the preset */ 040 private static final Set<String> PRESET_ROLE_CACHE = new HashSet<>(); 041 042 /** The collection of listeners */ 043 private static final Collection<TaggingPresetListener> listeners = new ArrayList<>(); 044 045 private TaggingPresets() { 046 // Hide constructor for utility classes 047 } 048 049 /** 050 * Initializes tagging presets from preferences. 051 */ 052 public static void readFromPreferences() { 053 taggingPresets.clear(); 054 taggingPresets.addAll(TaggingPresetReader.readFromPreferences(false, false)); 055 cachePresets(taggingPresets); 056 } 057 058 /** 059 * Initialize the tagging presets (load and may display error) 060 */ 061 public static void initialize() { 062 readFromPreferences(); 063 for (TaggingPreset tp: taggingPresets) { 064 if (!(tp instanceof TaggingPresetSeparator)) { 065 MainApplication.getToolbar().register(tp); 066 } 067 } 068 if (taggingPresets.isEmpty()) { 069 MainApplication.getMenu().presetsMenu.setVisible(false); 070 } else { 071 Map<TaggingPresetMenu, JMenu> submenus = new HashMap<>(); 072 for (final TaggingPreset p : taggingPresets) { 073 JMenu m = p.group != null ? submenus.get(p.group) : MainApplication.getMenu().presetsMenu; 074 if (m == null && p.group != null) { 075 Logging.error("No tagging preset submenu for " + p.group); 076 } else if (m == null) { 077 Logging.error("No tagging preset menu. Tagging preset " + p + " won't be available there"); 078 } else if (p instanceof TaggingPresetSeparator) { 079 m.add(new JSeparator()); 080 } else if (p instanceof TaggingPresetMenu) { 081 JMenu submenu = new JMenu(p); 082 submenu.setText(p.getLocaleName()); 083 ((TaggingPresetMenu) p).menu = submenu; 084 submenus.put((TaggingPresetMenu) p, submenu); 085 m.add(submenu); 086 } else { 087 JMenuItem mi = new JMenuItem(p); 088 mi.setText(p.getLocaleName()); 089 m.add(mi); 090 } 091 } 092 for (JMenu submenu : submenus.values()) { 093 if (submenu.getItemCount() >= Config.getPref().getInt("taggingpreset.min-elements-for-scroller", 15)) { 094 MenuScroller.setScrollerFor(submenu); 095 } 096 } 097 } 098 if (Config.getPref().getBoolean("taggingpreset.sortmenu")) { 099 TaggingPresetMenu.sortMenu(MainApplication.getMenu().presetsMenu); 100 } 101 } 102 103 /** 104 * Initialize the cache for presets. This is done only once. 105 * @param presets Tagging presets to cache 106 */ 107 public static void cachePresets(Collection<TaggingPreset> presets) { 108 for (final TaggingPreset p : presets) { 109 for (TaggingPresetItem item : p.data) { 110 cachePresetItem(p, item); 111 } 112 } 113 } 114 115 private static void cachePresetItem(TaggingPreset p, TaggingPresetItem item) { 116 if (item instanceof KeyedItem) { 117 KeyedItem ki = (KeyedItem) item; 118 if (ki.key != null && ki.getValues() != null) { 119 PRESET_TAG_CACHE.putAll(ki.key, ki.getValues()); 120 } 121 } else if (item instanceof Roles) { 122 Roles r = (Roles) item; 123 for (Role i : r.roles) { 124 if (i.key != null) { 125 PRESET_ROLE_CACHE.add(i.key); 126 } 127 } 128 } else if (item instanceof CheckGroup) { 129 for (KeyedItem check : ((CheckGroup) item).checks) { 130 cachePresetItem(p, check); 131 } 132 } 133 } 134 135 /** 136 * Replies a new collection containing all tagging presets. 137 * @return a new collection containing all tagging presets. Empty if presets are not initialized (never null) 138 */ 139 public static Collection<TaggingPreset> getTaggingPresets() { 140 return Collections.unmodifiableCollection(taggingPresets); 141 } 142 143 /** 144 * Replies a set of all roles in the tagging presets. 145 * @return a set of all roles in the tagging presets. 146 */ 147 public static Set<String> getPresetRoles() { 148 return Collections.unmodifiableSet(PRESET_ROLE_CACHE); 149 } 150 151 /** 152 * Replies a set of all keys in the tagging presets. 153 * @return a set of all keys in the tagging presets. 154 */ 155 public static Set<String> getPresetKeys() { 156 return Collections.unmodifiableSet(PRESET_TAG_CACHE.keySet()); 157 } 158 159 /** 160 * Return set of values for a key in the tagging presets 161 * @param key the key 162 * @return set of values for a key in the tagging presets or null if none is found 163 */ 164 public static Set<String> getPresetValues(String key) { 165 Set<String> values = PRESET_TAG_CACHE.get(key); 166 if (values != null) 167 return Collections.unmodifiableSet(values); 168 return null; 169 } 170 171 /** 172 * Replies a new collection of all presets matching the parameters. 173 * 174 * @param t the preset types to include 175 * @param tags the tags to perform matching on, see {@link TaggingPresetItem#matches(Map)} 176 * @param onlyShowable whether only {@link TaggingPreset#isShowable() showable} presets should be returned 177 * @return a new collection of all presets matching the parameters. 178 * @see TaggingPreset#matches(Collection, Map, boolean) 179 * @since 9266 180 */ 181 public static Collection<TaggingPreset> getMatchingPresets(final Collection<TaggingPresetType> t, 182 final Map<String, String> tags, final boolean onlyShowable) { 183 return SubclassFilteredCollection.filter(getTaggingPresets(), preset -> preset.matches(t, tags, onlyShowable)); 184 } 185 186 /** 187 * Replies a new collection of all presets matching the given preset. 188 * 189 * @param primitive the primitive 190 * @return a new collection of all presets matching the given preset. 191 * @see TaggingPreset#test(OsmPrimitive) 192 * @since 9265 193 */ 194 public static Collection<TaggingPreset> getMatchingPresets(final OsmPrimitive primitive) { 195 return SubclassFilteredCollection.filter(getTaggingPresets(), preset -> preset.test(primitive)); 196 } 197 198 /** 199 * Adds a list of tagging presets to the current list. 200 * @param presets The tagging presets to add 201 */ 202 public static void addTaggingPresets(Collection<TaggingPreset> presets) { 203 if (presets != null && taggingPresets.addAll(presets)) { 204 for (TaggingPresetListener listener : listeners) { 205 listener.taggingPresetsModified(); 206 } 207 } 208 } 209 210 /** 211 * Adds a tagging preset listener. 212 * @param listener The listener to add 213 */ 214 public static void addListener(TaggingPresetListener listener) { 215 if (listener != null) { 216 listeners.add(listener); 217 } 218 } 219 220 /** 221 * Removes a tagging preset listener. 222 * @param listener The listener to remove 223 */ 224 public static void removeListener(TaggingPresetListener listener) { 225 if (listener != null) { 226 listeners.remove(listener); 227 } 228 } 229}