source: josm/trunk/src/org/openstreetmap/josm/data/osm/TagCollection.java@ 3779

Last change on this file since 3779 was 3206, checked in by jttt, 14 years ago

Remove obsoleted methods from Dataset, replace some calls of getNodes()/getWays()/getRelations() with allPrimitives()

  • Property svn:eol-style set to native
File size: 23.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.Collections;
9import java.util.HashMap;
10import java.util.HashSet;
11import java.util.Iterator;
12import java.util.List;
13import java.util.Map;
14import java.util.Set;
15import java.util.Map.Entry;
16
17/**
18 * TagCollection is a collection of tags which can be used to manipulate
19 * tags managed by {@see OsmPrimitive}s.
20 *
21 * A TagCollection can be created:
22 * <ul>
23 * <li>from the tags managed by a specific {@see OsmPrimitive} with {@see #from(OsmPrimitive)}</li>
24 * <li>from the union of all tags managed by a collection of {@see OsmPrimitive}s with {@see #unionOfAllPrimitives(Collection)}</li>
25 * <li>from the union of all tags managed by a {@see DataSet} with {@see #unionOfAllPrimitives(DataSet)}</li>
26 * <li>from the intersection of all tags managed by a collection of primitives with {@see #commonToAllPrimitives(Collection)}</li>
27 * </ul>
28 *
29 * It provides methods to query the collection, like {@see #size()}, {@see #hasTagsFor(String)}, etc.
30 *
31 * Basic set operations allow to create the union, the intersection and the difference
32 * of tag collections, see {@see #union(TagCollection)}, {@see #intersect(TagCollection)},
33 * and {@see #minus(TagCollection)}.
34 *
35 *
36 */
37public class TagCollection implements Iterable<Tag> {
38
39 /**
40 * Creates a tag collection from the tags managed by a specific
41 * {@see OsmPrimitive}. If <code>primitive</code> is null, replies
42 * an empty tag collection.
43 *
44 * @param primitive the primitive
45 * @return a tag collection with the tags managed by a specific
46 * {@see OsmPrimitive}
47 */
48 public static TagCollection from(Tagged primitive) {
49 TagCollection tags = new TagCollection();
50 for (String key: primitive.keySet()) {
51 tags.add(new Tag(key, primitive.get(key)));
52 }
53 return tags;
54 }
55
56 /**
57 * Creates a tag collection from a map of key/value-pairs. Replies
58 * an empty tag collection if {@code tags} is null.
59 *
60 * @param tags the key/value-pairs
61 * @return the tag collection
62 */
63 public static TagCollection from(Map<String,String> tags) {
64 TagCollection ret = new TagCollection();
65 if (tags == null) return ret;
66 for (Entry<String,String> entry: tags.entrySet()) {
67 String key = entry.getKey() == null? "" : entry.getKey();
68 String value = entry.getValue() == null ? "" : entry.getValue();
69 ret.add(new Tag(key,value));
70 }
71 return ret;
72 }
73
74 /**
75 * Creates a tag collection from the union of the tags managed by
76 * a collection of primitives. Replies an empty tag collection,
77 * if <code>primitives</code> is null.
78 *
79 * @param primitives the primitives
80 * @return a tag collection with the union of the tags managed by
81 * a collection of primitives
82 */
83 public static TagCollection unionOfAllPrimitives(Collection<? extends Tagged> primitives) {
84 TagCollection tags = new TagCollection();
85 if (primitives == null) return tags;
86 for (Tagged primitive: primitives) {
87 if (primitive == null) {
88 continue;
89 }
90 tags.add(TagCollection.from(primitive));
91 }
92 return tags;
93 }
94
95 /**
96 * Replies a tag collection with the tags which are common to all primitives in in
97 * <code>primitives</code>. Replies an empty tag collection of <code>primitives</code>
98 * is null.
99 *
100 * @param primitives the primitives
101 * @return a tag collection with the tags which are common to all primitives
102 */
103 public static TagCollection commonToAllPrimitives(Collection<? extends Tagged> primitives) {
104 TagCollection tags = new TagCollection();
105 if (primitives == null || primitives.isEmpty()) return tags;
106 // initialize with the first
107 //
108 tags.add(TagCollection.from(primitives.iterator().next()));
109
110 // intersect with the others
111 //
112 for (Tagged primitive: primitives) {
113 if (primitive == null) {
114 continue;
115 }
116 tags.add(tags.intersect(TagCollection.from(primitive)));
117 }
118 return tags;
119 }
120
121 /**
122 * Replies a tag collection with the union of the tags which are common to all primitives in
123 * the dataset <code>ds</code>. Returns an empty tag collection of <code>ds</code> is null.
124 *
125 * @param ds the dataset
126 * @return a tag collection with the union of the tags which are common to all primitives in
127 * the dataset <code>ds</code>
128 */
129 public static TagCollection unionOfAllPrimitives(DataSet ds) {
130 TagCollection tags = new TagCollection();
131 if (ds == null) return tags;
132 tags.add(TagCollection.unionOfAllPrimitives(ds.allPrimitives()));
133 return tags;
134 }
135
136 private final HashSet<Tag> tags = new HashSet<Tag>();
137
138 /**
139 * Creates an empty tag collection
140 */
141 public TagCollection() {
142 }
143
144 /**
145 * Creates a clone of the tag collection <code>other</code>. Creats an empty
146 * tag collection if <code>other</code> is null.
147 *
148 * @param other the other collection
149 */
150 public TagCollection(TagCollection other) {
151 if (other != null) {
152 tags.addAll(other.tags);
153 }
154 }
155
156 /**
157 * Replies the number of tags in this tag collection
158 *
159 * @return the number of tags in this tag collection
160 */
161 public int size() {
162 return tags.size();
163 }
164
165 /**
166 * Replies true if this tag collection is empty
167 *
168 * @return true if this tag collection is empty; false, otherwise
169 */
170 public boolean isEmpty() {
171 return size() == 0;
172 }
173
174 /**
175 * Adds a tag to the tag collection. If <code>tag</code> is null, nothing is added.
176 *
177 * @param tag the tag to add
178 */
179 public void add(Tag tag){
180 if (tag == null) return;
181 if (tags.contains(tag)) return;
182 tags.add(tag);
183 }
184
185 /**
186 * Adds a collection of tags to the tag collection. If <code>tags</code> is null, nothing
187 * is added. null values in the collection are ignored.
188 *
189 * @param tags the collection of tags
190 */
191 public void add(Collection<Tag> tags) {
192 if (tags == null) return;
193 for (Tag tag: tags){
194 add(tag);
195 }
196 }
197
198 /**
199 * Adds the tags of another tag collection to this collection. Adds nothing, if
200 * <code>tags</code> is null.
201 *
202 * @param tags the other tag collection
203 */
204 public void add(TagCollection tags) {
205 if (tags == null) return;
206 this.tags.addAll(tags.tags);
207 }
208
209 /**
210 * Removes a specific tag from the tag collection. Does nothing if <code>tag</code> is
211 * null.
212 *
213 * @param tag the tag to be removed
214 */
215 public void remove(Tag tag) {
216 if (tag == null) return;
217 tags.remove(tag);
218 }
219
220 /**
221 * Removes a collection of tags from the tag collection. Does nothing if <code>tags</code> is
222 * null.
223 *
224 * @param tags the tags to be removed
225 */
226 public void remove(Collection<Tag> tags) {
227 if (tags == null) return;
228 this.tags.removeAll(tags);
229 }
230
231 /**
232 * Removes all tags in the tag collection <code>tags</code> from the current tag collection.
233 * Does nothing if <code>tags</code> is null.
234 *
235 * @param tags the tag collection to be removed.
236 */
237 public void remove(TagCollection tags) {
238 if (tags == null) return;
239 this.tags.removeAll(tags.tags);
240 }
241
242 /**
243 * Removes all tags whose keys are equal to <code>key</code>. Does nothing if <code>key</code>
244 * is null.
245 *
246 * @param key the key to be removed
247 */
248 public void removeByKey(String key) {
249 if (key == null) return;
250 Iterator<Tag> it = tags.iterator();
251 while(it.hasNext()) {
252 if (it.next().matchesKey(key)) {
253 it.remove();
254 }
255 }
256 }
257
258 /**
259 * Removes all tags whose key is in the collection <code>keys</code>. Does nothing if
260 * <code>keys</code> is null.
261 *
262 * @param keys the collection of keys to be removed
263 */
264 public void removeByKey(Collection<String> keys) {
265 if (keys == null) return;
266 for (String key: keys) {
267 removeByKey(key);
268 }
269 }
270
271 /**
272 * Replies true if the this tag collection contains <code>tag</code>.
273 *
274 * @param tag the tag to look up
275 * @return true if the this tag collection contains <code>tag</code>; false, otherwise
276 */
277 public boolean contains(Tag tag) {
278 return tags.contains(tag);
279 }
280
281 /**
282 * Replies true if this tag collection contains at least one tag with key <code>key</code>.
283 *
284 * @param key the key to look up
285 * @return true if this tag collection contains at least one tag with key <code>key</code>; false, otherwise
286 */
287 public boolean containsKey(String key) {
288 if (key == null) return false;
289 for (Tag tag: tags) {
290 if (tag.matchesKey(key)) return true;
291 }
292 return false;
293 }
294
295 /**
296 * Replies true if this tag collection contains all tags in <code>tags</code>. Replies
297 * false, if tags is null.
298 *
299 * @param tags the tags to look up
300 * @return true if this tag collection contains all tags in <code>tags</code>. Replies
301 * false, if tags is null.
302 */
303 public boolean containsAll(Collection<Tag> tags) {
304 if (tags == null) return false;
305 return this.tags.containsAll(tags);
306 }
307
308 /**
309 * Replies true if this tag collection at least one tag for every key in <code>keys</code>.
310 * Replies false, if <code>keys</code> is null. null values in <code>keys</code> are ignored.
311 *
312 * @param keys the keys to lookup
313 * @return true if this tag collection at least one tag for every key in <code>keys</code>.
314 */
315 public boolean containsAllKeys(Collection<String> keys) {
316 if (keys == null) return false;
317 for (String key: keys) {
318 if (key == null) {
319 continue;
320 }
321 if (! containsKey(key)) return false;
322 }
323 return true;
324 }
325
326 /**
327 * Replies the number of tags with key <code>key</code>
328 *
329 * @param key the key to look up
330 * @return the number of tags with key <code>key</code>. 0, if key is null.
331 */
332 public int getNumTagsFor(String key) {
333 if (key == null) return 0;
334 int count = 0;
335 for (Tag tag: tags) {
336 if (tag.matchesKey(key)) {
337 count++;
338 }
339 }
340 return count;
341 }
342
343 /**
344 * Replies true if there is at least one tag for the given key.
345 *
346 * @param key the key to look up
347 * @return true if there is at least one tag for the given key. false, if key is null.
348 */
349 public boolean hasTagsFor(String key) {
350 return getNumTagsFor(key) > 0;
351 }
352
353 /**
354 * Replies true it there is at least one tag with a non empty value for key.
355 * Replies false if key is null.
356 *
357 * @param key the key
358 * @return true it there is at least one tag with a non empty value for key.
359 */
360 public boolean hasValuesFor(String key) {
361 if (key == null) return false;
362 Set<String> values = getTagsFor(key).getValues();
363 values.remove("");
364 return !values.isEmpty();
365 }
366
367 /**
368 * Replies true if there is exactly one tag for <code>key</code> and
369 * if the value of this tag is not empty. Replies false if key is
370 * null.
371 *
372 * @param key the key
373 * @return true if there is exactly one tag for <code>key</code> and
374 * if the value of this tag is not empty
375 */
376 public boolean hasUniqueNonEmptyValue(String key) {
377 if (key == null) return false;
378 Set<String> values = getTagsFor(key).getValues();
379 return values.size() == 1 && ! values.contains("");
380 }
381
382 /**
383 * Replies true if there is a tag with an empty value for <code>key</code>.
384 * Replies false, if key is null.
385 *
386 * @param key the key
387 * @return true if there is a tag with an empty value for <code>key</code>
388 */
389 public boolean hasEmptyValue(String key) {
390 if (key == null) return false;
391 Set<String> values = getTagsFor(key).getValues();
392 return values.contains("");
393 }
394
395 /**
396 * Replies true if there is exactly one tag for <code>key</code> and if
397 * the value for this tag is empty. Replies false if key is null.
398 *
399 * @param key the key
400 * @return true if there is exactly one tag for <code>key</code> and if
401 * the value for this tag is empty
402 */
403 public boolean hasUniqueEmptyValue(String key) {
404 if (key == null) return false;
405 Set<String> values = getTagsFor(key).getValues();
406 return values.size() == 1 && values.contains("");
407 }
408
409 /**
410 * Replies a tag collection with the tags for a given key. Replies an empty collection
411 * if key is null.
412 *
413 * @param key the key to look up
414 * @return a tag collection with the tags for a given key. Replies an empty collection
415 * if key is null.
416 */
417 public TagCollection getTagsFor(String key) {
418 TagCollection ret = new TagCollection();
419 if (key == null)
420 return ret;
421 for (Tag tag: tags) {
422 if (tag.matchesKey(key)) {
423 ret.add(tag);
424 }
425 }
426 return ret;
427 }
428
429 /**
430 * Replies a tag collection with all tags whose key is equal to one of the keys in
431 * <code>keys</code>. Replies an empty collection if keys is null.
432 *
433 * @param keys the keys to look up
434 * @return a tag collection with all tags whose key is equal to one of the keys in
435 * <code>keys</code>
436 */
437 public TagCollection getTagsFor(Collection<String> keys) {
438 TagCollection ret = new TagCollection();
439 if (keys == null)
440 return ret;
441 for(String key : keys) {
442 if (key != null) {
443 ret.add(getTagsFor(key));
444 }
445 }
446 return ret;
447 }
448
449 /**
450 * Replies the tags of this tag collection as set
451 *
452 * @return the tags of this tag collection as set
453 */
454 public Set<Tag> asSet() {
455 return new HashSet<Tag>(tags);
456 }
457
458 /**
459 * Replies the tags of this tag collection as list.
460 * Note that the order of the list is not preserved between method invocations.
461 *
462 * @return the tags of this tag collection as list.
463 */
464 public List<Tag> asList() {
465 return new ArrayList<Tag>(tags);
466 }
467
468 /**
469 * Replies an iterator to iterate over the tags in this collection
470 *
471 * @return the iterator
472 */
473 public Iterator<Tag> iterator() {
474 return tags.iterator();
475 }
476
477 /**
478 * Replies the set of keys of this tag collection.
479 *
480 * @return the set of keys of this tag collection
481 */
482 public Set<String> getKeys() {
483 HashSet<String> ret = new HashSet<String>();
484 for (Tag tag: tags) {
485 ret.add(tag.getKey());
486 }
487 return ret;
488 }
489
490 /**
491 * Replies the set of keys which have at least 2 matching tags.
492 *
493 * @return the set of keys which have at least 2 matching tags.
494 */
495 public Set<String> getKeysWithMultipleValues() {
496 HashMap<String, Integer> counters = new HashMap<String, Integer>();
497 for (Tag tag: tags) {
498 Integer v = counters.get(tag.getKey());
499 counters.put(tag.getKey(),(v==null) ? 1 : v+1);
500 }
501 Set<String> ret = new HashSet<String>();
502 for (Entry<String, Integer> e : counters.entrySet()) {
503 if (e.getValue() > 1) {
504 ret.add(e.getKey());
505 }
506 }
507 return ret;
508 }
509
510 /**
511 * Sets a unique tag for the key of this tag. All other tags with the same key are
512 * removed from the collection. Does nothing if tag is null.
513 *
514 * @param tag the tag to set
515 */
516 public void setUniqueForKey(Tag tag) {
517 if (tag == null) return;
518 removeByKey(tag.getKey());
519 add(tag);
520 }
521
522 /**
523 * Sets a unique tag for the key of this tag. All other tags with the same key are
524 * removed from the collection. Assume the empty string for key and value if either
525 * key or value is null.
526 *
527 * @param key the key
528 * @param value the value
529 */
530 public void setUniqueForKey(String key, String value) {
531 Tag tag = new Tag(key, value);
532 setUniqueForKey(tag);
533 }
534
535 /**
536 * Replies the set of values in this tag collection
537 *
538 * @return the set of values
539 */
540 public Set<String> getValues() {
541 HashSet<String> ret = new HashSet<String>();
542 for (Tag tag: tags) {
543 ret.add(tag.getValue());
544 }
545 return ret;
546 }
547
548 /**
549 * Replies the set of values for a given key. Replies an empty collection if there
550 * are no values for the given key.
551 *
552 * @param key the key to look up
553 * @return the set of values for a given key. Replies an empty collection if there
554 * are no values for the given key
555 */
556 public Set<String> getValues(String key) {
557 HashSet<String> ret = new HashSet<String>();
558 if (key == null) return ret;
559 for (Tag tag: tags) {
560 if (tag.matchesKey(key)) {
561 ret.add(tag.getValue());
562 }
563 }
564 return ret;
565 }
566
567 /**
568 * Replies true if for every key there is one tag only, i.e. exactly one value.
569 *
570 * @return
571 */
572 public boolean isApplicableToPrimitive() {
573 return size() == getKeys().size();
574 }
575
576 /**
577 * Applies this tag collection to an {@see OsmPrimitive}. Does nothing if
578 * primitive is null
579 *
580 * @param primitive the primitive
581 * @throws IllegalStateException thrown if this tag collection can't be applied
582 * because there are keys with multiple values
583 */
584 public void applyTo(Tagged primitive) throws IllegalStateException {
585 if (primitive == null) return;
586 if (! isApplicableToPrimitive())
587 throw new IllegalStateException(tr("Tag collection cannot be applied to a primitive because there are keys with multiple values."));
588 for (Tag tag: tags) {
589 if (tag.getValue() == null || tag.getValue().equals("")) {
590 primitive.remove(tag.getKey());
591 } else {
592 primitive.put(tag.getKey(), tag.getValue());
593 }
594 }
595 }
596
597 /**
598 * Applies this tag collection to a collection of {@see OsmPrimitive}s. Does nothing if
599 * primitives is null
600 *
601 * @param primitives the collection of primitives
602 * @throws IllegalStateException thrown if this tag collection can't be applied
603 * because there are keys with multiple values
604 */
605 public void applyTo(Collection<? extends Tagged> primitives) throws IllegalStateException{
606 if (primitives == null) return;
607 if (! isApplicableToPrimitive())
608 throw new IllegalStateException(tr("Tag collection cannot be applied to a primitive because there are keys with multiple values."));
609 for (Tagged primitive: primitives) {
610 applyTo(primitive);
611 }
612 }
613
614 /**
615 * Replaces the tags of an {@see OsmPrimitive} by the tags in this collection . Does nothing if
616 * primitive is null
617 *
618 * @param primitive the primitive
619 * @throws IllegalStateException thrown if this tag collection can't be applied
620 * because there are keys with multiple values
621 */
622 public void replaceTagsOf(Tagged primitive) throws IllegalStateException {
623 if (primitive == null) return;
624 if (! isApplicableToPrimitive())
625 throw new IllegalStateException(tr("Tag collection cannot be applied to a primitive because there are keys with multiple values."));
626 primitive.removeAll();
627 for (Tag tag: tags) {
628 primitive.put(tag.getKey(), tag.getValue());
629 }
630 }
631
632 /**
633 * Replaces the tags of a collection of{@see OsmPrimitive}s by the tags in this collection.
634 * Does nothing if primitives is null
635 *
636 * @param primitive the collection of primitives
637 * @throws IllegalStateException thrown if this tag collection can't be applied
638 * because there are keys with multiple values
639 */
640 public void replaceTagsOf(Collection<? extends Tagged> primitives) throws IllegalStateException {
641 if (primitives == null) return;
642 if (! isApplicableToPrimitive())
643 throw new IllegalStateException(tr("Tag collection cannot be applied to a primitive because there are keys with multiple values."));
644 for (Tagged primitive: primitives) {
645 replaceTagsOf(primitive);
646 }
647 }
648
649 /**
650 * Builds the intersection of this tag collection and another tag collection
651 *
652 * @param other the other tag collection. If null, replies an empty tag collection.
653 * @return the intersection of this tag collection and another tag collection
654 */
655 public TagCollection intersect(TagCollection other) {
656 if (other == null) {
657 other = new TagCollection();
658 }
659 TagCollection ret = new TagCollection(this);
660 for (Tag tag: tags) {
661 if (other.contains(tag)) {
662 ret.add(tag);
663 }
664 }
665 return ret;
666 }
667
668 /**
669 * Replies the difference of this tag collection and another tag collection
670 *
671 * @param other the other tag collection. May be null.
672 * @return the difference of this tag collection and another tag collection
673 */
674 public TagCollection minus(TagCollection other) {
675 TagCollection ret = new TagCollection(this);
676 if (other != null) {
677 ret.remove(other);
678 }
679 return ret;
680 }
681
682 /**
683 * Replies the union of this tag collection and another tag collection
684 *
685 * @param other the other tag collection. May be null.
686 * @return the union of this tag collection and another tag collection
687 */
688 public TagCollection union(TagCollection other) {
689 TagCollection ret = new TagCollection(this);
690 if (other != null) {
691 ret.add(other);
692 }
693 return ret;
694 }
695
696 public TagCollection emptyTagsForKeysMissingIn(TagCollection other) {
697 TagCollection ret = new TagCollection();
698 for(String key: this.minus(other).getKeys()) {
699 ret.add(new Tag(key));
700 }
701 return ret;
702 }
703
704 /**
705 * Replies the concatenation of all tag values (concatenated by a semicolon)
706 *
707 * @return the concatenation of all tag values
708 */
709 public String getJoinedValues(String key) {
710 StringBuffer buffer = new StringBuffer();
711 List<String> values = new ArrayList<String>(getValues(key));
712 values.remove("");
713 Collections.sort(values);
714 Iterator<String> iter = values.iterator();
715 while (iter.hasNext()) {
716 buffer.append(iter.next());
717 if (iter.hasNext()) {
718 buffer.append(";");
719 }
720 }
721 return buffer.toString();
722 }
723
724 @Override
725 public String toString() {
726 return tags.toString();
727 }
728}
Note: See TracBrowser for help on using the repository browser.