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