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