// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.data.osm; import java.util.Collection; import java.util.Locale; import java.util.Map; import java.util.regex.Pattern; import java.util.stream.Stream; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.tools.CheckParameterUtil; import org.openstreetmap.josm.tools.TextTagParser; /** * Utility methods/constants that are useful for generic OSM tag handling. */ public final class OsmUtils { /** * A value that should be used to indicate true * @since 12186 */ public static final String TRUE_VALUE = "yes"; /** * A value that should be used to indicate false * @since 12186 */ public static final String FALSE_VALUE = "no"; /** * A value that should be used to indicate that a property applies reversed on the way * @since 12186 */ public static final String REVERSE_VALUE = "-1"; /** * Discouraged synonym for {@link #TRUE_VALUE} */ public static final String trueval = TRUE_VALUE; /** * Discouraged synonym for {@link #FALSE_VALUE} */ public static final String falseval = FALSE_VALUE; /** * Discouraged synonym for {@link #REVERSE_VALUE} */ public static final String reverseval = REVERSE_VALUE; private OsmUtils() { // Hide default constructor for utils classes } /** * Converts a string to a boolean value * @param value The string to convert * @return {@link Boolean#TRUE} if that string represents a true value, * {@link Boolean#FALSE} if it represents a false value, * null otherwise. */ public static Boolean getOsmBoolean(String value) { if (value == null) return null; String lowerValue = value.toLowerCase(Locale.ENGLISH); if (isTrue(lowerValue)) return Boolean.TRUE; if (isFalse(lowerValue)) return Boolean.FALSE; return null; } /** * Normalizes the OSM boolean value * @param value The tag value * @return The best true/false value or the old value if the input cannot be converted. * @see #TRUE_VALUE * @see #FALSE_VALUE */ public static String getNamedOsmBoolean(String value) { Boolean res = getOsmBoolean(value); return res == null ? value : (res ? trueval : falseval); } /** * Check if the value is a value indicating that a property applies reversed. * @param value The value to check * @return true if it is reversed. */ public static boolean isReversed(String value) { if (value == null) { return false; } switch (value) { case "reverse": case "-1": return true; default: return false; } } /** * Check if a tag value represents a boolean true value * @param value The value to check * @return true if it is a true value. */ public static boolean isTrue(String value) { if (value == null) { return false; } switch (value) { case "true": case "yes": case "1": case "on": return true; default: return false; } } /** * Check if a tag value represents a boolean false value * @param value The value to check * @return true if it is a true value. */ public static boolean isFalse(String value) { if (value == null) { return false; } switch (value) { case "false": case "no": case "0": case "off": return true; default: return false; } } /** * Creates a new OSM primitive around (0,0) according to the given assertion. Originally written for unit tests, * this can also be used in another places like validation of local MapCSS validator rules. * Ways and relations created using this method are empty. * @param assertion The assertion describing OSM primitive (ex: "way name=Foo railway=rail") * @return a new OSM primitive according to the given assertion * @throws IllegalArgumentException if assertion is null or if the primitive type cannot be deduced from it * @since 7356 */ public static OsmPrimitive createPrimitive(String assertion) { return createPrimitive(assertion, LatLon.ZERO, false); } /** * Creates a new OSM primitive according to the given assertion. Originally written for unit tests, * this can also be used in another places like validation of local MapCSS validator rules. * @param assertion The assertion describing OSM primitive (ex: "way name=Foo railway=rail") * @param around the coordinate at which the primitive will be located * @param enforceLocation if {@code true}, ways and relations will not be empty to force a physical location * @return a new OSM primitive according to the given assertion * @throws IllegalArgumentException if assertion is null or if the primitive type cannot be deduced from it * @since 14486 */ public static OsmPrimitive createPrimitive(String assertion, LatLon around, boolean enforceLocation) { CheckParameterUtil.ensureParameterNotNull(assertion, "assertion"); final String[] x = assertion.split("\\s+", 2); final OsmPrimitive p = "n".equals(x[0]) || "node".equals(x[0]) ? newNode(around) : "w".equals(x[0]) || "way".equals(x[0]) || /*for MapCSS related usage*/ "area".equals(x[0]) ? newWay(around, enforceLocation) : "r".equals(x[0]) || "relation".equals(x[0]) ? newRelation(around, enforceLocation) : null; if (p == null) { throw new IllegalArgumentException("Expecting n/node/w/way/r/relation/area, but got '" + x[0] + '\''); } if (x.length > 1) { for (final Map.Entry i : TextTagParser.readTagsFromText(x[1]).entrySet()) { p.put(i.getKey(), i.getValue()); } } return p; } private static Node newNode(LatLon around) { return new Node(around); } private static Way newWay(LatLon around, boolean enforceLocation) { Way w = new Way(); if (enforceLocation) { w.addNode(newNode(new LatLon(around.lat()+0.1, around.lon()))); w.addNode(newNode(new LatLon(around.lat()-0.1, around.lon()))); } return w; } private static Relation newRelation(LatLon around, boolean enforceLocation) { Relation r = new Relation(); if (enforceLocation) { r.addMember(new RelationMember(null, newNode(around))); } return r; } /** * Returns the layer value of primitive (null for layer 0). * @param w OSM primitive * @return the value of "layer" key, or null if absent or set to 0 (default value) * @since 12986 * @since 13637 (signature) */ public static String getLayer(IPrimitive w) { String layer1 = w.get("layer"); if ("0".equals(layer1)) { layer1 = null; // 0 is default value for layer. } return layer1; } /** * Determines if the given collection contains primitives, and that none of them belong to a locked layer. * @param collection collection of OSM primitives * @return {@code true} if the given collection is not empty and does not contain any primitive in a locked layer. * @since 13611 * @since 13957 (signature) */ public static boolean isOsmCollectionEditable(Collection collection) { if (collection == null || collection.isEmpty()) { return false; } // see #16510: optimization: only consider the first primitive, as collection always refer to the same dataset OsmData ds = collection.iterator().next().getDataSet(); return ds == null || !ds.isLocked(); } /** * Splits a tag value by semi-colon value separator. * Spaces around the ; are ignored. * * @param value the value to separate * @return the separated values as Stream * @since 15671 */ public static Stream splitMultipleValues(String value) { return Pattern.compile("\\s*;\\s*").splitAsStream(value); } }