001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.osm; 003 004import java.io.Serializable; 005import java.util.Collection; 006import java.util.Collections; 007import java.util.Map; 008import java.util.Map.Entry; 009import java.util.Objects; 010 011import org.openstreetmap.josm.tools.CheckParameterUtil; 012import org.openstreetmap.josm.tools.Utils; 013 014/** 015 * Tag represents an immutable key/value-pair. Both the key and the value may be empty, but not null. 016 * <p> 017 * It implements the {@link Tagged} interface. However, since instances of this class are immutable, 018 * the modifying methods throw an {@link UnsupportedOperationException}. 019 */ 020public class Tag implements Tagged, Entry<String, String>, Serializable { 021 022 private static final long serialVersionUID = 1; 023 024 private final String key; 025 private final String value; 026 027 /** 028 * Create an empty tag whose key and value are empty. 029 */ 030 public Tag() { 031 this("", ""); 032 } 033 034 /** 035 * Create a tag whose key is <code>key</code> and whose value is 036 * empty. 037 * 038 * @param key the key. If null, it is set to the empty key. 039 */ 040 public Tag(String key) { 041 this(key, ""); 042 } 043 044 /** 045 * Creates a tag for a key and a value. If key and/or value are null, 046 * the empty value "" is assumed. 047 * 048 * @param key the key 049 * @param value the value 050 */ 051 public Tag(String key, String value) { 052 this.key = key == null ? "" : key; 053 this.value = value == null ? "" : value; 054 } 055 056 /** 057 * Creates clone of the tag <code>tag</code>. 058 * 059 * @param tag the tag. 060 */ 061 public Tag(Tag tag) { 062 this(tag.getKey(), tag.getValue()); 063 } 064 065 /** 066 * Replies the key of the tag. This is never null. 067 * 068 * @return the key of the tag 069 */ 070 @Override 071 public String getKey() { 072 return key; 073 } 074 075 /** 076 * Replies the value of the tag. This is never null. 077 * 078 * @return the value of the tag 079 */ 080 @Override 081 public String getValue() { 082 return value; 083 } 084 085 /** 086 * This is not supported by this implementation. 087 * @param value ignored 088 * @return (Does not return) 089 * @throws UnsupportedOperationException always 090 */ 091 @Override 092 public String setValue(String value) { 093 throw new UnsupportedOperationException(); 094 } 095 096 /** 097 * Replies true if the key of this tag is equal to <code>key</code>. 098 * If <code>key</code> is null, assumes the empty key. 099 * 100 * @param key the key 101 * @return true if the key of this tag is equal to <code>key</code> 102 */ 103 public boolean matchesKey(String key) { 104 return this.key.equals(key); 105 } 106 107 @Override 108 public int hashCode() { 109 return Objects.hash(key, value); 110 } 111 112 @Override 113 public boolean equals(Object obj) { 114 if (this == obj) return true; 115 if (obj == null || getClass() != obj.getClass()) return false; 116 Tag tag = (Tag) obj; 117 return Objects.equals(key, tag.key) && 118 Objects.equals(value, tag.value); 119 } 120 121 /** 122 * This constructs a {@link Tag} by splitting {@code s} on the first equality sign. 123 * @param s the string to convert 124 * @return the constructed tag 125 * @see org.openstreetmap.josm.tools.TextTagParser 126 */ 127 public static Tag ofString(String s) { 128 CheckParameterUtil.ensureParameterNotNull(s, "s"); 129 final String[] x = s.split("=", 2); 130 if (x.length == 2) { 131 return new Tag(x[0], x[1]); 132 } else { 133 throw new IllegalArgumentException('\'' + s + "' does not contain '='"); 134 } 135 } 136 137 @Override 138 public String toString() { 139 return key + '=' + value; 140 } 141 142 /** 143 * Removes leading, trailing, and multiple inner whitespaces from the given string, to be used as a key or value. 144 * @param s The string 145 * @return The string without leading, trailing or multiple inner whitespaces 146 * @since 6699 147 * @deprecated since 13597. Use {@link Utils#removeWhiteSpaces(String)} instead 148 */ 149 @Deprecated 150 public static String removeWhiteSpaces(String s) { 151 return Utils.removeWhiteSpaces(s); 152 } 153 154 /** 155 * Unsupported. 156 * @param keys ignored 157 * @throws UnsupportedOperationException always 158 */ 159 @Override 160 public void setKeys(Map<String, String> keys) { 161 throw new UnsupportedOperationException(); 162 } 163 164 @Override 165 public Map<String, String> getKeys() { 166 return Collections.singletonMap(key, value); 167 } 168 169 /** 170 * Unsupported. 171 * @param key ignored 172 * @param value ignored 173 * @throws UnsupportedOperationException always 174 */ 175 @Override 176 public void put(String key, String value) { 177 throw new UnsupportedOperationException(); 178 } 179 180 @Override 181 public String get(String k) { 182 return key.equals(k) ? value : null; 183 } 184 185 /** 186 * Unsupported. 187 * @param key ignored 188 * @throws UnsupportedOperationException always 189 */ 190 @Override 191 public void remove(String key) { 192 throw new UnsupportedOperationException(); 193 } 194 195 @Override 196 public boolean hasKeys() { 197 return true; 198 } 199 200 @Override 201 public Collection<String> keySet() { 202 return Collections.singleton(key); 203 } 204 205 @Override 206 public final int getNumKeys() { 207 return 1; 208 } 209 210 /** 211 * Unsupported. 212 * @throws UnsupportedOperationException always 213 */ 214 @Override 215 public void removeAll() { 216 throw new UnsupportedOperationException(); 217 } 218 219 /** 220 * true if this is a direction dependent tag (e.g. oneway) 221 * 222 * @return {@code true} if this is is a direction dependent tag 223 * @since 10716 224 */ 225 public boolean isDirectionKey() { 226 return OsmPrimitive.directionKeys.match(this); 227 } 228 229}