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

Last change on this file since 8394 was 8379, checked in by Don-vip, 9 years ago

Consecutively calls to StringBuffer/StringBuilder .append should reuse the target object

  • Property svn:eol-style set to native
File size: 9.8 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 .append(leftRight.equals(a) ? b : a)
82 .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 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 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 for (StringSwitcher prefixSuffixSwitcher : stringSwitchers) {
126 newKey = prefixSuffixSwitcher.apply(key);
127 if (!key.equals(newKey)) {
128 break;
129 }
130 }
131 } else if (key.startsWith("incline") || key.endsWith("incline")
132 || key.startsWith("direction") || key.endsWith("direction")) {
133 newValue = UP_DOWN.apply(value);
134 if (newValue.equals(value)) {
135 newValue = invertNumber(value);
136 }
137 } else if (key.endsWith(":forward") || key.endsWith(":backward")) {
138 // Change key but not left/right value (fix #8518)
139 newKey = FORWARD_BACKWARD.apply(key);
140 } else if (!ignoreKeyForCorrection(key)) {
141 for (StringSwitcher prefixSuffixSwitcher : stringSwitchers) {
142 newKey = prefixSuffixSwitcher.apply(key);
143 if (!key.equals(newKey)) {
144 break;
145 }
146 newValue = prefixSuffixSwitcher.apply(value);
147 if (!value.equals(newValue)) {
148 break;
149 }
150 }
151 }
152 return new Tag(newKey, newValue);
153 }
154 }
155
156 private static final StringSwitcher FORWARD_BACKWARD = new StringSwitcher("forward", "backward");
157 private static final StringSwitcher UP_DOWN = new StringSwitcher("up", "down");
158
159 private static final StringSwitcher[] stringSwitchers = new StringSwitcher[] {
160 new StringSwitcher("left", "right"),
161 new StringSwitcher("forwards", "backwards"),
162 new StringSwitcher("east", "west"),
163 new StringSwitcher("north", "south"),
164 FORWARD_BACKWARD, UP_DOWN
165 };
166
167 /**
168 * Tests whether way can be reversed without semantic change, i.e., whether tags have to be changed.
169 * Looks for keys like oneway, oneway:bicycle, cycleway:right:oneway, left/right.
170 * @param way
171 * @return false if tags should be changed to keep semantic, true otherwise.
172 */
173 public static boolean isReversible(Way way) {
174 for (Tag tag : TagCollection.from(way)) {
175 if (!tag.equals(TagSwitcher.apply(tag))) {
176 return false;
177 }
178 }
179 return true;
180 }
181
182 public static List<Way> irreversibleWays(List<Way> ways) {
183 List<Way> newWays = new ArrayList<>(ways);
184 for (Way way : ways) {
185 if (isReversible(way)) {
186 newWays.remove(way);
187 }
188 }
189 return newWays;
190 }
191
192 public static String invertNumber(String value) {
193 Pattern pattern = Pattern.compile("^([+-]?)(\\d.*)$", Pattern.CASE_INSENSITIVE);
194 Matcher matcher = pattern.matcher(value);
195 if (!matcher.matches()) return value;
196 String sign = matcher.group(1);
197 String rest = matcher.group(2);
198 sign = "-".equals(sign) ? "" : "-";
199 return sign + rest;
200 }
201
202 @Override
203 public Collection<Command> execute(Way oldway, Way way) throws UserCancelException {
204 Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap = new HashMap<>();
205
206 List<TagCorrection> tagCorrections = new ArrayList<>();
207 for (String key : way.keySet()) {
208 String value = way.get(key);
209 Tag newTag = TagSwitcher.apply(key, value);
210 String newKey = newTag.getKey();
211 String newValue = newTag.getValue();
212
213 boolean needsCorrection = !key.equals(newKey);
214 if (way.get(newKey) != null && way.get(newKey).equals(newValue)) {
215 needsCorrection = false;
216 }
217 if (!value.equals(newValue)) {
218 needsCorrection = true;
219 }
220
221 if (needsCorrection) {
222 tagCorrections.add(new TagCorrection(key, value, newKey, newValue));
223 }
224 }
225 if (!tagCorrections.isEmpty()) {
226 tagCorrectionsMap.put(way, tagCorrections);
227 }
228
229 Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap = new HashMap<>();
230 List<RoleCorrection> roleCorrections = new ArrayList<>();
231
232 Collection<OsmPrimitive> referrers = oldway.getReferrers();
233 for (OsmPrimitive referrer: referrers) {
234 if (! (referrer instanceof Relation)) {
235 continue;
236 }
237 Relation relation = (Relation)referrer;
238 int position = 0;
239 for (RelationMember member : relation.getMembers()) {
240 if (!member.getMember().hasEqualSemanticAttributes(oldway)
241 || !member.hasRole()) {
242 position++;
243 continue;
244 }
245
246 boolean found = false;
247 String newRole = null;
248 for (StringSwitcher prefixSuffixSwitcher : stringSwitchers) {
249 newRole = prefixSuffixSwitcher.apply(member.getRole());
250 if (!newRole.equals(member.getRole())) {
251 found = true;
252 break;
253 }
254 }
255
256 if (found) {
257 roleCorrections.add(new RoleCorrection(relation, position, member, newRole));
258 }
259
260 position++;
261 }
262 }
263 if (!roleCorrections.isEmpty()) {
264 roleCorrectionMap.put(way, roleCorrections);
265 }
266
267 return applyCorrections(tagCorrectionsMap, roleCorrectionMap,
268 tr("When reversing this way, the following changes are suggested in order to maintain data consistency."));
269 }
270
271 private static boolean ignoreKeyForCorrection(String key) {
272 for (Pattern ignoredKey : ignoredKeys) {
273 if (ignoredKey.matcher(key).matches()) {
274 return true;
275 }
276 }
277 return false;
278 }
279}
Note: See TracBrowser for help on using the repository browser.