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