// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.corrector; import static org.openstreetmap.josm.tools.I18n.tr; import static org.openstreetmap.josm.tools.I18n.trn; import java.util.Arrays; import java.util.Map; import javax.swing.JOptionPane; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.osm.Tag; import org.openstreetmap.josm.data.osm.TagCollection; import org.openstreetmap.josm.data.osm.Tagged; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; import org.openstreetmap.josm.gui.DefaultNameFormatter; import org.openstreetmap.josm.tools.UserCancelException; import org.openstreetmap.josm.tools.Utils; /** * A ReverseWayNoTagCorrector warns about ways that should not be reversed * because their semantic meaning cannot be preserved in that case. * E.g. natural=coastline, natural=cliff, barrier=retaining_wall cannot be changed. * @see ReverseWayTagCorrector for handling of tags that can be modified (oneway=yes, etc.) * @since 5724 */ public final class ReverseWayNoTagCorrector { private ReverseWayNoTagCorrector() { // Hide default constructor for utils classes } /** * Tags that imply a semantic meaning from the way direction and cannot be changed. */ private static final TagCollection directionalTags = new TagCollection(Arrays.asList(new Tag[]{ new Tag("natural", "coastline"), new Tag("natural", "cliff"), new Tag("barrier", "guard_rail"), new Tag("barrier", "kerb"), new Tag("barrier", "retaining_wall"), new Tag("man_made", "embankment"), })); /** * Replies the tags that imply a semantic meaning from way direction and cannot be changed. * @param way The way to look for * @return tags that imply a semantic meaning from way direction and cannot be changed */ public static TagCollection getDirectionalTags(Tagged way) { final TagCollection collection = new TagCollection(); for (Map.Entry entry : way.getKeys().entrySet()) { final Tag tag = new Tag(entry.getKey(), entry.getValue()); final boolean isDirectional = directionalTags.contains(tag) || tag.isDirectionKey(); if (isDirectional) { final boolean cannotBeCorrected = ReverseWayTagCorrector.getTagCorrections(tag).isEmpty(); if (cannotBeCorrected) { collection.add(tag); } } } return collection; } /** * Tests whether way can be reversed without semantic change. * Looks for tags like natural=cliff, barrier=retaining_wall. * @param way The way to check * @return false if the semantic meaning change if the way is reversed, true otherwise. */ public static boolean isReversible(Tagged way) { return getDirectionalTags(way).isEmpty(); } private static boolean confirmReverseWay(Way way, TagCollection tags) { String msg = trn( // Singular, if a single tag is impacted "You are going to reverse the way ''{0}''," + "
whose semantic meaning of its tag ''{1}'' is defined by its direction.
" + "Do you really want to change the way direction, thus its semantic meaning?", // Plural, if several tags are impacted "You are going to reverse the way ''{0}''," + "
whose semantic meaning of these tags are defined by its direction:
{1}" + "Do you really want to change the way direction, thus its semantic meaning?", tags.size(), way.getDisplayName(DefaultNameFormatter.getInstance()), Utils.joinAsHtmlUnorderedList(tags) ); int ret = ConditionalOptionPaneUtil.showOptionDialog( "reverse_directional_way", Main.parent, msg, tr("Reverse directional way."), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, null ); switch(ret) { case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION: case JOptionPane.YES_OPTION: return true; default: return false; } } /** * Checks the given way can be safely reversed and asks user to confirm the operation if it not the case. * @param way The way to check * @throws UserCancelException If the user cancels the operation */ public static void checkAndConfirmReverseWay(Way way) throws UserCancelException { TagCollection tags = getDirectionalTags(way); if (!tags.isEmpty() && !confirmReverseWay(way, tags)) { throw new UserCancelException(); } } }