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