001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.corrector; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005import static org.openstreetmap.josm.tools.I18n.trn; 006 007import java.util.Arrays; 008import java.util.Map; 009 010import javax.swing.JOptionPane; 011 012import org.openstreetmap.josm.Main; 013import org.openstreetmap.josm.data.osm.OsmPrimitive; 014import org.openstreetmap.josm.data.osm.Tag; 015import org.openstreetmap.josm.data.osm.TagCollection; 016import org.openstreetmap.josm.data.osm.Tagged; 017import org.openstreetmap.josm.data.osm.Way; 018import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 019import org.openstreetmap.josm.gui.DefaultNameFormatter; 020import org.openstreetmap.josm.tools.UserCancelException; 021import org.openstreetmap.josm.tools.Utils; 022 023/** 024 * A ReverseWayNoTagCorrector warns about ways that should not be reversed 025 * because their semantic meaning cannot be preserved in that case. 026 * E.g. natural=coastline, natural=cliff, barrier=retaining_wall cannot be changed. 027 * @see ReverseWayTagCorrector for handling of tags that can be modified (oneway=yes, etc.) 028 * @since 5724 029 */ 030public final class ReverseWayNoTagCorrector { 031 032 private ReverseWayNoTagCorrector() { 033 // Hide default constructor for utils classes 034 } 035 036 /** 037 * Tags that imply a semantic meaning from the way direction and cannot be changed. 038 */ 039 private static final TagCollection directionalTags = new TagCollection(Arrays.asList(new Tag[]{ 040 new Tag("natural", "coastline"), 041 new Tag("natural", "cliff"), 042 new Tag("barrier", "guard_rail"), 043 new Tag("barrier", "kerb"), 044 new Tag("barrier", "retaining_wall"), 045 new Tag("man_made", "embankment"), 046 })); 047 048 /** 049 * Replies the tags that imply a semantic meaning from <code>way</code> direction and cannot be changed. 050 * @param way The way to look for 051 * @return tags that imply a semantic meaning from <code>way</code> direction and cannot be changed 052 */ 053 public static TagCollection getDirectionalTags(Tagged way) { 054 final TagCollection collection = new TagCollection(); 055 for (Map.Entry<String, String> entry : way.getKeys().entrySet()) { 056 final Tag tag = new Tag(entry.getKey(), entry.getValue()); 057 final boolean isDirectional = directionalTags.contains(tag) || OsmPrimitive.directionalKeyPredicate.evaluate(tag); 058 if (isDirectional) { 059 final boolean cannotBeCorrected = ReverseWayTagCorrector.getTagCorrections(tag).isEmpty(); 060 if (cannotBeCorrected) { 061 collection.add(tag); 062 } 063 } 064 } 065 return collection; 066 } 067 068 /** 069 * Tests whether way can be reversed without semantic change. 070 * Looks for tags like natural=cliff, barrier=retaining_wall. 071 * @param way The way to check 072 * @return false if the semantic meaning change if the way is reversed, true otherwise. 073 */ 074 public static boolean isReversible(Tagged way) { 075 return getDirectionalTags(way).isEmpty(); 076 } 077 078 protected static boolean confirmReverseWay(Way way, TagCollection tags) { 079 String msg = trn( 080 // Singular, if a single tag is impacted 081 "<html>You are going to reverse the way ''{0}''," 082 + "<br/> whose semantic meaning of its tag ''{1}'' is defined by its direction.<br/>" 083 + "Do you really want to change the way direction, thus its semantic meaning?</html>", 084 // Plural, if several tags are impacted 085 "<html>You are going to reverse the way ''{0}''," 086 + "<br/> whose semantic meaning of these tags are defined by its direction:<br/>{1}" 087 + "Do you really want to change the way direction, thus its semantic meaning?</html>", 088 tags.size(), 089 way.getDisplayName(DefaultNameFormatter.getInstance()), 090 Utils.joinAsHtmlUnorderedList(tags) 091 ); 092 int ret = ConditionalOptionPaneUtil.showOptionDialog( 093 "reverse_directional_way", 094 Main.parent, 095 msg, 096 tr("Reverse directional way."), 097 JOptionPane.YES_NO_CANCEL_OPTION, 098 JOptionPane.WARNING_MESSAGE, 099 null, 100 null 101 ); 102 switch(ret) { 103 case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION: 104 case JOptionPane.YES_OPTION: 105 return true; 106 default: 107 return false; 108 } 109 } 110 111 /** 112 * Checks the given way can be safely reversed and asks user to confirm the operation if it not the case. 113 * @param way The way to check 114 * @throws UserCancelException If the user cancels the operation 115 */ 116 public static void checkAndConfirmReverseWay(Way way) throws UserCancelException { 117 TagCollection tags = getDirectionalTags(way); 118 if (!tags.isEmpty() && !confirmReverseWay(way, tags)) { 119 throw new UserCancelException(); 120 } 121 } 122}