Changeset 10674 in josm for trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java
- Timestamp:
- 2016-07-29T22:15:28+02:00 (8 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java
r10659 r10674 6 6 import java.text.MessageFormat; 7 7 import java.util.Arrays; 8 import java.util.Collection;9 8 import java.util.EnumSet; 10 9 import java.util.Map; 11 10 import java.util.Objects; 12 11 import java.util.Set; 12 import java.util.function.BiFunction; 13 import java.util.function.IntFunction; 13 14 import java.util.function.Predicate; 14 15 import java.util.regex.Pattern; … … 28 29 import org.openstreetmap.josm.tools.CheckParameterUtil; 29 30 import org.openstreetmap.josm.tools.Predicates; 30 import org.openstreetmap.josm.tools.SubclassFilteredCollection;31 31 import org.openstreetmap.josm.tools.Utils; 32 32 33 /** 34 * This is a condition that needs to be fulfilled in order to apply a MapCSS style. 35 */ 33 36 @FunctionalInterface 34 37 public interface Condition { 35 38 39 /** 40 * Checks if the condition applies in the given MapCSS {@link Environment}. 41 * @param e The environment to check. May not be <code>null</code>. 42 * @return <code>true</code> if the condition applies. 43 */ 36 44 boolean applies(Environment e); 37 45 46 /** 47 * Create a new condition that checks the key and the value of the object. 48 * @param k The key. 49 * @param v The reference value 50 * @param op The operation to use when comparing the value 51 * @param context The type of context to use. 52 * @param considerValAsKey whether to consider {@code v} as another key and compare the values of key {@code k} and key {@code v}. 53 * @return The new condition. 54 */ 38 55 static Condition createKeyValueCondition(String k, String v, Op op, Context context, boolean considerValAsKey) { 39 56 switch (context) { … … 59 76 } 60 77 78 /** 79 * Create a condition in which the key and the value need to match a given regexp 80 * @param k The key regexp 81 * @param v The value regexp 82 * @param op The operation to use when comparing the key and the value. 83 * @return The new condition. 84 */ 61 85 static Condition createRegexpKeyRegexpValueCondition(String k, String v, Op op) { 62 86 return new RegexpKeyValueRegexpCondition(k, v, op); 63 87 } 64 88 89 /** 90 * Creates a condition that checks the given key. 91 * @param k The key to test for 92 * @param not <code>true</code> to invert the match 93 * @param matchType The match type to check for. 94 * @param context The context this rule is found in. 95 * @return the new condition. 96 */ 65 97 static Condition createKeyCondition(String k, boolean not, KeyMatchType matchType, Context context) { 66 98 switch (context) { … … 79 111 } 80 112 113 /** 114 * Create a new pseudo class condition 115 * @param id The id of the pseudo class 116 * @param not <code>true</code> to invert the condition 117 * @param context The context the class is found in. 118 * @return The new condition 119 */ 81 120 static PseudoClassCondition createPseudoClassCondition(String id, boolean not, Context context) { 82 121 return PseudoClassCondition.createPseudoClassCondition(id, not, context); 83 122 } 84 123 124 /** 125 * Create a new class condition 126 * @param id The id of the class to match 127 * @param not <code>true</code> to invert the condition 128 * @param context Ignored 129 * @return The new condition 130 */ 85 131 static ClassCondition createClassCondition(String id, boolean not, Context context) { 86 132 return new ClassCondition(id, not); 87 133 } 88 134 135 /** 136 * Create a new condition that a expression needs to be fulfilled 137 * @param e the expression to check 138 * @param context Ignored 139 * @return The new condition 140 */ 89 141 static ExpressionCondition createExpressionCondition(Expression e, Context context) { 90 142 return new ExpressionCondition(e); … … 96 148 enum Op { 97 149 /** The value equals the given reference. */ 98 EQ ,150 EQ(Objects::equals), 99 151 /** The value does not equal the reference. */ 100 NEQ ,152 NEQ(EQ), 101 153 /** The value is greater than or equal to the given reference value (as float). */ 102 GREATER_OR_EQUAL ,154 GREATER_OR_EQUAL(comparisonResult -> comparisonResult >= 0), 103 155 /** The value is greater than the given reference value (as float). */ 104 GREATER ,156 GREATER(comparisonResult -> comparisonResult > 0), 105 157 /** The value is less than or equal to the given reference value (as float). */ 106 LESS_OR_EQUAL ,158 LESS_OR_EQUAL(comparisonResult -> comparisonResult <= 0), 107 159 /** The value is less than the given reference value (as float). */ 108 LESS ,160 LESS(comparisonResult -> comparisonResult < 0), 109 161 /** The reference is treated as regular expression and the value needs to match it. */ 110 REGEX ,162 REGEX((test, prototype) -> Pattern.compile(prototype).matcher(test).find()), 111 163 /** The reference is treated as regular expression and the value needs to not match it. */ 112 NREGEX ,164 NREGEX(REGEX), 113 165 /** The reference is treated as a list separated by ';'. Spaces around the ; are ignored. 114 166 * The value needs to be equal one of the list elements. */ 115 ONE_OF ,167 ONE_OF((test, prototype) -> Arrays.asList(test.split("\\s*;\\s*")).contains(prototype)), 116 168 /** The value needs to begin with the reference string. */ 117 BEGINS_WITH ,169 BEGINS_WITH((test, prototype) -> test.startsWith(prototype)), 118 170 /** The value needs to end with the reference string. */ 119 ENDS_WITH ,171 ENDS_WITH((test, prototype) -> test.endsWith(prototype)), 120 172 /** The value needs to contain the reference string. */ 121 CONTAINS ;173 CONTAINS((test, prototype) -> test.contains(prototype)); 122 174 123 175 static final Set<Op> NEGATED_OPS = EnumSet.of(NEQ, NREGEX); 176 177 private final BiFunction<String, String, Boolean> function; 178 179 private final boolean negated; 180 181 /** 182 * Create a new string operation. 183 * @param func The function to apply during {@link #eval(String, String)}. 184 */ 185 Op(BiFunction<String, String, Boolean> func) { 186 this.function = func; 187 negated = false; 188 } 189 190 /** 191 * Create a new float operation that compares two float values 192 * @param comparatorResult A function to mapt the result of the comparison 193 */ 194 Op(IntFunction<Boolean> comparatorResult) { 195 this.function = (test, prototype) -> { 196 float testFloat; 197 try { 198 testFloat = Float.parseFloat(test); 199 } catch (NumberFormatException e) { 200 return false; 201 } 202 float prototypeFloat = Float.parseFloat(prototype); 203 204 int res = Float.compare(testFloat, prototypeFloat); 205 return comparatorResult.apply(res); 206 }; 207 negated = false; 208 } 209 210 /** 211 * Create a new Op by negating an other op. 212 * @param negate inverse operation 213 */ 214 Op(Op negate) { 215 this.function = (a, b) -> !negate.function.apply(a, b); 216 negated = true; 217 } 124 218 125 219 /** … … 130 224 */ 131 225 public boolean eval(String testString, String prototypeString) { 132 if (testString == null && !NEGATED_OPS.contains(this)) 133 return false; 134 switch (this) { 135 case EQ: 136 return Objects.equals(testString, prototypeString); 137 case NEQ: 138 return !Objects.equals(testString, prototypeString); 139 case REGEX: 140 case NREGEX: 141 final boolean contains = Pattern.compile(prototypeString).matcher(testString).find(); 142 return REGEX.equals(this) ? contains : !contains; 143 case ONE_OF: 144 return testString != null && Arrays.asList(testString.split("\\s*;\\s*")).contains(prototypeString); 145 case BEGINS_WITH: 146 return testString != null && testString.startsWith(prototypeString); 147 case ENDS_WITH: 148 return testString != null && testString.endsWith(prototypeString); 149 case CONTAINS: 150 return testString != null && testString.contains(prototypeString); 151 case GREATER_OR_EQUAL: 152 case GREATER: 153 case LESS_OR_EQUAL: 154 case LESS: 155 // See below 156 break; 157 default: 158 throw new AssertionError(); 159 } 160 161 float testFloat; 162 try { 163 testFloat = Float.parseFloat(testString); 164 } catch (NumberFormatException e) { 165 return false; 166 } 167 float prototypeFloat = Float.parseFloat(prototypeString); 168 169 switch (this) { 170 case GREATER_OR_EQUAL: 171 return testFloat >= prototypeFloat; 172 case GREATER: 173 return testFloat > prototypeFloat; 174 case LESS_OR_EQUAL: 175 return testFloat <= prototypeFloat; 176 case LESS: 177 return testFloat < prototypeFloat; 178 default: 179 throw new AssertionError(); 180 } 226 if (testString == null) 227 return negated; 228 else 229 return function.apply(testString, prototypeString); 181 230 } 182 231 } … … 202 251 * Extra class for performance reasons. 203 252 */ 204 class SimpleKeyValueCondition implements Condition {253 class SimpleKeyValueCondition implements Condition, ToTagConvertable { 205 254 /** 206 255 * The key to search for. … … 227 276 } 228 277 229 public Tag asTag() { 278 @Override 279 public Tag asTag(OsmPrimitive primitive) { 230 280 return new Tag(k, v); 231 281 } … … 242 292 * 243 293 */ 244 class KeyValueCondition implements Condition {294 class KeyValueCondition implements Condition, ToTagConvertable { 245 295 /** 246 296 * The key to search for. … … 258 308 * If this flag is set, {@link #v} is treated as a key and the value is the value set for that key. 259 309 */ 260 public boolean considerValAsKey;310 public final boolean considerValAsKey; 261 311 262 312 /** … … 280 330 } 281 331 282 public Tag asTag() { 332 @Override 333 public Tag asTag(OsmPrimitive primitive) { 283 334 return new Tag(k, v); 284 335 } … … 290 341 } 291 342 343 /** 344 * This condition requires a fixed key to match a given regexp 345 */ 292 346 class KeyValueRegexpCondition extends KeyValueCondition { 293 294 public final Pattern pattern;295 347 protected static final Set<Op> SUPPORTED_OPS = EnumSet.of(Op.REGEX, Op.NREGEX); 348 349 final Pattern pattern; 296 350 297 351 public KeyValueRegexpCondition(String k, String v, Op op, boolean considerValAsKey) { … … 319 373 } 320 374 375 /** 376 * A condition that checks that a key with the matching pattern has a value with the matching pattern. 377 */ 321 378 class RegexpKeyValueRegexpCondition extends KeyValueRegexpCondition { 322 379 323 380 public final Pattern keyPattern; 324 381 382 /** 383 * Create a condition in which the key and the value need to match a given regexp 384 * @param k The key regexp 385 * @param v The value regexp 386 * @param op The operation to use when comparing the key and the value. 387 */ 325 388 public RegexpKeyValueRegexpCondition(String k, String v, Op op) { 326 389 super(k, v, op, false); … … 419 482 * </pre> 420 483 */ 421 class KeyCondition implements Condition {484 class KeyCondition implements Condition, ToTagConvertable { 422 485 423 486 /** … … 484 547 * @return The tag. 485 548 */ 549 @Override 486 550 public Tag asTag(OsmPrimitive p) { 487 551 String key = label; 488 552 if (KeyMatchType.REGEX.equals(matchType)) { 489 final Collection<String> matchingKeys = SubclassFilteredCollection.filter(p.keySet(), containsPattern); 490 if (!matchingKeys.isEmpty()) { 491 key = matchingKeys.iterator().next(); 492 } 553 key = p.keySet().stream().filter(containsPattern).findAny().orElse(key); 493 554 } 494 555 return new Tag(key, p.get(key)); … … 513 574 @Override 514 575 public boolean applies(Environment env) { 515 return env != null && env.getCascade(env.layer) != null && (not ^ env.getCascade(env.layer).containsKey(id)); 576 Cascade cascade = env.getCascade(env.layer); 577 return cascade != null && (not ^ cascade.containsKey(id)); 516 578 } 517 579 … … 703 765 } 704 766 767 /** 768 * Create a new pseudo class condition 769 * @param id The id of the pseudo class 770 * @param not <code>true</code> to invert the condition 771 * @param context The context the class is found in. 772 * @return The new condition 773 */ 705 774 public static PseudoClassCondition createPseudoClassCondition(String id, boolean not, Context context) { 706 775 CheckParameterUtil.ensureThat(!"sameTags".equals(id) || Context.LINK.equals(context), "sameTags only supported in LINK context"); … … 753 822 } 754 823 824 /** 825 * A condition that is fulfilled whenever the expression is evaluated to be true. 826 */ 755 827 class ExpressionCondition implements Condition { 756 828 … … 776 848 } 777 849 } 850 851 /** 852 * This is a condition that can be converted to a tag 853 * @author Michael Zangl 854 * @since 10674 855 */ 856 public interface ToTagConvertable { 857 /** 858 * Converts the current condition to a tag 859 * @param primitive A primitive to use as context. May be ignored. 860 * @return A tag with the key/value of this condition. 861 */ 862 Tag asTag(OsmPrimitive primitive); 863 } 778 864 }
Note:
See TracChangeset
for help on using the changeset viewer.