source: josm/trunk/src/org/openstreetmap/josm/gui/tagging/ac/AutoCompletionManager.java@ 5266

Last change on this file since 5266 was 5266, checked in by bastiK, 12 years ago

fixed majority of javadoc warnings by replacing "{@see" by "{@link"

  • Property svn:eol-style set to native
File size: 10.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.tagging.ac;
3
4import java.util.ArrayList;
5import java.util.Arrays;
6import java.util.Collection;
7import java.util.Collections;
8import java.util.HashSet;
9import java.util.List;
10import java.util.Map;
11import java.util.Map.Entry;
12import java.util.Set;
13
14import org.openstreetmap.josm.data.osm.DataSet;
15import org.openstreetmap.josm.data.osm.OsmPrimitive;
16import org.openstreetmap.josm.data.osm.OsmUtils;
17import org.openstreetmap.josm.data.osm.Relation;
18import org.openstreetmap.josm.data.osm.RelationMember;
19import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
20import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
21import org.openstreetmap.josm.data.osm.event.DataSetListener;
22import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
23import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
24import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
25import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
26import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
27import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
28import org.openstreetmap.josm.gui.tagging.TaggingPreset;
29import org.openstreetmap.josm.tools.MultiMap;
30
31/**
32 * AutoCompletionManager holds a cache of keys with a list of
33 * possible auto completion values for each key.
34 *
35 * Each DataSet is assigned one AutoCompletionManager instance such that
36 * <ol>
37 * <li>any key used in a tag in the data set is part of the key list in the cache</li>
38 * <li>any value used in a tag for a specific key is part of the autocompletion list of
39 * this key</li>
40 * </ol>
41 *
42 * Building up auto completion lists should not
43 * slow down tabbing from input field to input field. Looping through the complete
44 * data set in order to build up the auto completion list for a specific input
45 * field is not efficient enough, hence this cache.
46 *
47 * TODO: respect the relation type for member role autocompletion
48 */
49public class AutoCompletionManager implements DataSetListener {
50
51 /** If the dirty flag is set true, a rebuild is necessary. */
52 protected boolean dirty;
53 /** The data set that is managed */
54 protected DataSet ds;
55
56 /**
57 * the cached tags given by a tag key and a list of values for this tag
58 * only accessed by getTagCache(), rebuild() and cachePrimitiveTags()
59 * use getTagCache() accessor
60 */
61 protected MultiMap<String, String> tagCache;
62 /**
63 * the same as tagCache but for the preset keys and values
64 * can be accessed directly
65 */
66 protected static final MultiMap<String, String> presetTagCache = new MultiMap<String, String>();
67 /**
68 * the cached list of member roles
69 * only accessed by getRoleCache(), rebuild() and cacheRelationMemberRoles()
70 * use getRoleCache() accessor
71 */
72 protected Set<String> roleCache;
73 /**
74 * the same as roleCache but for the preset roles
75 * can be accessed directly
76 */
77 protected static final Set<String> presetRoleCache = new HashSet<String>();
78
79 public AutoCompletionManager(DataSet ds) {
80 this.ds = ds;
81 dirty = true;
82 }
83
84 protected MultiMap<String, String> getTagCache() {
85 if (dirty) {
86 rebuild();
87 dirty = false;
88 }
89 return tagCache;
90 }
91
92 protected Set<String> getRoleCache() {
93 if (dirty) {
94 rebuild();
95 dirty = false;
96 }
97 return roleCache;
98 }
99
100 /**
101 * initializes the cache from the primitives in the dataset
102 *
103 */
104 protected void rebuild() {
105 tagCache = new MultiMap<String, String>();
106 roleCache = new HashSet<String>();
107 cachePrimitives(ds.allNonDeletedCompletePrimitives());
108 }
109
110 protected void cachePrimitives(Collection<? extends OsmPrimitive> primitives) {
111 for (OsmPrimitive primitive : primitives) {
112 cachePrimitiveTags(primitive);
113 if (primitive instanceof Relation) {
114 cacheRelationMemberRoles((Relation) primitive);
115 }
116 }
117 }
118
119 /**
120 * make sure, the keys and values of all tags held by primitive are
121 * in the auto completion cache
122 *
123 * @param primitive an OSM primitive
124 */
125 protected void cachePrimitiveTags(OsmPrimitive primitive) {
126 for (String key: primitive.keySet()) {
127 String value = primitive.get(key);
128 tagCache.put(key, value);
129 }
130 }
131
132 /**
133 * Caches all member roles of the relation <code>relation</code>
134 *
135 * @param relation the relation
136 */
137 protected void cacheRelationMemberRoles(Relation relation){
138 for (RelationMember m: relation.getMembers()) {
139 if (m.hasRole()) {
140 roleCache.add(m.getRole());
141 }
142 }
143 }
144
145 /**
146 * Initialize the cache for presets. This is done only once.
147 */
148 public static void cachePresets(Collection<TaggingPreset> presets) {
149 for (final TaggingPreset p : presets) {
150 for (TaggingPreset.Item item : p.data) {
151 if (item instanceof TaggingPreset.KeyedItem) {
152 TaggingPreset.KeyedItem ki = (TaggingPreset.KeyedItem) item;
153 if (ki.key == null) {
154 continue;
155 }
156 presetTagCache.putAll(ki.key, ki.getValues());
157 } else if (item instanceof TaggingPreset.Roles) {
158 TaggingPreset.Roles r = (TaggingPreset.Roles) item;
159 for (TaggingPreset.Role i : r.roles) {
160 if (i.key != null) {
161 presetRoleCache.add(i.key);
162 }
163 }
164 }
165 }
166 }
167 }
168
169 /**
170 * replies the keys held by the cache
171 *
172 * @return the list of keys held by the cache
173 */
174 protected List<String> getDataKeys() {
175 return new ArrayList<String>(getTagCache().keySet());
176 }
177
178 protected List<String> getPresetKeys() {
179 return new ArrayList<String>(presetTagCache.keySet());
180 }
181
182 /**
183 * replies the auto completion values allowed for a specific key. Replies
184 * an empty list if key is null or if key is not in {@link #getKeys()}.
185 *
186 * @param key
187 * @return the list of auto completion values
188 */
189 protected List<String> getDataValues(String key) {
190 return new ArrayList<String>(getTagCache().getValues(key));
191 }
192
193 protected static List<String> getPresetValues(String key) {
194 return new ArrayList<String>(presetTagCache.getValues(key));
195 }
196
197 /**
198 * Replies the list of member roles
199 *
200 * @return the list of member roles
201 */
202 public List<String> getMemberRoles() {
203 return new ArrayList<String>(getRoleCache());
204 }
205
206 /**
207 * Populates the an {@link AutoCompletionList} with the currently cached
208 * member roles.
209 *
210 * @param list the list to populate
211 */
212 public void populateWithMemberRoles(AutoCompletionList list) {
213 list.add(presetRoleCache, AutoCompletionItemPritority.IS_IN_STANDARD);
214 list.add(getRoleCache(), AutoCompletionItemPritority.IS_IN_DATASET);
215 }
216
217 /**
218 * Populates the an {@link AutoCompletionList} with the currently cached
219 * tag keys
220 *
221 * @param list the list to populate
222 */
223 public void populateWithKeys(AutoCompletionList list) {
224 list.add(getPresetKeys(), AutoCompletionItemPritority.IS_IN_STANDARD);
225 list.add(getDataKeys(), AutoCompletionItemPritority.IS_IN_DATASET);
226 }
227
228 /**
229 * Populates the an {@link AutoCompletionList} with the currently cached
230 * values for a tag
231 *
232 * @param list the list to populate
233 * @param key the tag key
234 */
235 public void populateWithTagValues(AutoCompletionList list, String key) {
236 populateWithTagValues(list, Arrays.asList(key));
237 }
238
239 /**
240 * Populates the an {@link AutoCompletionList} with the currently cached
241 * values for some given tags
242 *
243 * @param list the list to populate
244 * @param keys the tag keys
245 */
246 public void populateWithTagValues(AutoCompletionList list, List<String> keys) {
247 for (String key : keys) {
248 list.add(getPresetValues(key), AutoCompletionItemPritority.IS_IN_STANDARD);
249 list.add(getDataValues(key), AutoCompletionItemPritority.IS_IN_DATASET);
250 }
251 }
252
253 /**
254 * Returns the currently cached tag keys.
255 * @return a list of tag keys
256 */
257 public List<AutoCompletionListItem> getKeys() {
258 AutoCompletionList list = new AutoCompletionList();
259 populateWithKeys(list);
260 return list.getList();
261 }
262
263 /**
264 * Returns the currently cached tag values for a given tag key.
265 * @param key the tag key
266 * @return a list of tag values
267 */
268 public List<AutoCompletionListItem> getValues(String key) {
269 return getValues(Arrays.asList(key));
270 }
271
272 /**
273 * Returns the currently cached tag values for a given list of tag keys.
274 * @param keys the tag keys
275 * @return a list of tag values
276 */
277 public List<AutoCompletionListItem> getValues(List<String> keys) {
278 AutoCompletionList list = new AutoCompletionList();
279 populateWithTagValues(list, keys);
280 return list.getList();
281 }
282
283 /*********************************************************
284 * Implementation of the DataSetListener interface
285 *
286 **/
287
288 public void primitivesAdded(PrimitivesAddedEvent event) {
289 if (dirty)
290 return;
291 cachePrimitives(event.getPrimitives());
292 }
293
294 public void primitivesRemoved(PrimitivesRemovedEvent event) {
295 dirty = true;
296 }
297
298 public void tagsChanged(TagsChangedEvent event) {
299 if (dirty)
300 return;
301 Map<String, String> newKeys = event.getPrimitive().getKeys();
302 Map<String, String> oldKeys = event.getOriginalKeys();
303
304 if (!newKeys.keySet().containsAll(oldKeys.keySet())) {
305 // Some keys removed, might be the last instance of key, rebuild necessary
306 dirty = true;
307 } else {
308 for (Entry<String, String> oldEntry: oldKeys.entrySet()) {
309 if (!oldEntry.getValue().equals(newKeys.get(oldEntry.getKey()))) {
310 // Value changed, might be last instance of value, rebuild necessary
311 dirty = true;
312 return;
313 }
314 }
315 cachePrimitives(Collections.singleton(event.getPrimitive()));
316 }
317 }
318
319 public void nodeMoved(NodeMovedEvent event) {/* ignored */}
320
321 public void wayNodesChanged(WayNodesChangedEvent event) {/* ignored */}
322
323 public void relationMembersChanged(RelationMembersChangedEvent event) {
324 dirty = true; // TODO: not necessary to rebuid if a member is added
325 }
326
327 public void otherDatasetChange(AbstractDatasetChangedEvent event) {/* ignored */}
328
329 public void dataChanged(DataChangedEvent event) {
330 dirty = true;
331 }
332}
Note: See TracBrowser for help on using the repository browser.