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