source: josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java@ 7509

Last change on this file since 7509 was 7005, checked in by Don-vip, 10 years ago

see #8465 - use diamond operator where applicable

  • Property svn:eol-style set to native
File size: 9.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.corrector;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.HashMap;
9import java.util.List;
10import java.util.Map;
11import java.util.regex.Matcher;
12import java.util.regex.Pattern;
13
14import org.openstreetmap.josm.command.Command;
15import org.openstreetmap.josm.data.osm.OsmPrimitive;
16import org.openstreetmap.josm.data.osm.OsmUtils;
17import org.openstreetmap.josm.data.osm.Relation;
18import org.openstreetmap.josm.data.osm.RelationMember;
19import org.openstreetmap.josm.data.osm.Tag;
20import org.openstreetmap.josm.data.osm.TagCollection;
21import org.openstreetmap.josm.data.osm.Way;
22
23/**
24 * A ReverseWayTagCorrector handles necessary corrections of tags
25 * when a way is reversed. E.g. oneway=yes needs to be changed
26 * to oneway=-1 and vice versa.
27 *
28 * The Corrector offers the automatic resolution in an dialog
29 * for the user to confirm.
30 */
31public class ReverseWayTagCorrector extends TagCorrector<Way> {
32
33 private static final String SEPARATOR = "[:_]";
34
35 private static final Pattern getPatternFor(String s) {
36 return getPatternFor(s, false);
37 }
38
39 private static final Pattern getPatternFor(String s, boolean exactMatch) {
40 if (exactMatch) {
41 return Pattern.compile("(^)(" + s + ")($)");
42 } else {
43 return Pattern.compile("(^|.*" + SEPARATOR + ")(" + s + ")(" + SEPARATOR + ".*|$)",
44 Pattern.CASE_INSENSITIVE);
45 }
46 }
47
48 private static final Collection<Pattern> ignoredKeys = new ArrayList<>();
49 static {
50 for (String s : OsmPrimitive.getUninterestingKeys()) {
51 ignoredKeys.add(getPatternFor(s));
52 }
53 for (String s : new String[]{"name", "ref", "tiger:county"}) {
54 ignoredKeys.add(getPatternFor(s, false));
55 }
56 for (String s : new String[]{"tiger:county", "turn:lanes", "change:lanes", "placement"}) {
57 ignoredKeys.add(getPatternFor(s, true));
58 }
59 }
60
61 private static class StringSwitcher {
62
63 private final String a;
64 private final String b;
65 private final Pattern pattern;
66
67 public StringSwitcher(String a, String b) {
68 this.a = a;
69 this.b = b;
70 this.pattern = getPatternFor(a + "|" + b);
71 }
72
73 public String apply(String text) {
74 Matcher m = pattern.matcher(text);
75
76 if (m.lookingAt()) {
77 String leftRight = m.group(2).toLowerCase();
78
79 StringBuilder result = new StringBuilder();
80 result.append(text.substring(0, m.start(2)));
81 result.append(leftRight.equals(a) ? b : a);
82 result.append(text.substring(m.end(2)));
83
84 return result.toString();
85 }
86 return text;
87 }
88 }
89
90 /**
91 * Reverses a given tag.
92 * @since 5787
93 */
94 public static final class TagSwitcher {
95
96 private TagSwitcher() {
97 // Hide implicit public constructor for utility class
98 }
99
100 /**
101 * Reverses a given tag.
102 * @param tag The tag to reverse
103 * @return The reversed tag (is equal to <code>tag</code> if no change is needed)
104 */
105 public static final Tag apply(final Tag tag) {
106 return apply(tag.getKey(), tag.getValue());
107 }
108
109 /**
110 * Reverses a given tag (key=value).
111 * @param key The tag key
112 * @param value The tag value
113 * @return The reversed tag (is equal to <code>key=value</code> if no change is needed)
114 */
115 public static final Tag apply(final String key, final String value) {
116 String newKey = key;
117 String newValue = value;
118
119 if (key.startsWith("oneway") || key.endsWith("oneway")) {
120 if (OsmUtils.isReversed(value)) {
121 newValue = OsmUtils.trueval;
122 } else if (OsmUtils.isTrue(value)) {
123 newValue = OsmUtils.reverseval;
124 }
125 } else if (key.startsWith("incline") || key.endsWith("incline")
126 || key.startsWith("direction") || key.endsWith("direction")) {
127 newValue = UP_DOWN.apply(value);
128 if (newValue.equals(value)) {
129 newValue = invertNumber(value);
130 }
131 } else if (key.endsWith(":forward") || key.endsWith(":backward")) {
132 // Change key but not left/right value (fix #8518)
133 newKey = FORWARD_BACKWARD.apply(key);
134
135 } else if (!ignoreKeyForCorrection(key)) {
136 for (StringSwitcher prefixSuffixSwitcher : stringSwitchers) {
137 newKey = prefixSuffixSwitcher.apply(key);
138 if (!key.equals(newKey)) {
139 break;
140 }
141 newValue = prefixSuffixSwitcher.apply(value);
142 if (!value.equals(newValue)) {
143 break;
144 }
145 }
146 }
147 return new Tag(newKey, newValue);
148 }
149 }
150
151 private static final StringSwitcher FORWARD_BACKWARD = new StringSwitcher("forward", "backward");
152 private static final StringSwitcher UP_DOWN = new StringSwitcher("up", "down");
153
154 private static final StringSwitcher[] stringSwitchers = new StringSwitcher[] {
155 new StringSwitcher("left", "right"),
156 new StringSwitcher("forwards", "backwards"),
157 new StringSwitcher("east", "west"),
158 new StringSwitcher("north", "south"),
159 FORWARD_BACKWARD, UP_DOWN
160 };
161
162 /**
163 * Tests whether way can be reversed without semantic change, i.e., whether tags have to be changed.
164 * Looks for keys like oneway, oneway:bicycle, cycleway:right:oneway, left/right.
165 * @param way
166 * @return false if tags should be changed to keep semantic, true otherwise.
167 */
168 public static boolean isReversible(Way way) {
169 for (Tag tag : TagCollection.from(way)) {
170 if (!tag.equals(TagSwitcher.apply(tag))) {
171 return false;
172 }
173 }
174 return true;
175 }
176
177 public static List<Way> irreversibleWays(List<Way> ways) {
178 List<Way> newWays = new ArrayList<>(ways);
179 for (Way way : ways) {
180 if (isReversible(way)) {
181 newWays.remove(way);
182 }
183 }
184 return newWays;
185 }
186
187 public static String invertNumber(String value) {
188 Pattern pattern = Pattern.compile("^([+-]?)(\\d.*)$", Pattern.CASE_INSENSITIVE);
189 Matcher matcher = pattern.matcher(value);
190 if (!matcher.matches()) return value;
191 String sign = matcher.group(1);
192 String rest = matcher.group(2);
193 sign = "-".equals(sign) ? "" : "-";
194 return sign + rest;
195 }
196
197 @Override
198 public Collection<Command> execute(Way oldway, Way way) throws UserCancelException {
199 Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap = new HashMap<>();
200
201 List<TagCorrection> tagCorrections = new ArrayList<>();
202 for (String key : way.keySet()) {
203 String value = way.get(key);
204 Tag newTag = TagSwitcher.apply(key, value);
205 String newKey = newTag.getKey();
206 String newValue = newTag.getValue();
207
208 boolean needsCorrection = !key.equals(newKey);
209 if (way.get(newKey) != null && way.get(newKey).equals(newValue)) {
210 needsCorrection = false;
211 }
212 if (!value.equals(newValue)) {
213 needsCorrection = true;
214 }
215
216 if (needsCorrection) {
217 tagCorrections.add(new TagCorrection(key, value, newKey, newValue));
218 }
219 }
220 if (!tagCorrections.isEmpty()) {
221 tagCorrectionsMap.put(way, tagCorrections);
222 }
223
224 Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap = new HashMap<>();
225 List<RoleCorrection> roleCorrections = new ArrayList<>();
226
227 Collection<OsmPrimitive> referrers = oldway.getReferrers();
228 for (OsmPrimitive referrer: referrers) {
229 if (! (referrer instanceof Relation)) {
230 continue;
231 }
232 Relation relation = (Relation)referrer;
233 int position = 0;
234 for (RelationMember member : relation.getMembers()) {
235 if (!member.getMember().hasEqualSemanticAttributes(oldway)
236 || !member.hasRole()) {
237 position++;
238 continue;
239 }
240
241 boolean found = false;
242 String newRole = null;
243 for (StringSwitcher prefixSuffixSwitcher : stringSwitchers) {
244 newRole = prefixSuffixSwitcher.apply(member.getRole());
245 if (!newRole.equals(member.getRole())) {
246 found = true;
247 break;
248 }
249 }
250
251 if (found) {
252 roleCorrections.add(new RoleCorrection(relation, position, member, newRole));
253 }
254
255 position++;
256 }
257 }
258 if (!roleCorrections.isEmpty()) {
259 roleCorrectionMap.put(way, roleCorrections);
260 }
261
262 return applyCorrections(tagCorrectionsMap, roleCorrectionMap,
263 tr("When reversing this way, the following changes are suggested in order to maintain data consistency."));
264 }
265
266 private static boolean ignoreKeyForCorrection(String key) {
267 for (Pattern ignoredKey : ignoredKeys) {
268 if (ignoredKey.matcher(key).matches()) {
269 return true;
270 }
271 }
272 return false;
273 }
274}
Note: See TracBrowser for help on using the repository browser.