// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.corrector;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openstreetmap.josm.command.Command;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.OsmUtils;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.RelationMember;
import org.openstreetmap.josm.data.osm.Way;
public class ReverseWayTagCorrector extends TagCorrector<Way> {
private static class PrefixSuffixSwitcher {
private static final String SEPARATOR = "[:_]?";
private final String a;
private final String b;
private final Pattern startPattern;
private final Pattern endPattern;
public PrefixSuffixSwitcher(String a, String b) {
this.a = a;
this.b = b;
startPattern = Pattern.compile(
"^(" + a + "|" + b + ")(" + SEPARATOR + "|$)",
Pattern.CASE_INSENSITIVE);
endPattern = Pattern.compile("^.*" +
SEPARATOR + "(" + a + "|" + b + ")$",
Pattern.CASE_INSENSITIVE);
}
public String apply(String text) {
Matcher m = startPattern.matcher(text);
if (!m.lookingAt()) {
m = endPattern.matcher(text);
}
if (m.lookingAt()) {
String leftRight = m.group(1).toLowerCase();
StringBuilder result = new StringBuilder();
result.append(text.substring(0, m.start(1)));
result.append(leftRight.equals(a) ? b : a);
result.append(text.substring(m.end(1)));
return result.toString();
}
return text;
}
}
private static PrefixSuffixSwitcher[] prefixSuffixSwitchers =
new PrefixSuffixSwitcher[] {
new PrefixSuffixSwitcher("left", "right"),
new PrefixSuffixSwitcher("forward", "backward"),
new PrefixSuffixSwitcher("forwards", "backwards"),
new PrefixSuffixSwitcher("up", "down"),
};
private static ArrayList<String> reversibleTags = new ArrayList<String>(
Arrays.asList(new String[] {"oneway", "incline", "direction"}));
public static boolean isReversible(Way way) {
for (String key : way.keySet()) {
if (reversibleTags.contains(key)) return false;
for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) {
if (!key.equals(prefixSuffixSwitcher.apply(key))) return false;
}
}
return true;
}
public static List<Way> irreversibleWays(List<Way> ways) {
List<Way> newWays = new ArrayList<Way>(ways);
for (Way way : ways) {
if (isReversible(way)) {
newWays.remove(way);
}
}
return newWays;
}
public String invertNumber(String value) {
Pattern pattern = Pattern.compile("^([+-]?)(\\d.*)$", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(value);
if (!matcher.matches()) return value;
String sign = matcher.group(1);
String rest = matcher.group(2);
sign = sign.equals("-") ? "" : "-";
return sign + rest;
}
@Override
public Collection<Command> execute(Way oldway, Way way) throws UserCancelException {
Map<OsmPrimitive, List<TagCorrection>> tagCorrectionsMap =
new HashMap<OsmPrimitive, List<TagCorrection>>();
ArrayList<TagCorrection> tagCorrections = new ArrayList<TagCorrection>();
for (String key : way.keySet()) {
String newKey = key;
String value = way.get(key);
String newValue = value;
if (key.equals("oneway")) {
if (OsmUtils.isReversed(value)) {
newValue = OsmUtils.trueval;
} else if (OsmUtils.isTrue(value)) {
newValue = OsmUtils.reverseval;
}
} else if (key.equals("incline") || key.equals("direction")) {
PrefixSuffixSwitcher switcher = new PrefixSuffixSwitcher("up", "down");
newValue = switcher.apply(value);
if (newValue.equals(value)) {
newValue = invertNumber(value);
}
} else {
for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) {
newKey = prefixSuffixSwitcher.apply(key);
if (!key.equals(newKey)) {
break;
}
newValue = prefixSuffixSwitcher.apply(value);
if (!value.equals(newValue)) {
break;
}
}
}
if (!key.equals(newKey) || !value.equals(newValue)) {
tagCorrections.add(new TagCorrection(key, value, newKey, newValue));
}
}
if (!tagCorrections.isEmpty()) {
tagCorrectionsMap.put(way, tagCorrections);
}
Map<OsmPrimitive, List<RoleCorrection>> roleCorrectionMap =
new HashMap<OsmPrimitive, List<RoleCorrection>>();
ArrayList<RoleCorrection> roleCorrections = new ArrayList<RoleCorrection>();
Collection<OsmPrimitive> referrers = oldway.getReferrers();
for (OsmPrimitive referrer: referrers) {
if (! (referrer instanceof Relation)) {
continue;
}
Relation relation = (Relation)referrer;
int position = 0;
for (RelationMember member : relation.getMembers()) {
if (!member.getMember().hasEqualSemanticAttributes(oldway)
|| !member.hasRole()) {
position++;
continue;
}
boolean found = false;
String newRole = null;
for (PrefixSuffixSwitcher prefixSuffixSwitcher : prefixSuffixSwitchers) {
newRole = prefixSuffixSwitcher.apply(member.getRole());
if (!newRole.equals(member.getRole())) {
found = true;
break;
}
}
if (found) {
roleCorrections.add(new RoleCorrection(relation, position, member, newRole));
}
position++;
}
}
if (!roleCorrections.isEmpty()) {
roleCorrectionMap.put(way, roleCorrections);
}
return applyCorrections(tagCorrectionsMap, roleCorrectionMap,
tr("When reversing this way, the following changes to properties "
+ "of the way and its nodes are suggested in order "
+ "to maintain data consistency."));
}
}