001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.osm;
003
004import java.util.Arrays;
005import java.util.HashSet;
006import java.util.Locale;
007import java.util.Map;
008import java.util.Set;
009
010import org.openstreetmap.josm.data.coor.LatLon;
011import org.openstreetmap.josm.tools.CheckParameterUtil;
012import org.openstreetmap.josm.tools.TextTagParser;
013
014/**
015 * Utility methods/constants that are useful for generic OSM tag handling.
016 */
017public final class OsmUtils {
018
019    private static final Set<String> TRUE_VALUES = new HashSet<>(Arrays
020            .asList("true", "yes", "1", "on"));
021    private static final Set<String> FALSE_VALUES = new HashSet<>(Arrays
022            .asList("false", "no", "0", "off"));
023    private static final Set<String> REVERSE_VALUES = new HashSet<>(Arrays
024            .asList("reverse", "-1"));
025
026    /**
027     * A value that should be used to indicate true
028     * @since 12186
029     */
030    public static final String TRUE_VALUE = "yes";
031    /**
032     * A value that should be used to indicate false
033     * @since 12186
034     */
035    public static final String FALSE_VALUE = "no";
036    /**
037     * A value that should be used to indicate that a property applies reversed on the way
038     * @since 12186
039     */
040    public static final String REVERSE_VALUE = "-1";
041
042    /**
043     * Discouraged synonym for {@link #TRUE_VALUE}
044     */
045    public static final String trueval = TRUE_VALUE;
046    /**
047     * Discouraged synonym for {@link #FALSE_VALUE}
048     */
049    public static final String falseval = FALSE_VALUE;
050    /**
051     * Discouraged synonym for {@link #REVERSE_VALUE}
052     */
053    public static final String reverseval = REVERSE_VALUE;
054
055    private OsmUtils() {
056        // Hide default constructor for utils classes
057    }
058
059    /**
060     * Converts a string to a boolean value
061     * @param value The string to convert
062     * @return {@link Boolean#TRUE} if that string represents a true value,
063     *         {@link Boolean#FALSE} if it represents a false value,
064     *         <code>null</code> otherwise.
065     */
066    public static Boolean getOsmBoolean(String value) {
067        if (value == null) return null;
068        String lowerValue = value.toLowerCase(Locale.ENGLISH);
069        if (TRUE_VALUES.contains(lowerValue)) return Boolean.TRUE;
070        if (FALSE_VALUES.contains(lowerValue)) return Boolean.FALSE;
071        return null;
072    }
073
074    /**
075     * Normalizes the OSM boolean value
076     * @param value The tag value
077     * @return The best true/false value or the old value if the input cannot be converted.
078     * @see #TRUE_VALUE
079     * @see #FALSE_VALUE
080     */
081    public static String getNamedOsmBoolean(String value) {
082        Boolean res = getOsmBoolean(value);
083        return res == null ? value : (res ? trueval : falseval);
084    }
085
086    /**
087     * Check if the value is a value indicating that a property applies reversed.
088     * @param value The value to check
089     * @return true if it is reversed.
090     */
091    public static boolean isReversed(String value) {
092        return REVERSE_VALUES.contains(value);
093    }
094
095    /**
096     * Check if a tag value represents a boolean true value
097     * @param value The value to check
098     * @return true if it is a true value.
099     */
100    public static boolean isTrue(String value) {
101        return TRUE_VALUES.contains(value);
102    }
103
104    /**
105     * Check if a tag value represents a boolean false value
106     * @param value The value to check
107     * @return true if it is a true value.
108     */
109    public static boolean isFalse(String value) {
110        return FALSE_VALUES.contains(value);
111    }
112
113    /**
114     * Creates a new OSM primitive according to the given assertion. Originally written for unit tests,
115     * this can also be used in another places like validation of local MapCSS validator rules.
116     * @param assertion The assertion describing OSM primitive (ex: "way name=Foo railway=rail")
117     * @return a new OSM primitive according to the given assertion
118     * @throws IllegalArgumentException if assertion is null or if the primitive type cannot be deduced from it
119     * @since 7356
120     */
121    public static OsmPrimitive createPrimitive(String assertion) {
122        CheckParameterUtil.ensureParameterNotNull(assertion, "assertion");
123        final String[] x = assertion.split("\\s+", 2);
124        final OsmPrimitive p = "n".equals(x[0]) || "node".equals(x[0])
125                ? new Node(LatLon.ZERO)
126                : "w".equals(x[0]) || "way".equals(x[0]) || /*for MapCSS related usage*/ "area".equals(x[0])
127                ? new Way()
128                : "r".equals(x[0]) || "relation".equals(x[0])
129                ? new Relation()
130                : null;
131        if (p == null) {
132            throw new IllegalArgumentException("Expecting n/node/w/way/r/relation/area, but got '" + x[0] + '\'');
133        }
134        if (x.length > 1) {
135            for (final Map.Entry<String, String> i : TextTagParser.readTagsFromText(x[1]).entrySet()) {
136                p.put(i.getKey(), i.getValue());
137            }
138        }
139        return p;
140    }
141
142    /**
143     * Returns the layer value of primitive (null for layer 0).
144     * @param w OSM primitive
145     * @return the value of "layer" key, or null if absent or set to 0 (default value)
146     * @since 12986
147     */
148    public static String getLayer(OsmPrimitive w) {
149        String layer1 = w.get("layer");
150        if ("0".equals(layer1)) {
151            layer1 = null; // 0 is default value for layer.
152        }
153        return layer1;
154    }
155}