- Timestamp:
- 2016-07-29T22:15:28+02:00 (8 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/osm/OsmUtils.java
r8846 r10674 75 75 throw new IllegalArgumentException("Expecting n/node/w/way/r/relation/area, but got '" + x[0] + '\''); 76 76 } 77 for (final Map.Entry<String, String> i : TextTagParser.readTagsFromText(x[1]).entrySet()) { 78 p.put(i.getKey(), i.getValue()); 77 if (x.length > 1) { 78 for (final Map.Entry<String, String> i : TextTagParser.readTagsFromText(x[1]).entrySet()) { 79 p.put(i.getKey(), i.getValue()); 80 } 79 81 } 80 82 return p; -
trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java
r10672 r10674 421 421 try { 422 422 final Condition c = matchingSelector.getConditions().get(index); 423 final Tag tag = c instanceof Condition.KeyCondition 424 ? ((Condition.KeyCondition) c).asTag(p) 425 : c instanceof Condition.SimpleKeyValueCondition 426 ? ((Condition.SimpleKeyValueCondition) c).asTag() 427 : c instanceof Condition.KeyValueCondition 428 ? ((Condition.KeyValueCondition) c).asTag() 423 final Tag tag = c instanceof Condition.ToTagConvertable 424 ? ((Condition.ToTagConvertable) c).asTag(p) 429 425 : null; 430 426 if (tag == null) { -
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 } -
trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java
r10670 r10674 465 465 @Override 466 466 public boolean matches(Environment env) { 467 CheckParameterUtil.ensureParameterNotNull(env, "env"); 467 468 if (conds == null) return true; 468 469 for (Condition c : conds) { -
trunk/test/performance/org/openstreetmap/josm/PerformanceTestUtils.java
r9793 r10674 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm; 3 4 import java.util.ArrayList; 5 import java.util.Collections; 3 6 4 7 import org.openstreetmap.josm.io.XmlWriter; … … 11 14 */ 12 15 public final class PerformanceTestUtils { 16 private static final int TIMES_WARMUP = 2; 17 private static final int TIMES_RUN = 8; 18 19 /** 20 * A helper class that captures the time from object creation until #done() was called. 21 * @author Michael Zangl 22 */ 23 public static class PerformanceTestTimerCapture { 24 private final long time; 25 26 protected PerformanceTestTimerCapture() { 27 time = System.nanoTime(); 28 } 29 30 /** 31 * Get the time since this object was created. 32 * @return The time. 33 */ 34 public long getTimeSinceCreation() { 35 return (System.nanoTime() - time) / 1000000; 36 } 37 } 38 13 39 /** 14 40 * A timer that measures the time from it's creation to the {@link #done()} call. 15 41 * @author Michael Zangl 16 42 */ 17 public static class PerformanceTestTimer {43 public static class PerformanceTestTimer extends PerformanceTestTimerCapture { 18 44 private final String name; 19 private final long time; 20 private boolean measurementPlotsPlugin = false; 45 private boolean measurementPlotsPlugin = true; 21 46 22 47 protected PerformanceTestTimer(String name) { 23 48 this.name = name; 24 time = System.nanoTime();25 49 } 26 50 … … 37 61 */ 38 62 public void done() { 39 long dTime = (System.nanoTime() - time) / 1000000;63 long dTime = getTimeSinceCreation(); 40 64 if (measurementPlotsPlugin) { 41 65 measurementPlotsPluginOutput(name + "(ms)", dTime); … … 50 74 51 75 /** 52 * Starts a new performance timer. 76 * Starts a new performance timer. The timer will output the measurements in a format understood by Jenkins. 77 * <p> 78 * The timer can only be used to meassure one value. 53 79 * @param name The name/description of the timer. 54 80 * @return A {@link PerformanceTestTimer} object of which you can call {@link PerformanceTestTimer#done()} when done. … … 56 82 @SuppressFBWarnings(value = "DM_GC", justification = "Performance test code") 57 83 public static PerformanceTestTimer startTimer(String name) { 84 cleanSystem(); 85 return new PerformanceTestTimer(name); 86 } 87 88 /** 89 * Runs the given performance test several (approx. 10) times and prints the median run time. 90 * @param name The name to use in the output 91 * @param testRunner The test to run 92 */ 93 public static void runPerformanceTest(String name, Runnable testRunner) { 94 for (int i = 0; i < TIMES_WARMUP; i++) { 95 cleanSystem(); 96 PerformanceTestTimerCapture capture = new PerformanceTestTimerCapture(); 97 testRunner.run(); 98 capture.getTimeSinceCreation(); 99 } 100 ArrayList<Long> times = new ArrayList<>(); 101 for (int i = 0; i < TIMES_RUN; i++) { 102 cleanSystem(); 103 PerformanceTestTimerCapture capture = new PerformanceTestTimerCapture(); 104 testRunner.run(); 105 times.add(capture.getTimeSinceCreation()); 106 } 107 System.out.println(times); 108 Collections.sort(times); 109 // Sort out e.g. GC during test run. 110 double avg = times.subList(2, times.size() - 2).stream().mapToLong(l -> l).average().getAsDouble(); 111 measurementPlotsPluginOutput(name, avg); 112 } 113 114 private static void cleanSystem() { 58 115 System.gc(); 59 116 System.runFinalization(); 60 return new PerformanceTestTimer(name);61 117 } 62 118 … … 68 124 * @param name the name / title of the measurement 69 125 * @param value the value 70 * @see https://wiki.jenkins-ci.org/display/JENKINS/Measurement+Plots+Plugin126 * @see "https://wiki.jenkins-ci.org/display/JENKINS/Measurement+Plots+Plugin" 71 127 */ 72 128 public static void measurementPlotsPluginOutput(String name, double value) { -
trunk/test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSourceFilterTest.java
r10222 r10674 104 104 data.generateDataSet(); 105 105 CssGenerator css = new CssGenerator(data).addKeyValueRules(TEST_RULE_COUNT); 106 runTest(data, css, "only key=value rules" , false);106 runTest(data, css, "only key=value rules"); 107 107 } 108 108 … … 115 115 data.generateDataSet(); 116 116 CssGenerator css = new CssGenerator(data).addHasKeyRules(TEST_RULE_COUNT); 117 runTest(data, css, "only has key rules" , false);117 runTest(data, css, "only has key rules"); 118 118 } 119 119 … … 126 126 data.generateDataSet(); 127 127 CssGenerator css = new CssGenerator(data).addKeyRegexpRules(TEST_RULE_COUNT); 128 runTest(data, css, "regular expressions" , true);128 runTest(data, css, "regular expressions"); 129 129 } 130 130 … … 137 137 data.generateDataSet(); 138 138 CssGenerator css = new CssGenerator(data).addIsTrueRules(TEST_RULE_COUNT); 139 runTest(data, css, "is true" , false);139 runTest(data, css, "is true"); 140 140 } 141 141 142 private void runTest(KeyValueDataGenerator data, CssGenerator css, String description , boolean measurementPlotsPlugin) {142 private void runTest(KeyValueDataGenerator data, CssGenerator css, String description) { 143 143 MapCSSStyleSource source = new MapCSSStyleSource(css.getCss()); 144 144 PerformanceTestTimer timer = PerformanceTestUtils.startTimer("MapCSSStyleSource#loadStyleSource(...) for " + description); … … 146 146 timer.done(); 147 147 148 if (measurementPlotsPlugin) { 149 timer = PerformanceTestUtils.startTimer(description); 150 timer.setMeasurementPlotsPluginOutput(true); 151 } else { 152 timer = PerformanceTestUtils.startTimer(APPLY_CALLS + "x MapCSSStyleSource#apply(...) for " + description); 153 } 148 timer = PerformanceTestUtils.startTimer(APPLY_CALLS + "x MapCSSStyleSource#apply(...) for " + description); 154 149 for (int i = 0; i < APPLY_CALLS; i++) { 155 150 MultiCascade mc = new MultiCascade();
Note:
See TracChangeset
for help on using the changeset viewer.