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