001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.osm; 003 004import java.util.Arrays; 005import java.util.Collection; 006import java.util.Map; 007import java.util.Objects; 008import java.util.stream.Collectors; 009import java.util.stream.Stream; 010 011import org.openstreetmap.josm.tools.Utils; 012 013/** 014 * Objects implement Tagged if they provide a map of key/value pairs. 015 * 016 * @since 2115 017 */ 018// FIXME: better naming? setTags(), getTags(), getKeys() instead of keySet() ? 019// 020public interface Tagged { 021 022 /** 023 * The maximum tag length allowed by OSM API 024 * @since 13414 025 */ 026 int MAX_TAG_LENGTH = 255; 027 028 /** 029 * Sets the map of key/value pairs 030 * 031 * @param keys the map of key value pairs. If null, reset to the empty map. 032 */ 033 void setKeys(Map<String, String> keys); 034 035 /** 036 * Replies the map of key/value pairs. Never null, but may be the empty map. 037 * 038 * @return the map of key/value pairs 039 */ 040 Map<String, String> getKeys(); 041 042 /** 043 * Calls the visitor for every key/value pair. 044 * 045 * @param visitor The visitor to call. 046 * @see #getKeys() 047 * @since 13668 048 */ 049 default void visitKeys(KeyValueVisitor visitor) { 050 getKeys().forEach((k, v) -> visitor.visitKeyValue(this, k, v)); 051 } 052 053 /** 054 * Sets a key/value pairs 055 * 056 * @param key the key 057 * @param value the value. If null, removes the key/value pair. 058 */ 059 void put(String key, String value); 060 061 /** 062 * Sets a key/value pairs 063 * 064 * @param tag The tag to set. 065 * @since 10736 066 */ 067 default void put(Tag tag) { 068 put(tag.getKey(), tag.getValue()); 069 } 070 071 /** 072 * Replies the value of the given key; null, if there is no value for this key 073 * 074 * @param key the key 075 * @return the value 076 */ 077 String get(String key); 078 079 /** 080 * Removes a given key/value pair 081 * 082 * @param key the key 083 */ 084 void remove(String key); 085 086 /** 087 * Replies true, if there is at least one key/value pair; false, otherwise 088 * 089 * @return true, if there is at least one key/value pair; false, otherwise 090 */ 091 boolean hasKeys(); 092 093 /** 094 * Replies true if there is a tag with key <code>key</code>. 095 * The value could however be empty. See {@link #hasTag(String)} to check for non-empty tags. 096 * 097 * @param key the key 098 * @return true, if there is a tag with key <code>key</code> 099 * @see #hasTag(String) 100 * @since 11608 101 */ 102 default boolean hasKey(String key) { 103 return get(key) != null; 104 } 105 106 /** 107 * Replies true if there is a non-empty tag with key <code>key</code>. 108 * 109 * @param key the key 110 * @return true, if there is a non-empty tag with key <code>key</code> 111 * @see Tagged#hasKey(String) 112 * @since 13430 113 */ 114 default boolean hasTag(String key) { 115 return !Utils.isEmpty(get(key)); 116 } 117 118 /** 119 * Tests whether this primitive contains a tag consisting of {@code key} and {@code value}. 120 * @param key the key forming the tag. 121 * @param value value forming the tag. 122 * @return true if primitive contains a tag consisting of {@code key} and {@code value}. 123 * @since 13668 124 */ 125 default boolean hasTag(String key, String value) { 126 return Objects.equals(value, get(key)); 127 } 128 129 /** 130 * Tests whether this primitive contains a tag consisting of {@code key} and any of {@code values}. 131 * @param key the key forming the tag. 132 * @param values one or many values forming the tag. 133 * @return true if primitive contains a tag consisting of {@code key} and any of {@code values}. 134 * @since 13668 135 */ 136 default boolean hasTag(String key, String... values) { 137 return hasTag(key, Arrays.asList(values)); 138 } 139 140 /** 141 * Tests whether this primitive contains a tag consisting of {@code key} and any of {@code values}. 142 * @param key the key forming the tag. 143 * @param values one or many values forming the tag. 144 * @return true if primitive contains a tag consisting of {@code key} and any of {@code values}. 145 * @since 13668 146 */ 147 default boolean hasTag(String key, Collection<String> values) { 148 String v = get(key); 149 return v != null && values.contains(v); 150 } 151 152 /** 153 * Tests whether this primitive contains a tag consisting of {@code key} and a value different from {@code value}. 154 * @param key the key forming the tag. 155 * @param value value not forming the tag. 156 * @return true if primitive contains a tag consisting of {@code key} and a value different from {@code value}. 157 * @since 13668 158 */ 159 default boolean hasTagDifferent(String key, String value) { 160 String v = get(key); 161 return v != null && !v.equals(value); 162 } 163 164 /** 165 * Tests whether this primitive contains a tag consisting of {@code key} and none of {@code values}. 166 * @param key the key forming the tag. 167 * @param values one or many values forming the tag. 168 * @return true if primitive contains a tag consisting of {@code key} and none of {@code values}. 169 * @since 13668 170 */ 171 default boolean hasTagDifferent(String key, String... values) { 172 return hasTagDifferent(key, Arrays.asList(values)); 173 } 174 175 /** 176 * Tests whether this primitive contains a tag consisting of {@code key} and none of {@code values}. 177 * @param key the key forming the tag. 178 * @param values one or many values forming the tag. 179 * @return true if primitive contains a tag consisting of {@code key} and none of {@code values}. 180 * @since 13668 181 */ 182 default boolean hasTagDifferent(String key, Collection<String> values) { 183 String v = get(key); 184 return v != null && !values.contains(v); 185 } 186 187 /** 188 * Replies the set of keys 189 * 190 * @return the set of keys 191 * @see #keys() 192 */ 193 Collection<String> keySet(); 194 195 /** 196 * Replies the keys as stream 197 * 198 * @return the keys as stream 199 * @see #keySet() 200 * @since 17584 201 */ 202 default Stream<String> keys() { 203 return keySet().stream(); 204 } 205 206 /** 207 * Gets the number of keys 208 * @return The number of keys set for this tagged object. 209 * @since 13625 210 */ 211 int getNumKeys(); 212 213 /** 214 * Removes all tags 215 */ 216 void removeAll(); 217 218 /** 219 * Returns true if the {@code key} corresponds to an OSM true value. 220 * @param key OSM key 221 * @return {@code true} if the {@code key} corresponds to an OSM true value 222 * @see OsmUtils#isTrue(String) 223 */ 224 default boolean isKeyTrue(String key) { 225 return OsmUtils.isTrue(get(key)); 226 } 227 228 /** 229 * Returns true if the {@code key} corresponds to an OSM false value. 230 * @param key OSM key 231 * @return {@code true} if the {@code key} corresponds to an OSM false value 232 * @see OsmUtils#isFalse(String) 233 */ 234 default boolean isKeyFalse(String key) { 235 return OsmUtils.isFalse(get(key)); 236 } 237 238 /** 239 * Returns a Tagged instance for the given tag collection 240 * @param tags the tag collection 241 * @return a Tagged instance for the given tag collection 242 */ 243 static Tagged ofTags(Collection<Tag> tags) { 244 return ofMap(tags.stream().collect(Collectors.toMap(Tag::getKey, Tag::getValue, (a, b) -> a))); 245 } 246 247 /** 248 * Returns a Tagged instance for the given tag map 249 * @param tags the tag map 250 * @return a Tagged instance for the given tag map 251 */ 252 static Tagged ofMap(Map<String, String> tags) { 253 return new Tagged() { 254 255 @Override 256 public String get(String key) { 257 return tags.get(key); 258 } 259 260 @Override 261 public Map<String, String> getKeys() { 262 return tags; 263 } 264 265 @Override 266 public Collection<String> keySet() { 267 return tags.keySet(); 268 } 269 270 @Override 271 public void put(String key, String value) { 272 tags.put(key, value); 273 } 274 275 @Override 276 public void setKeys(Map<String, String> keys) { 277 tags.putAll(keys); 278 } 279 280 @Override 281 public boolean hasKeys() { 282 return !tags.isEmpty(); 283 } 284 285 @Override 286 public int getNumKeys() { 287 return tags.size(); 288 } 289 290 @Override 291 public void remove(String key) { 292 tags.remove(key); 293 } 294 295 @Override 296 public void removeAll() { 297 tags.clear(); 298 } 299 }; 300 } 301}