Index: /trunk/src/org/openstreetmap/josm/data/osm/OsmUtils.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/OsmUtils.java	(revision 10673)
+++ /trunk/src/org/openstreetmap/josm/data/osm/OsmUtils.java	(revision 10674)
@@ -75,6 +75,8 @@
             throw new IllegalArgumentException("Expecting n/node/w/way/r/relation/area, but got '" + x[0] + '\'');
         }
-        for (final Map.Entry<String, String> i : TextTagParser.readTagsFromText(x[1]).entrySet()) {
-            p.put(i.getKey(), i.getValue());
+        if (x.length > 1) {
+            for (final Map.Entry<String, String> i : TextTagParser.readTagsFromText(x[1]).entrySet()) {
+                p.put(i.getKey(), i.getValue());
+            }
         }
         return p;
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 10673)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 10674)
@@ -421,10 +421,6 @@
             try {
                 final Condition c = matchingSelector.getConditions().get(index);
-                final Tag tag = c instanceof Condition.KeyCondition
-                        ? ((Condition.KeyCondition) c).asTag(p)
-                        : c instanceof Condition.SimpleKeyValueCondition
-                        ? ((Condition.SimpleKeyValueCondition) c).asTag()
-                        : c instanceof Condition.KeyValueCondition
-                        ? ((Condition.KeyValueCondition) c).asTag()
+                final Tag tag = c instanceof Condition.ToTagConvertable
+                        ? ((Condition.ToTagConvertable) c).asTag(p)
                         : null;
                 if (tag == null) {
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java	(revision 10673)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java	(revision 10674)
@@ -6,9 +6,10 @@
 import java.text.MessageFormat;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.EnumSet;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.IntFunction;
 import java.util.function.Predicate;
 import java.util.regex.Pattern;
@@ -28,12 +29,28 @@
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.Predicates;
-import org.openstreetmap.josm.tools.SubclassFilteredCollection;
 import org.openstreetmap.josm.tools.Utils;
 
+/**
+ * This is a condition that needs to be fulfilled in order to apply a MapCSS style.
+ */
 @FunctionalInterface
 public interface Condition {
 
+    /**
+     * Checks if the condition applies in the given MapCSS {@link Environment}.
+     * @param e The environment to check. May not be <code>null</code>.
+     * @return <code>true</code> if the condition applies.
+     */
     boolean applies(Environment e);
 
+    /**
+     * Create a new condition that checks the key and the value of the object.
+     * @param k The key.
+     * @param v The reference value
+     * @param op The operation to use when comparing the value
+     * @param context The type of context to use.
+     * @param considerValAsKey whether to consider {@code v} as another key and compare the values of key {@code k} and key {@code v}.
+     * @return The new condition.
+     */
     static Condition createKeyValueCondition(String k, String v, Op op, Context context, boolean considerValAsKey) {
         switch (context) {
@@ -59,8 +76,23 @@
     }
 
+    /**
+     * Create a condition in which the key and the value need to match a given regexp
+     * @param k The key regexp
+     * @param v The value regexp
+     * @param op The operation to use when comparing the key and the value.
+     * @return The new condition.
+     */
     static Condition createRegexpKeyRegexpValueCondition(String k, String v, Op op) {
         return new RegexpKeyValueRegexpCondition(k, v, op);
     }
 
+    /**
+     * Creates a condition that checks the given key.
+     * @param k The key to test for
+     * @param not <code>true</code> to invert the match
+     * @param matchType The match type to check for.
+     * @param context The context this rule is found in.
+     * @return the new condition.
+     */
     static Condition createKeyCondition(String k, boolean not, KeyMatchType matchType, Context context) {
         switch (context) {
@@ -79,12 +111,32 @@
     }
 
+    /**
+     * Create a new pseudo class condition
+     * @param id The id of the pseudo class
+     * @param not <code>true</code> to invert the condition
+     * @param context The context the class is found in.
+     * @return The new condition
+     */
     static PseudoClassCondition createPseudoClassCondition(String id, boolean not, Context context) {
         return PseudoClassCondition.createPseudoClassCondition(id, not, context);
     }
 
+    /**
+     * Create a new class condition
+     * @param id The id of the class to match
+     * @param not <code>true</code> to invert the condition
+     * @param context Ignored
+     * @return The new condition
+     */
     static ClassCondition createClassCondition(String id, boolean not, Context context) {
         return new ClassCondition(id, not);
     }
 
+    /**
+     * Create a new condition that a expression needs to be fulfilled
+     * @param e the expression to check
+     * @param context Ignored
+     * @return The new condition
+     */
     static ExpressionCondition createExpressionCondition(Expression e, Context context) {
         return new ExpressionCondition(e);
@@ -96,30 +148,72 @@
     enum Op {
         /** The value equals the given reference. */
-        EQ,
+        EQ(Objects::equals),
         /** The value does not equal the reference. */
-        NEQ,
+        NEQ(EQ),
         /** The value is greater than or equal to the given reference value (as float). */
-        GREATER_OR_EQUAL,
+        GREATER_OR_EQUAL(comparisonResult -> comparisonResult >= 0),
         /** The value is greater than the given reference value (as float). */
-        GREATER,
+        GREATER(comparisonResult -> comparisonResult > 0),
         /** The value is less than or equal to the given reference value (as float). */
-        LESS_OR_EQUAL,
+        LESS_OR_EQUAL(comparisonResult -> comparisonResult <= 0),
         /** The value is less than the given reference value (as float). */
-        LESS,
+        LESS(comparisonResult -> comparisonResult < 0),
         /** The reference is treated as regular expression and the value needs to match it. */
-        REGEX,
+        REGEX((test, prototype) -> Pattern.compile(prototype).matcher(test).find()),
         /** The reference is treated as regular expression and the value needs to not match it. */
-        NREGEX,
+        NREGEX(REGEX),
         /** The reference is treated as a list separated by ';'. Spaces around the ; are ignored.
          *  The value needs to be equal one of the list elements. */
-        ONE_OF,
+        ONE_OF((test, prototype) -> Arrays.asList(test.split("\\s*;\\s*")).contains(prototype)),
         /** The value needs to begin with the reference string. */
-        BEGINS_WITH,
+        BEGINS_WITH((test, prototype) -> test.startsWith(prototype)),
         /** The value needs to end with the reference string. */
-        ENDS_WITH,
+        ENDS_WITH((test, prototype) -> test.endsWith(prototype)),
         /** The value needs to contain the reference string. */
-        CONTAINS;
+        CONTAINS((test, prototype) -> test.contains(prototype));
 
         static final Set<Op> NEGATED_OPS = EnumSet.of(NEQ, NREGEX);
+
+        private final BiFunction<String, String, Boolean> function;
+
+        private final boolean negated;
+
+        /**
+         * Create a new string operation.
+         * @param func The function to apply during {@link #eval(String, String)}.
+         */
+        Op(BiFunction<String, String, Boolean> func) {
+            this.function = func;
+            negated = false;
+        }
+
+        /**
+         * Create a new float operation that compares two float values
+         * @param comparatorResult A function to mapt the result of the comparison
+         */
+        Op(IntFunction<Boolean> comparatorResult) {
+            this.function = (test, prototype) -> {
+                float testFloat;
+                try {
+                    testFloat = Float.parseFloat(test);
+                } catch (NumberFormatException e) {
+                    return false;
+                }
+                float prototypeFloat = Float.parseFloat(prototype);
+
+                int res = Float.compare(testFloat, prototypeFloat);
+                return comparatorResult.apply(res);
+            };
+            negated = false;
+        }
+
+        /**
+         * Create a new Op by negating an other op.
+         * @param negate inverse operation
+         */
+        Op(Op negate) {
+            this.function = (a, b) -> !negate.function.apply(a, b);
+            negated = true;
+        }
 
         /**
@@ -130,53 +224,8 @@
          */
         public boolean eval(String testString, String prototypeString) {
-            if (testString == null && !NEGATED_OPS.contains(this))
-                return false;
-            switch (this) {
-            case EQ:
-                return Objects.equals(testString, prototypeString);
-            case NEQ:
-                return !Objects.equals(testString, prototypeString);
-            case REGEX:
-            case NREGEX:
-                final boolean contains = Pattern.compile(prototypeString).matcher(testString).find();
-                return REGEX.equals(this) ? contains : !contains;
-            case ONE_OF:
-                return testString != null && Arrays.asList(testString.split("\\s*;\\s*")).contains(prototypeString);
-            case BEGINS_WITH:
-                return testString != null && testString.startsWith(prototypeString);
-            case ENDS_WITH:
-                return testString != null && testString.endsWith(prototypeString);
-            case CONTAINS:
-                return testString != null && testString.contains(prototypeString);
-            case GREATER_OR_EQUAL:
-            case GREATER:
-            case LESS_OR_EQUAL:
-            case LESS:
-                // See below
-                break;
-            default:
-                throw new AssertionError();
-            }
-
-            float testFloat;
-            try {
-                testFloat = Float.parseFloat(testString);
-            } catch (NumberFormatException e) {
-                return false;
-            }
-            float prototypeFloat = Float.parseFloat(prototypeString);
-
-            switch (this) {
-            case GREATER_OR_EQUAL:
-                return testFloat >= prototypeFloat;
-            case GREATER:
-                return testFloat > prototypeFloat;
-            case LESS_OR_EQUAL:
-                return testFloat <= prototypeFloat;
-            case LESS:
-                return testFloat < prototypeFloat;
-            default:
-                throw new AssertionError();
-            }
+            if (testString == null)
+                return negated;
+            else
+                return function.apply(testString, prototypeString);
         }
     }
@@ -202,5 +251,5 @@
      * Extra class for performance reasons.
      */
-    class SimpleKeyValueCondition implements Condition {
+    class SimpleKeyValueCondition implements Condition, ToTagConvertable {
         /**
          * The key to search for.
@@ -227,5 +276,6 @@
         }
 
-        public Tag asTag() {
+        @Override
+        public Tag asTag(OsmPrimitive primitive) {
             return new Tag(k, v);
         }
@@ -242,5 +292,5 @@
      *
      */
-    class KeyValueCondition implements Condition {
+    class KeyValueCondition implements Condition, ToTagConvertable {
         /**
          * The key to search for.
@@ -258,5 +308,5 @@
          * If this flag is set, {@link #v} is treated as a key and the value is the value set for that key.
          */
-        public boolean considerValAsKey;
+        public final boolean considerValAsKey;
 
         /**
@@ -280,5 +330,6 @@
         }
 
-        public Tag asTag() {
+        @Override
+        public Tag asTag(OsmPrimitive primitive) {
             return new Tag(k, v);
         }
@@ -290,8 +341,11 @@
     }
 
+    /**
+     * This condition requires a fixed key to match a given regexp
+     */
     class KeyValueRegexpCondition extends KeyValueCondition {
-
-        public final Pattern pattern;
         protected static final Set<Op> SUPPORTED_OPS = EnumSet.of(Op.REGEX, Op.NREGEX);
+
+        final Pattern pattern;
 
         public KeyValueRegexpCondition(String k, String v, Op op, boolean considerValAsKey) {
@@ -319,8 +373,17 @@
     }
 
+    /**
+     * A condition that checks that a key with the matching pattern has a value with the matching pattern.
+     */
     class RegexpKeyValueRegexpCondition extends KeyValueRegexpCondition {
 
         public final Pattern keyPattern;
 
+        /**
+         * Create a condition in which the key and the value need to match a given regexp
+         * @param k The key regexp
+         * @param v The value regexp
+         * @param op The operation to use when comparing the key and the value.
+         */
         public RegexpKeyValueRegexpCondition(String k, String v, Op op) {
             super(k, v, op, false);
@@ -419,5 +482,5 @@
      * </pre>
      */
-    class KeyCondition implements Condition {
+    class KeyCondition implements Condition, ToTagConvertable {
 
         /**
@@ -484,11 +547,9 @@
          * @return The tag.
          */
+        @Override
         public Tag asTag(OsmPrimitive p) {
             String key = label;
             if (KeyMatchType.REGEX.equals(matchType)) {
-                final Collection<String> matchingKeys = SubclassFilteredCollection.filter(p.keySet(), containsPattern);
-                if (!matchingKeys.isEmpty()) {
-                    key = matchingKeys.iterator().next();
-                }
+                key = p.keySet().stream().filter(containsPattern).findAny().orElse(key);
             }
             return new Tag(key, p.get(key));
@@ -513,5 +574,6 @@
         @Override
         public boolean applies(Environment env) {
-            return env != null && env.getCascade(env.layer) != null && (not ^ env.getCascade(env.layer).containsKey(id));
+            Cascade cascade = env.getCascade(env.layer);
+            return cascade != null && (not ^ cascade.containsKey(id));
         }
 
@@ -703,4 +765,11 @@
         }
 
+        /**
+         * Create a new pseudo class condition
+         * @param id The id of the pseudo class
+         * @param not <code>true</code> to invert the condition
+         * @param context The context the class is found in.
+         * @return The new condition
+         */
         public static PseudoClassCondition createPseudoClassCondition(String id, boolean not, Context context) {
             CheckParameterUtil.ensureThat(!"sameTags".equals(id) || Context.LINK.equals(context), "sameTags only supported in LINK context");
@@ -753,4 +822,7 @@
     }
 
+    /**
+     * A condition that is fulfilled whenever the expression is evaluated to be true.
+     */
     class ExpressionCondition implements Condition {
 
@@ -776,3 +848,17 @@
         }
     }
+
+    /**
+     * This is a condition that can be converted to a tag
+     * @author Michael Zangl
+     * @since 10674
+     */
+    public interface ToTagConvertable {
+        /**
+         * Converts the current condition to a tag
+         * @param primitive A primitive to use as context. May be ignored.
+         * @return A tag with the key/value of this condition.
+         */
+        Tag asTag(OsmPrimitive primitive);
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java	(revision 10673)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java	(revision 10674)
@@ -465,4 +465,5 @@
         @Override
         public boolean matches(Environment env) {
+            CheckParameterUtil.ensureParameterNotNull(env, "env");
             if (conds == null) return true;
             for (Condition c : conds) {
Index: /trunk/test/performance/org/openstreetmap/josm/PerformanceTestUtils.java
===================================================================
--- /trunk/test/performance/org/openstreetmap/josm/PerformanceTestUtils.java	(revision 10673)
+++ /trunk/test/performance/org/openstreetmap/josm/PerformanceTestUtils.java	(revision 10674)
@@ -1,4 +1,7 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm;
+
+import java.util.ArrayList;
+import java.util.Collections;
 
 import org.openstreetmap.josm.io.XmlWriter;
@@ -11,16 +14,37 @@
  */
 public final class PerformanceTestUtils {
+    private static final int TIMES_WARMUP = 2;
+    private static final int TIMES_RUN = 8;
+
+    /**
+     * A helper class that captures the time from object creation until #done() was called.
+     * @author Michael Zangl
+     */
+    public static class PerformanceTestTimerCapture {
+        private final long time;
+
+        protected PerformanceTestTimerCapture() {
+            time = System.nanoTime();
+        }
+
+        /**
+         * Get the time since this object was created.
+         * @return The time.
+         */
+        public long getTimeSinceCreation() {
+            return (System.nanoTime() - time) / 1000000;
+        }
+    }
+
     /**
      * A timer that measures the time from it's creation to the {@link #done()} call.
      * @author Michael Zangl
      */
-    public static class PerformanceTestTimer {
+    public static class PerformanceTestTimer extends PerformanceTestTimerCapture {
         private final String name;
-        private final long time;
-        private boolean measurementPlotsPlugin = false;
+        private boolean measurementPlotsPlugin = true;
 
         protected PerformanceTestTimer(String name) {
             this.name = name;
-            time = System.nanoTime();
         }
 
@@ -37,5 +61,5 @@
          */
         public void done() {
-            long dTime = (System.nanoTime() - time) / 1000000;
+            long dTime = getTimeSinceCreation();
             if (measurementPlotsPlugin) {
                 measurementPlotsPluginOutput(name + "(ms)", dTime);
@@ -50,5 +74,7 @@
 
     /**
-     * Starts a new performance timer.
+     * Starts a new performance timer. The timer will output the measurements in a format understood by Jenkins.
+     * <p>
+     * The timer can only be used to meassure one value.
      * @param name The name/description of the timer.
      * @return A {@link PerformanceTestTimer} object of which you can call {@link PerformanceTestTimer#done()} when done.
@@ -56,7 +82,37 @@
     @SuppressFBWarnings(value = "DM_GC", justification = "Performance test code")
     public static PerformanceTestTimer startTimer(String name) {
+        cleanSystem();
+        return new PerformanceTestTimer(name);
+    }
+
+    /**
+     * Runs the given performance test several (approx. 10) times and prints the median run time.
+     * @param name The name to use in the output
+     * @param testRunner The test to run
+     */
+    public static void runPerformanceTest(String name, Runnable testRunner) {
+        for (int i = 0; i < TIMES_WARMUP; i++) {
+            cleanSystem();
+            PerformanceTestTimerCapture capture = new PerformanceTestTimerCapture();
+            testRunner.run();
+            capture.getTimeSinceCreation();
+        }
+        ArrayList<Long> times = new ArrayList<>();
+        for (int i = 0; i < TIMES_RUN; i++) {
+            cleanSystem();
+            PerformanceTestTimerCapture capture = new PerformanceTestTimerCapture();
+            testRunner.run();
+            times.add(capture.getTimeSinceCreation());
+        }
+        System.out.println(times);
+        Collections.sort(times);
+        // Sort out e.g. GC during test run.
+        double avg = times.subList(2, times.size() - 2).stream().mapToLong(l -> l).average().getAsDouble();
+        measurementPlotsPluginOutput(name, avg);
+    }
+
+    private static void cleanSystem() {
         System.gc();
         System.runFinalization();
-        return new PerformanceTestTimer(name);
     }
 
@@ -68,5 +124,5 @@
      * @param name the name / title of the measurement
      * @param value the value
-     * @see https://wiki.jenkins-ci.org/display/JENKINS/Measurement+Plots+Plugin
+     * @see "https://wiki.jenkins-ci.org/display/JENKINS/Measurement+Plots+Plugin"
      */
     public static void measurementPlotsPluginOutput(String name, double value) {
Index: /trunk/test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSConditionPerformanceTest.java
===================================================================
--- /trunk/test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSConditionPerformanceTest.java	(revision 10674)
+++ /trunk/test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSConditionPerformanceTest.java	(revision 10674)
@@ -0,0 +1,68 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.mapcss;
+
+import java.util.EnumSet;
+
+import org.junit.Test;
+import org.openstreetmap.josm.PerformanceTestUtils;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.Op;
+
+/**
+ * Performance test of MapCSS Condition objects.
+ * @author Michael Zangl
+ */
+public class MapCSSConditionPerformanceTest {
+    /**
+     * Test the performance of all OP entries.
+     */
+    @Test
+    public void testAllOps() {
+        // The JIT does some really heavy optimisations if it notices that other values are not used.
+        // If we want to simulate a real scenario, we need to invoke every op several times to let the compiler
+        // build the jump tables.
+        for (Op op : Op.values()) {
+            getRunner(op).run();
+        }
+        for (Op op : Op.values()) {
+            runTest(op);
+        }
+    }
+
+    private void runTest(Op op) {
+        Runnable r = getRunner(op);
+        PerformanceTestUtils.runPerformanceTest("Condition.Op." + op, r);
+    }
+
+    private Runnable getRunner(Op op) {
+        Runnable r;
+        if (EnumSet.of(Op.LESS, Op.LESS_OR_EQUAL, Op.GREATER, Op.GREATER_OR_EQUAL).contains(op)) {
+            r = () -> {
+                for (int i = 0; i < 10000; i++) {
+                        op.eval(null, "0.2");
+                        op.eval("nan", "0.1");
+                        op.eval("0.2983", "192.312");
+                        op.eval("0.2983", "0.2983");
+                        op.eval("2983", "1000");
+                        op.eval("1000", "1000");
+                }
+            };
+        } else {
+            // regexp are slow
+            int runs = EnumSet.of(Op.ONE_OF, Op.REGEX, Op.NREGEX).contains(op) ? 10000 : 100000;
+            r = () -> {
+                for (int i = 0; i < runs; i++) {
+                    op.eval("k1", "v1");
+                    op.eval("k1", "k1");
+                    op.eval("", "v1");
+                    op.eval(null, "abc");
+                    op.eval("extreamlylongkeyextreamlylongkeyextreamlylongkeyextreamlylongkey",
+                            "longvaluelongvaluelongvaluelongvalue");
+                    op.eval("0.2983", "192.312");
+                    op.eval("0.2983", "0.2983");
+                    op.eval("2983", "\\d+");
+                }
+            };
+        }
+        return r;
+    }
+}
Index: /trunk/test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSourceFilterTest.java
===================================================================
--- /trunk/test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSourceFilterTest.java	(revision 10673)
+++ /trunk/test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSourceFilterTest.java	(revision 10674)
@@ -104,5 +104,5 @@
         data.generateDataSet();
         CssGenerator css = new CssGenerator(data).addKeyValueRules(TEST_RULE_COUNT);
-        runTest(data, css, "only key=value rules", false);
+        runTest(data, css, "only key=value rules");
     }
 
@@ -115,5 +115,5 @@
         data.generateDataSet();
         CssGenerator css = new CssGenerator(data).addHasKeyRules(TEST_RULE_COUNT);
-        runTest(data, css, "only has key rules", false);
+        runTest(data, css, "only has key rules");
     }
 
@@ -126,5 +126,5 @@
         data.generateDataSet();
         CssGenerator css = new CssGenerator(data).addKeyRegexpRules(TEST_RULE_COUNT);
-        runTest(data, css, "regular expressions", true);
+        runTest(data, css, "regular expressions");
     }
 
@@ -137,8 +137,8 @@
         data.generateDataSet();
         CssGenerator css = new CssGenerator(data).addIsTrueRules(TEST_RULE_COUNT);
-        runTest(data, css, "is true", false);
+        runTest(data, css, "is true");
     }
 
-    private void runTest(KeyValueDataGenerator data, CssGenerator css, String description, boolean measurementPlotsPlugin) {
+    private void runTest(KeyValueDataGenerator data, CssGenerator css, String description) {
         MapCSSStyleSource source = new MapCSSStyleSource(css.getCss());
         PerformanceTestTimer timer = PerformanceTestUtils.startTimer("MapCSSStyleSource#loadStyleSource(...) for " + description);
@@ -146,10 +146,5 @@
         timer.done();
 
-        if (measurementPlotsPlugin) {
-            timer = PerformanceTestUtils.startTimer(description);
-            timer.setMeasurementPlotsPluginOutput(true);
-        } else {
-            timer = PerformanceTestUtils.startTimer(APPLY_CALLS + "x MapCSSStyleSource#apply(...) for " + description);
-        }
+        timer = PerformanceTestUtils.startTimer(APPLY_CALLS + "x MapCSSStyleSource#apply(...) for " + description);
         for (int i = 0; i < APPLY_CALLS; i++) {
             MultiCascade mc = new MultiCascade();
Index: /trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionTest.java	(revision 10674)
+++ /trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionTest.java	(revision 10674)
@@ -0,0 +1,251 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.mapcss;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmUtils;
+import org.openstreetmap.josm.gui.mappaint.Environment;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.Context;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.Op;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.SimpleKeyValueCondition;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.ToTagConvertable;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * This test universally tests all {@link Condition}s.
+ * @author Michael Zangl
+ */
+public class ConditionTest {
+    /**
+     * We need prefs for nodes.
+     */
+    @Rule
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules().preferences();
+    private OsmPrimitive node0;
+    private OsmPrimitive node1;
+    private OsmPrimitive node2;
+    private OsmPrimitive node3;
+    private OsmPrimitive node4;
+
+    /**
+     * Set up some useful test data.
+     */
+    @Before
+    public void setUp() {
+        node0 = OsmUtils.createPrimitive("n");
+        node1 = OsmUtils.createPrimitive("n k1=v1 k2=v1 f1=0.2 r1=ababx c1=xya one=a;b");
+        node2 = OsmUtils.createPrimitive("n k1=v1 k2=v1a f1=0.8 r1=abxabxab c1=xy one=a;;x");
+        node3 = OsmUtils.createPrimitive("n k1=v1 f1=-100 c1=axy one=x;y;z");
+        node4 = OsmUtils.createPrimitive("n k1=v2a k2=v3 f1=x r1=abab c1=axya one=x;a;y");
+    }
+
+    /**
+     * Test {@link Op#EQ}.
+     */
+    @Test
+    public void testKeyValueEq() {
+        Condition op = Condition.createKeyValueCondition("k1", "v1", Op.EQ, Context.PRIMITIVE, false);
+        assertFalse(op.applies(genEnv(node0)));
+        assertTrue(op.applies(genEnv(node1)));
+        assertTrue(op.applies(genEnv(node2)));
+        assertTrue(op.applies(genEnv(node3)));
+        assertFalse(op.applies(genEnv(node4)));
+
+        assertTrue(op instanceof SimpleKeyValueCondition);
+        assertEquals("[k1=v1]", op.toString());
+        assertEquals("k1", ((ToTagConvertable) op).asTag(null).getKey());
+        assertEquals("v1", ((ToTagConvertable) op).asTag(null).getValue());
+    }
+
+    /**
+     * Test {@link Op#EQ} and interpetation as key
+     */
+    @Test
+    public void testKeyValueEqAsKey() {
+        Condition op = Condition.createKeyValueCondition("k1", "k2", Op.EQ, Context.PRIMITIVE, true);
+        assertFalse(op.applies(genEnv(node0)));
+        assertTrue(op.applies(genEnv(node1)));
+        assertFalse(op.applies(genEnv(node2)));
+        assertFalse(op.applies(genEnv(node3)));
+        assertFalse(op.applies(genEnv(node4)));
+
+        assertEquals("k1", ((ToTagConvertable) op).asTag(null).getKey());
+        assertEquals("k2", ((ToTagConvertable) op).asTag(null).getValue());
+    }
+
+    /**
+     * Test {@link Op#NEQ}
+     */
+    @Test
+    public void testKeyValueNeq() {
+        Condition op = Condition.createKeyValueCondition("k1", "v1", Op.NEQ, Context.PRIMITIVE, false);
+        assertTrue(op.applies(genEnv(node0)));
+        assertFalse(op.applies(genEnv(node1)));
+        assertFalse(op.applies(genEnv(node2)));
+        assertFalse(op.applies(genEnv(node3)));
+        assertTrue(op.applies(genEnv(node4)));
+    }
+
+    /**
+     * Test {@link Op#GREATER_OR_EQUAL}
+     */
+    @Test
+    public void testKeyValueGreatherEq() {
+        Condition op = Condition.createKeyValueCondition("f1", "0.2", Op.GREATER_OR_EQUAL, Context.PRIMITIVE, false);
+        assertFalse(op.applies(genEnv(node0)));
+        assertTrue(op.applies(genEnv(node1)));
+        assertTrue(op.applies(genEnv(node2)));
+        assertFalse(op.applies(genEnv(node3)));
+        assertFalse(op.applies(genEnv(node4)));
+    }
+
+    /**
+     * Test {@link Op#GREATER}
+     */
+    @Test
+    public void testKeyValueGreather() {
+        Condition op = Condition.createKeyValueCondition("f1", "0.2", Op.GREATER, Context.PRIMITIVE, false);
+        assertFalse(op.applies(genEnv(node0)));
+        assertFalse(op.applies(genEnv(node1)));
+        assertTrue(op.applies(genEnv(node2)));
+        assertFalse(op.applies(genEnv(node3)));
+        assertFalse(op.applies(genEnv(node4)));
+    }
+
+    /**
+     * Test {@link Op#LESS_OR_EQUAL}
+     */
+    @Test
+    public void testKeyValueLessEq() {
+        Condition op = Condition.createKeyValueCondition("f1", "0.2", Op.LESS_OR_EQUAL, Context.PRIMITIVE, false);
+        assertFalse(op.applies(genEnv(node0)));
+        assertTrue(op.applies(genEnv(node1)));
+        assertFalse(op.applies(genEnv(node2)));
+        assertTrue(op.applies(genEnv(node3)));
+        assertFalse(op.applies(genEnv(node4)));
+    }
+
+    /**
+     * Test {@link Op#LESS}
+     */
+    @Test
+    public void testKeyValueLess() {
+        Condition op = Condition.createKeyValueCondition("f1", "0.2", Op.LESS, Context.PRIMITIVE, false);
+        assertFalse(op.applies(genEnv(node0)));
+        assertFalse(op.applies(genEnv(node1)));
+        assertFalse(op.applies(genEnv(node2)));
+        assertTrue(op.applies(genEnv(node3)));
+        assertFalse(op.applies(genEnv(node4)));
+    }
+
+    /**
+     * Test {@link Op#REGEX}
+     */
+    @Test
+    public void testKeyValueRegex() {
+        Condition op = Condition.createKeyValueCondition("r1", "(ab){2}", Op.REGEX, Context.PRIMITIVE, false);
+        assertFalse(op.applies(genEnv(node0)));
+        assertTrue(op.applies(genEnv(node1)));
+        assertFalse(op.applies(genEnv(node2)));
+        assertFalse(op.applies(genEnv(node3)));
+        assertTrue(op.applies(genEnv(node4)));
+    }
+
+    /**
+     * Test {@link Op#NREGEX}
+     */
+    @Test
+    public void testKeyValueNregex() {
+        Condition op = Condition.createKeyValueCondition("r1", "(ab){2}", Op.NREGEX, Context.PRIMITIVE, false);
+        assertTrue(op.applies(genEnv(node0)));
+        assertFalse(op.applies(genEnv(node1)));
+        assertTrue(op.applies(genEnv(node2)));
+        assertTrue(op.applies(genEnv(node3)));
+        assertFalse(op.applies(genEnv(node4)));
+    }
+
+    /**
+     * Test {@link Op#ONE_OF}
+     */
+    @Test
+    public void testKeyValueOneOf() {
+        Condition op = Condition.createKeyValueCondition("one", "a", Op.ONE_OF, Context.PRIMITIVE, false);
+        assertFalse(op.applies(genEnv(node0)));
+        assertTrue(op.applies(genEnv(node1)));
+        assertTrue(op.applies(genEnv(node2)));
+        assertFalse(op.applies(genEnv(node3)));
+        assertTrue(op.applies(genEnv(node4)));
+    }
+
+    /**
+     * Test {@link Op#BEGINS_WITH}
+     */
+    @Test
+    public void testKeyValueBeginsWith() {
+        Condition op = Condition.createKeyValueCondition("c1", "xy", Op.BEGINS_WITH, Context.PRIMITIVE, false);
+        assertFalse(op.applies(genEnv(node0)));
+        assertTrue(op.applies(genEnv(node1)));
+        assertTrue(op.applies(genEnv(node2)));
+        assertFalse(op.applies(genEnv(node3)));
+        assertFalse(op.applies(genEnv(node4)));
+    }
+
+    /**
+     * Test {@link Op#ENDS_WITH}
+     */
+    @Test
+    public void testKeyValueEndsWith() {
+        Condition op = Condition.createKeyValueCondition("c1", "xy", Op.ENDS_WITH, Context.PRIMITIVE, false);
+        assertFalse(op.applies(genEnv(node0)));
+        assertFalse(op.applies(genEnv(node1)));
+        assertTrue(op.applies(genEnv(node2)));
+        assertTrue(op.applies(genEnv(node3)));
+        assertFalse(op.applies(genEnv(node4)));
+    }
+
+    /**
+     * Test {@link Op#CONTAINS}
+     */
+    @Test
+    public void testKeyValueContains() {
+        Condition op = Condition.createKeyValueCondition("c1", "xy", Op.CONTAINS, Context.PRIMITIVE, false);
+        assertFalse(op.applies(genEnv(node0)));
+        assertTrue(op.applies(genEnv(node1)));
+        assertTrue(op.applies(genEnv(node2)));
+        assertTrue(op.applies(genEnv(node3)));
+        assertTrue(op.applies(genEnv(node4)));
+    }
+
+    /**
+     * Test of {@link Condition#createRegexpKeyRegexpValueCondition(String, String, Op)}
+     */
+    @Test
+    public void testRegexpKeyValueRegexpCondition() {
+        Condition op = Condition.createRegexpKeyRegexpValueCondition("^k", "\\da", Op.REGEX);
+        assertFalse(op.applies(genEnv(node0)));
+        assertFalse(op.applies(genEnv(node1)));
+        assertTrue(op.applies(genEnv(node2)));
+        assertFalse(op.applies(genEnv(node3)));
+        assertTrue(op.applies(genEnv(node4)));
+
+        Condition notOp = Condition.createRegexpKeyRegexpValueCondition("^k", "\\da", Op.NREGEX);
+        assertTrue(notOp.applies(genEnv(node0)));
+        assertTrue(notOp.applies(genEnv(node1)));
+        assertFalse(notOp.applies(genEnv(node2)));
+        assertTrue(notOp.applies(genEnv(node3)));
+        assertFalse(notOp.applies(genEnv(node4)));
+    }
+
+    private Environment genEnv(OsmPrimitive primitive) {
+        return new Environment(primitive);
+    }
+}
