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