001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.validation.tests; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.util.Collection; 007import java.util.LinkedHashMap; 008import java.util.LinkedList; 009import java.util.List; 010import java.util.Map; 011 012import org.openstreetmap.josm.command.ChangePropertyCommand; 013import org.openstreetmap.josm.command.ChangePropertyKeyCommand; 014import org.openstreetmap.josm.command.Command; 015import org.openstreetmap.josm.command.SequenceCommand; 016import org.openstreetmap.josm.data.osm.Node; 017import org.openstreetmap.josm.data.osm.OsmPrimitive; 018import org.openstreetmap.josm.data.osm.Relation; 019import org.openstreetmap.josm.data.osm.Tag; 020import org.openstreetmap.josm.data.osm.Way; 021import org.openstreetmap.josm.data.validation.Severity; 022import org.openstreetmap.josm.data.validation.Test; 023import org.openstreetmap.josm.data.validation.TestError; 024import org.openstreetmap.josm.tools.Utils; 025 026/** 027 * Checks and corrects deprecated and unnecessary tags. 028 * @since 4442 029 */ 030public class DeprecatedTags extends Test { 031 032 private final List<DeprecationCheck> checks = new LinkedList<DeprecationCheck>(); 033 034 /** 035 * Constructs a new {@code DeprecatedTags} test. 036 */ 037 public DeprecatedTags() { 038 super(tr("Deprecated Tags"), tr("Checks and corrects deprecated tags.")); 039 checks.add(new DeprecationCheck(2101). 040 testAndRemove("barrier", "wire_fence"). 041 add("barrier", "fence"). 042 add("fence_type", "chain_link")); 043 checks.add(new DeprecationCheck(2102). 044 testAndRemove("barrier", "wood_fence"). 045 add("barrier", "fence"). 046 add("fence_type", "wood")); 047 checks.add(new DeprecationCheck(2103). 048 testAndRemove("highway", "ford"). 049 add("ford", "yes")); 050 // from http://wiki.openstreetmap.org/wiki/Deprecated_features 051 checks.add(new DeprecationCheck(2104). 052 test("class"). 053 alternative("highway")); 054 checks.add(new DeprecationCheck(2105). 055 testAndRemove("highway", "stile"). 056 add("barrier", "stile")); 057 checks.add(new DeprecationCheck(2106). 058 test("highway", "incline"). 059 alternative("incline")); 060 checks.add(new DeprecationCheck(2107). 061 test("highway", "incline"). 062 alternative("incline")); 063 checks.add(new DeprecationCheck(2108). 064 testAndRemove("highway", "unsurfaced"). 065 add("highway", "road"). 066 add("incline", "unpaved")); 067 checks.add(new DeprecationCheck(2109). 068 test("landuse", "wood"). 069 alternative("landuse", "forest"). 070 alternative("natural", "wood")); 071 checks.add(new DeprecationCheck(2110). 072 testAndRemove("natural", "marsh"). 073 add("natural", "wetland"). 074 add("wetland", "marsh")); 075 checks.add(new DeprecationCheck(2111). 076 test("highway", "byway")); 077 checks.add(new DeprecationCheck(2112). 078 test("power_source"). 079 alternative("generator:source")); 080 checks.add(new DeprecationCheck(2113). 081 test("power_rating"). 082 alternative("generator:output")); 083 // from http://wiki.openstreetmap.org/wiki/Tag:shop=organic 084 checks.add(new DeprecationCheck(2114). 085 testAndRemove("shop", "organic"). 086 add("shop", "supermarket"). 087 add("organic", "only")); 088 // from http://wiki.openstreetmap.org/wiki/Key:bicycle_parking 089 checks.add(new DeprecationCheck(2115). 090 testAndRemove("bicycle_parking", "sheffield"). 091 add("bicycle_parking", "stands")); 092 // http://wiki.openstreetmap.org/wiki/Tag:emergency=phone 093 checks.add(new DeprecationCheck(2116). 094 testAndRemove("amenity", "emergency_phone"). 095 add("emergency", "phone")); 096 // fix #8132 - http://wiki.openstreetmap.org/wiki/Tag:sport=gaelic_football 097 checks.add(new DeprecationCheck(2117). 098 testAndRemove("sport", "gaelic_football"). 099 add("sport", "gaelic_games")); 100 // see #8847 / #8961 - http://wiki.openstreetmap.org/wiki/Tag:power=station 101 checks.add(new DeprecationCheck(2118). 102 test("power", "station"). 103 alternative("power", "plant"). 104 alternative("power", "sub_station")); 105 checks.add(new DeprecationCheck(2119). 106 testAndRemove("generator:method", "dam"). 107 add("generator:method", "water-storage")); 108 checks.add(new DeprecationCheck(2120). 109 testAndRemove("generator:method", "pumped-storage"). 110 add("generator:method", "water-pumped-storage")); 111 checks.add(new DeprecationCheck(2121). 112 testAndRemove("generator:method", "pumping"). 113 add("generator:method", "water-pumped-storage")); 114 // see #8962 - http://wiki.openstreetmap.org/wiki/Key:fence_type 115 checks.add(new DeprecationCheck(2122). 116 test("fence_type", "chain"). 117 alternative("barrier", "chain"). 118 alternative("fence_type", "chain_link")); 119 // see #9000 - http://wiki.openstreetmap.org/wiki/Key:entrance 120 checks.add(new DeprecationCheck(2123). 121 test("building", "entrance"). 122 alternative("entrance")); 123 // see #9213 - Useless tag proposed in internal preset for years 124 checks.add(new DeprecationCheck(2124). 125 testAndRemove("board_type", "board")); 126 // see #8434 - http://wiki.openstreetmap.org/wiki/Proposed_features/monitoring_station 127 checks.add(new DeprecationCheck(2125). 128 testAndRemove("man_made", "measurement_station"). 129 add("man_made", "monitoring_station")); 130 checks.add(new DeprecationCheck(2126). 131 testAndRemove("measurement", "water_level"). 132 add("monitoring:water_level", "yes")); 133 checks.add(new DeprecationCheck(2127). 134 testAndRemove("measurement", "weather"). 135 add("monitoring:weather", "yes")); 136 checks.add(new DeprecationCheck(2128). 137 testAndRemove("measurement", "seismic"). 138 add("monitoring:seismic_activity", "yes")); 139 checks.add(new DeprecationCheck(2129). 140 test("monitoring:river_level"). 141 changeKey("monitoring:river_level", "monitoring:water_level")); 142 // see #9365 - Useless tag layer=0 143 checks.add(new UnnecessaryTagCheck(2130). 144 testAndRemove("layer", "0")); 145 } 146 147 /** 148 * Visiting call for primitives. 149 * @param p The primitive to inspect. 150 */ 151 public void visit(OsmPrimitive p) { 152 for (DeprecationCheck check : checks) { 153 if (check.matchesPrimitive(p)) { 154 errors.add(new DeprecationError(p, check)); 155 } 156 } 157 } 158 159 @Override 160 public void visit(Node n) { 161 visit((OsmPrimitive) n); 162 } 163 164 @Override 165 public void visit(Way w) { 166 visit((OsmPrimitive) w); 167 } 168 169 @Override 170 public void visit(Relation r) { 171 visit((OsmPrimitive) r); 172 } 173 174 /** 175 * Represents on deprecation check consisting of a series of {@code test}s, 176 * automatic {@code change}s/{@code keyChange}s (fixes for the deprecated tag), 177 * or a suggestion of tagging {@code alternatives}. 178 */ 179 private static class DeprecationCheck { 180 181 private int code; 182 protected final List<Tag> test = new LinkedList<Tag>(); 183 protected final List<Tag> change = new LinkedList<Tag>(); 184 protected final Map<String, String> keyChange = new LinkedHashMap<String, String>(); 185 protected final List<Tag> alternatives = new LinkedList<Tag>(); 186 187 /** 188 * Creates a new {@code DeprecationCheck}. 189 * @param code {@link TestError#code} 190 */ 191 public DeprecationCheck(int code) { 192 this.code = code; 193 } 194 195 /** 196 * Adds a test criterion which matches primitives with tag {@code key=value}. 197 * @return {@code this} 198 */ 199 DeprecationCheck test(String key, String value) { 200 test.add(new Tag(key, value)); 201 return this; 202 } 203 204 /** 205 * Adds a test criterion which matches primitives with key {@code key}. 206 * @return {@code this} 207 */ 208 DeprecationCheck test(String key) { 209 return test(key, null); 210 } 211 212 /** 213 * Adds an automatic fix which sets/adds the tag {@code key=value}. 214 * @return {@code this} 215 * @see #alternative(String, String) 216 * @see #alternative(String) 217 */ 218 DeprecationCheck add(String key, String value) { 219 change.add(new Tag(key, value)); 220 return this; 221 } 222 223 /** 224 * Adds an automatic fix which removes the key {@code key}. 225 * @return {@code this} 226 */ 227 DeprecationCheck remove(String key) { 228 change.add(new Tag(key)); 229 return this; 230 } 231 232 /** 233 * Adds an automatic fix which changes the key {@code oldKey} to {@code newKey}. 234 * @return {@code this} 235 */ 236 DeprecationCheck changeKey(String oldKey, String newKey) { 237 keyChange.put(oldKey, newKey); 238 return this; 239 } 240 241 /** 242 * Adds a test criterion which matches primitives with tag {@code key=value}, 243 * and an automatic fix which removes the key {@code key}. 244 * Equivalent to {@link #test(String, String)} plus {@link #remove(String)}. 245 * @return {@code this} 246 */ 247 DeprecationCheck testAndRemove(String key, String value) { 248 return test(key, value).remove(key); 249 } 250 251 /** 252 * Adds a suggestion to use an alternative tag {@code key=value} instead of the deprecated tag. 253 * This is used for cases where no automatic fix is sensible. 254 * @return {@code this} 255 */ 256 DeprecationCheck alternative(String key, String value) { 257 alternatives.add(new Tag(key, value)); 258 return this; 259 } 260 261 /** 262 * Adds a suggestion to use an alternative key {@code key} instead of the deprecated tag. 263 * This is used for cases where no automatic fix is sensible. 264 * @return {@code this} 265 */ 266 DeprecationCheck alternative(String key) { 267 return alternative(key, null); 268 } 269 270 /** 271 * Tests whether the {@link OsmPrimitive} contains a deprecated tag which is represented by this {@code DeprecationCheck}. 272 * @param p the primitive to test 273 * @return true when the primitive contains a deprecated tag 274 */ 275 boolean matchesPrimitive(OsmPrimitive p) { 276 for (Tag tag : test) { 277 String key = tag.getKey(); 278 String value = tag.getValue(); 279 if (value.isEmpty() && !p.hasKey(key)) 280 return false; 281 if (!value.isEmpty() && !value.equals(p.get(key))) 282 return false; 283 } 284 return true; 285 } 286 287 /** 288 * Constructs a fix in terms of a {@link Command} for the {@link OsmPrimitive}. 289 * @param p the primitive to construct the fix for 290 * @return the fix 291 */ 292 Command fixPrimitive(OsmPrimitive p) { 293 Collection<Command> cmds = new LinkedList<Command>(); 294 for (Tag tag : change) { 295 cmds.add(new ChangePropertyCommand(p, tag.getKey(), tag.getValue())); 296 } 297 for (Map.Entry<String, String> i : keyChange.entrySet()) { 298 cmds.add(new ChangePropertyKeyCommand(p, i.getKey(), i.getValue())); 299 } 300 return new SequenceCommand(tr("Deprecation fix of {0}", Utils.join(", ", test)), cmds); 301 } 302 303 /** 304 * Constructs a localized description for this deprecation check. 305 * @return a localized description 306 */ 307 String getDescription() { 308 if (alternatives.isEmpty()) 309 return tr("{0} is deprecated", Utils.join(", ", test)); 310 else 311 return tr("{0} is deprecated, use {1} instead", Utils.join(", ", test), Utils.join(tr(" or "), alternatives)); 312 } 313 } 314 315 private static class UnnecessaryTagCheck extends DeprecationCheck { 316 317 public UnnecessaryTagCheck(int code) { 318 super(code); 319 } 320 321 @Override 322 String getDescription() { 323 return tr("{0} is unnecessary", Utils.join(", ", test)); 324 } 325 } 326 327 private class DeprecationError extends TestError { 328 329 private OsmPrimitive p; 330 private DeprecationCheck check; 331 332 public DeprecationError(OsmPrimitive p, DeprecationCheck check) { 333 super(DeprecatedTags.this, Severity.WARNING, check.getDescription(), check.code, p); 334 this.p = p; 335 this.check = check; 336 } 337 338 @Override 339 public boolean isFixable() { 340 return !check.change.isEmpty() || !check.keyChange.isEmpty(); 341 } 342 343 @Override 344 public Command getFix() { 345 return check.fixPrimitive(p); 346 } 347 } 348}