source: josm/trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetItem.java

Last change on this file was 19286, checked in by taylor.smock, 6 months ago

See #24075: Reduce memory allocations for TaggingPresetItem#matches

Make PMD happy again.

As noted in the comments for the for loop in question, this is a fairly
significant performance optimization. The for-each loop that PMD prefers has
significant penalties on hot code sections for ArrayList objects, which we use
extensively.

TBH, the JVM should probably do this optimization for (at minimum) ArrayList
objects.

  • Property svn:eol-style set to native
File size: 8.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.tagging.presets;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trc;
6
7import java.io.File;
8import java.util.Arrays;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.EnumSet;
12import java.util.List;
13import java.util.Map;
14import java.util.RandomAccess;
15import java.util.Set;
16
17import javax.swing.ImageIcon;
18import javax.swing.JPanel;
19
20import org.openstreetmap.josm.data.osm.DataSet;
21import org.openstreetmap.josm.data.osm.OsmDataManager;
22import org.openstreetmap.josm.data.osm.OsmPrimitive;
23import org.openstreetmap.josm.data.osm.Tag;
24import org.openstreetmap.josm.data.preferences.BooleanProperty;
25import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem;
26import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
27import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
28import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
29import org.openstreetmap.josm.gui.util.LruCache;
30import org.openstreetmap.josm.tools.ImageProvider;
31import org.openstreetmap.josm.tools.Logging;
32import org.openstreetmap.josm.tools.Utils;
33import org.xml.sax.SAXException;
34
35/**
36 * Class that represents single part of a preset - one field or text label that is shown to user
37 * @since 6068
38 */
39public abstract class TaggingPresetItem {
40
41 // cache the parsing of types using a LRU cache
42 private static final Map<String, Set<TaggingPresetType>> TYPE_CACHE = new LruCache<>(16);
43 /**
44 * Display OSM keys as {@linkplain org.openstreetmap.josm.gui.widgets.OsmIdTextField#setHint hint}
45 */
46 protected static final BooleanProperty DISPLAY_KEYS_AS_HINT = new BooleanProperty("taggingpreset.display-keys-as-hint", true);
47
48 protected void initAutoCompletionField(AutoCompletingTextField field, String... key) {
49 initAutoCompletionField(field, Arrays.asList(key));
50 }
51
52 protected void initAutoCompletionField(AutoCompletingTextField field, List<String> keys) {
53 DataSet data = OsmDataManager.getInstance().getEditDataSet();
54 if (data == null) {
55 return;
56 }
57 AutoCompletionList list = new AutoCompletionList();
58 AutoCompletionManager.of(data).populateWithTagValues(list, keys);
59 field.setAutoCompletionList(list);
60 }
61
62 /**
63 * Returns all cached {@link AutoCompletionItem}s for given keys.
64 *
65 * @param keys retrieve the items for these keys
66 * @return the currently cached items, sorted by priority and alphabet
67 * @since 18221
68 */
69 protected List<AutoCompletionItem> getAllForKeys(List<String> keys) {
70 DataSet data = OsmDataManager.getInstance().getEditDataSet();
71 if (data == null) {
72 return Collections.emptyList();
73 }
74 return AutoCompletionManager.of(data).getAllForKeys(keys);
75 }
76
77 /**
78 * Called by {@link TaggingPreset#createPanel} during tagging preset panel creation.
79 * All components defining this tagging preset item must be added to given panel.
80 *
81 * @param p The panel where components must be added
82 * @param support supporting class for creating the GUI
83 * @return {@code true} if this item adds semantic tagging elements, {@code false} otherwise.
84 */
85 protected abstract boolean addToPanel(JPanel p, TaggingPresetItemGuiSupport support);
86
87 /**
88 * Adds the new tags to apply to selected OSM primitives when the preset holding this item is applied.
89 * @param changedTags The list of changed tags to modify if needed
90 */
91 protected abstract void addCommands(List<Tag> changedTags);
92
93 /**
94 * Tests whether the tags match this item.
95 * Note that for a match, at least one positive and no negative is required.
96 * @param tags the tags of an {@link OsmPrimitive}
97 * @return {@code true} if matches (positive), {@code null} if neutral, {@code false} if mismatches (negative).
98 */
99 public Boolean matches(Map<String, String> tags) {
100 return null; // NOSONAR
101 }
102
103 protected static Set<TaggingPresetType> getType(String types) throws SAXException {
104 if (Utils.isEmpty(types)) {
105 throw new SAXException(tr("Unknown type: {0}", types));
106 }
107 if (TYPE_CACHE.containsKey(types))
108 return TYPE_CACHE.get(types);
109 Set<TaggingPresetType> result = EnumSet.noneOf(TaggingPresetType.class);
110 for (String type : types.split(",", -1)) {
111 try {
112 TaggingPresetType presetType = TaggingPresetType.fromString(type);
113 if (presetType != null) {
114 result.add(presetType);
115 }
116 } catch (IllegalArgumentException e) {
117 throw new SAXException(tr("Unknown type: {0}", type), e);
118 }
119 }
120 TYPE_CACHE.put(types, result);
121 return result;
122 }
123
124 protected static String fixPresetString(String s) {
125 return s == null ? s : s.replace("'", "''");
126 }
127
128 protected static String getLocaleText(String text, String textContext, String defaultText) {
129 if (text == null) {
130 return defaultText;
131 } else if (textContext != null) {
132 return trc(textContext, fixPresetString(text));
133 } else {
134 return tr(fixPresetString(text));
135 }
136 }
137
138 protected static Integer parseInteger(String str) {
139 if (Utils.isEmpty(str))
140 return null;
141 try {
142 return Integer.valueOf(str);
143 } catch (NumberFormatException e) {
144 Logging.trace(e);
145 }
146 return null;
147 }
148
149 /**
150 * Loads a tagging preset icon
151 * @param iconName the icon name
152 * @param zipIcons zip file where the image is located
153 * @param maxSize maximum image size (or null)
154 * @return the requested image or null if the request failed
155 */
156 public static ImageIcon loadImageIcon(String iconName, File zipIcons, Integer maxSize) {
157 final Collection<String> s = TaggingPresets.ICON_SOURCES.get();
158 ImageProvider imgProv = new ImageProvider(iconName).setDirs(s).setId("presets").setArchive(zipIcons).setOptional(true);
159 if (maxSize != null && maxSize > 0) {
160 imgProv.setMaxSize(maxSize);
161 }
162 return imgProv.get();
163 }
164
165 /**
166 * Determine whether the given preset items match the tags
167 * @param data the preset items
168 * @param tags the tags to match
169 * @return whether the given preset items match the tags
170 * @since 9932
171 */
172 public static boolean matches(Iterable<? extends TaggingPresetItem> data, Map<String, String> tags) {
173 boolean atLeastOnePositiveMatch = false;
174 if (data instanceof List && data instanceof RandomAccess) {
175 List<? extends TaggingPresetItem> items = (List<? extends TaggingPresetItem>) data;
176 /* This is a memory allocation optimization, mostly for ArrayList.
177 * In test runs, this reduced the memory cost for this method by 99%.
178 * This appears to have also improved CPU cost for this method by ~10% as well.
179 * The big win for CPU cost is in GC improvements, which was around 80%.
180 * Overall improvement: 7.6 hours to 4.5 hours for validating a Colorado pbf extract (40% improvement).
181 */
182 for (int i = 0; i < items.size(); i++) { // NOPMD READ ABOVE: DO NOT REPLACE WITH ENHANCED FOR LOOP WITHOUT PROFILING!
183 TaggingPresetItem item = items.get(i);
184 Boolean m = item.matches(tags);
185 if (m != null && !m) {
186 return false;
187 } else if (m != null) {
188 atLeastOnePositiveMatch = true;
189 }
190 }
191 } else {
192 for (TaggingPresetItem item : data) {
193 Boolean m = item.matches(tags);
194 if (m != null && !m) {
195 return false;
196 } else if (m != null) {
197 atLeastOnePositiveMatch = true;
198 }
199 }
200 }
201 return atLeastOnePositiveMatch;
202 }
203}
Note: See TracBrowser for help on using the repository browser.