Index: /trunk/data/validator/highway.mapcss
===================================================================
--- /trunk/data/validator/highway.mapcss	(revision 6513)
+++ /trunk/data/validator/highway.mapcss	(revision 6513)
@@ -0,0 +1,98 @@
+way[highway][name =~ /(?i).* (Ave|Blvd|Br|Brg|Cct|Cir|Cl|Cr|Crct|Cres|Crt|Ct|Dr|Drv|Esp|Espl|Hwy|Ln|Mw|Mwy|Pl|Rd|Qy|Qys|Sq|St|Str|Ter|Tce|Tr|Wy)[.]?$/] {
+  throwWarning: tr("abbreviated street name");
+  assertMatch: "way highway=unclassified name=\"Foo Ave\"";
+  assertMatch: "way highway=unclassified name=\"Foo Ave.\"";
+  assertMatch: "way highway=unclassified name=\"Bou Blvd.\"";
+  assertMatch: "way highway=unclassified name=\"Bou blvd.\"";
+}
+
+node[oneway] {
+  throwWarning: tr("oneway tag on a node");
+  assertMatch: "node oneway=-1";
+  assertNoMatch: "way oneway=-1";
+}
+
+node[bridge?] {
+  throwWarning: tr("bridge tag on a node");
+  assertMatch: "node bridge=yes";
+  assertNoMatch: "node bridge=13";
+}
+
+node[highway =~ /motorway*|trunk*|primary*|secondary*|tertiary*|unclassified|residential|service|living_street|pedestrian|track|path|footway/] {
+  throwWarning: tr("wrong highway tag on a node");
+  assertMatch: "node highway=primary";
+  assertMatch: "node highway=primary_link";
+  assertNoMatch: "node highway=crossing";
+}
+
+way[highway=crossing], way[railway=crossing] {
+  throwWarning: tr("wrong crossing tag on a way");
+  assertMatch: "way highway=crossing";
+  assertNoMatch: "node highway=crossing";
+}
+
+way[highway=unclassified][!name] {
+  throwOther: tr("Unnamed unclassified highway");
+  assertMatch: "way highway=unclassified";
+  assertNoMatch: "way highway=unclassified name=Foo";
+}
+
+way[highway =~ /motorway|trunk|primary|secondary|tertiary/][!ref] {
+  throwOther: tr("highway without a reference");
+  assertMatch: "way highway=primary";
+  assertNoMatch: "way highway=primary ref=123";
+}
+
+way[highway=road] {
+  throwWarning: tr("temporary highway type");
+  assertMatch: "way highway=road";
+  assertNoMatch: "way highway=residential";
+}
+
+way[highway=cycleway][bicycle?!] {
+  throwWarning: tr("cycleway with tag bicycle");
+  assertMatch: "way highway=cycleway bicycle=false";
+  assertMatch: "way highway=cycleway bicycle=0";
+  assertNoMatch: "way highway=cycleway bicycle=yes";
+  assertNoMatch: "way highway=cycleway";
+}
+
+way[highway=footway][foot?!] {
+  throwWarning: tr("footway with tag foot");
+  assertMatch: "way highway=footway foot=false";
+  assertMatch: "way highway=footway foot=0";
+  assertNoMatch: "way highway=footway foot=yes";
+  assertNoMatch: "way highway=footway";
+}
+
+way[highway=cycleway][cycleway=lane] {
+  throwWarning: tr("separate cycleway as lane on a cycleway");
+  assertMatch: "way highway=cycleway cycleway=lane";
+  assertNoMatch: "way highway=cycleway";
+  assertNoMatch: "way highway=residential cycleway=lane";
+}
+
+way[highway][barrier] {
+  throwWarning: tr("barrier used on a way");
+  assertMatch: "way highway=residential barrier=hedge";
+  assertNoMatch: "way highway=residential";
+  assertNoMatch: "way barrier=hedge";
+}
+
+way[highway=footway][maxspeed], way[highway=steps][maxspeed] {
+  throwWarning: tr("maxspeed used for footway");
+  assertMatch: "way highway=footway maxspeed=20";
+  assertNoMatch: "way highway=residential maxspeed=20";
+  assertNoMatch: "way highway=footway";
+}
+
+*[name =~ /(?i).*Strasse.*/] {
+  throwWarning: tr("street name contains ss");
+  assertMatch: "way name=Foobarstrasse";
+  assertMatch: "way name=Foobar-Strassenweg";
+  assertNoMatch: "way name=Foobarstraße";
+}
+
+/*
+TODO: * : W : / *name * /i == * && name != * # misspelled key name
+*/
Index: /trunk/data/validator/tagchecker.cfg
===================================================================
--- /trunk/data/validator/tagchecker.cfg	(revision 6512)
+++ /trunk/data/validator/tagchecker.cfg	(revision 6513)
@@ -37,13 +37,4 @@
 # Empty lines and space signs are ignored
 
-way  : W : highway == * && name == /.* (Ave|Blvd|Br|Brg|Cct|Cir|Cl|Cr|Crct|Cres|Crt|Ct|Dr|Drv|Esp|Espl|Hwy|Ln|Mw|Mwy|Pl|Rd|Qy|Qys|Sq|St|Str|Ter|Tce|Tr|Wy)\.?$/i               # abbreviated street name
-
-node : W : oneway == *                                                         # oneway tag on a node
-node : W : bridge == BOOLEAN_TRUE                                              # bridge tag on a node
-node : W : highway == /motorway*|trunk*|primary*|secondary*|tertiary*|unclassified|residential|service|living_street|pedestrian|track|path|footway/  # wrong highway tag on a node
-way  : W : /highway|railway/ == crossing                                       # wrong crossing tag on a way
-way  : I : highway == unclassified && name != *                                # Unnamed unclassified highway
-way  : I : highway == /motorway|trunk|primary|secondary|tertiary/ && ref != *  # highway without a reference
-*    : W : highway == road                                                     # temporary highway type
 *    : W : / *name */i == * && name != *                                       # misspelled key name
 
@@ -52,21 +43,6 @@
 #way  : W : highway == /motorway|trunk|primary|secondary|tertiary|residential|pedestrian/ && /name|ref|(name:.*)|(.*_name)|(.*_ref)/ != * # Unnamed 
 
-way  : W : highway == cycleway && bicycle == BOOLEAN_FALSE     # cycleway with tag bicycle
-way  : W : highway == footway && foot == BOOLEAN_FALSE         # footway with tag foot
-#way  : I : highway == cycleway && bicycle == *                 # cycleway with tag bicycle
-#way  : I : highway == footway && foot == *                     # footway with tag foot
-way  : W : highway == cycleway && cycleway == lane             # separate cycleway as lane on a cycleway
-way  : W : highway == * && barrier == *                        # barrier used on a way
-
-#way  : I : waterway == * && layer != *                         # waterway without layer tag
-way  : I : highway == footway && maxspeed == *                 # maxspeed used for footway
-way  : I : highway == steps && maxspeed == *                   # maxspeed used for footway
-
 # see #5844, #6760
 #way  : W : oneway != BOOLEAN_FALSE && /.*:(backward|forward)/ == *    # oneway combined with *:backward/forward
-
-*    : W : layer == /\+.*/                                     # layer tag with + sign
-
-*    : I : name == /.*Strasse.*/i                              # street name contains ss
 
 relation : E : type != *                                       # relation without type
@@ -217,4 +193,5 @@
 
 # measurement values and units warnings (ticket #8687)
+*   : W : layer == /\+.*/                                     # layer tag with + sign
 way : W : layer == * && layer != /^0$|^-?[1-5]$/                                                                         # layer should be between -5 and 5
 *   : W : level == * && level != /^((([0-9]|-[1-9])|[1-9][0-9]*)(\.5)?)(;(([0-9]|-[1-9])|[1-9][0-9]*)(\.5)?)*$|^-0\.5$/  # level should be numbers with optional .5 increments
Index: /trunk/src/org/openstreetmap/josm/data/osm/Tag.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/Tag.java	(revision 6512)
+++ /trunk/src/org/openstreetmap/josm/data/osm/Tag.java	(revision 6513)
@@ -99,4 +99,10 @@
     }
 
+    /**
+     * This constructs a {@link Tag} by splitting {@code s} on the first equality sign.
+     * @see org.openstreetmap.josm.tools.TextTagParser
+     * @param s the string to convert
+     * @return the constructed tag
+     */
     public static Tag ofString(String s) {
         CheckParameterUtil.ensureParameterNotNull(s, "s");
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 6512)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 6513)
@@ -229,4 +229,5 @@
     public void initialize() throws Exception {
         addMapCSS(new InputStreamReader(getClass().getResourceAsStream("/data/validator/deprecated.mapcss"), "UTF-8"));
+        addMapCSS(new InputStreamReader(getClass().getResourceAsStream("/data/validator/highway.mapcss"), "UTF-8"));
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java	(revision 6512)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java	(revision 6513)
@@ -38,10 +38,10 @@
     }
 
-    public static Condition create(String k, boolean not, boolean yes, Context context) {
+    public static Condition create(String k, boolean not, boolean yes, boolean no, Context context) {
         switch (context) {
         case PRIMITIVE:
-            return new KeyCondition(k, not, yes);
+            return new KeyCondition(k, not, yes, no);
         case LINK:
-            if (yes)
+            if (yes || no)
                 throw new MapCSSException("Question mark operator ''?'' not supported in LINK context");
             if (not)
@@ -216,4 +216,7 @@
      *     ["a label"?]  PRIMITIVE:  the primitive has a tag "a label" whose value evaluates to a true-value
      *                   LINK:       not supported
+     *
+     *     ["a label"?!] PRIMITIVE:  the primitive has a tag "a label" whose value evaluates to a false-value
+     *                   LINK:       not supported
      * </pre>
      */
@@ -221,17 +224,13 @@
 
         private String label;
-        private boolean exclamationMarkPresent;
-        private boolean questionMarkPresent;
-
-        /**
-         *
-         * @param label
-         * @param exclamationMarkPresent
-         * @param questionMarkPresent
-         */
-        public KeyCondition(String label, boolean exclamationMarkPresent, boolean questionMarkPresent){
+        private boolean negateResult;
+        private boolean testForTrueValues;
+        private boolean testForFalseValues;
+
+        public KeyCondition(String label, boolean negateResult, boolean testForTrueValues, boolean testForFalseValues){
             this.label = label;
-            this.exclamationMarkPresent = exclamationMarkPresent;
-            this.questionMarkPresent = questionMarkPresent;
+            this.negateResult = negateResult;
+            this.testForTrueValues = testForTrueValues;
+            this.testForFalseValues = testForFalseValues;
         }
 
@@ -240,8 +239,10 @@
             switch(e.getContext()) {
             case PRIMITIVE:
-                if (questionMarkPresent)
-                    return OsmUtils.isTrue(e.osm.get(label)) ^ exclamationMarkPresent;
+                if (testForTrueValues)
+                    return OsmUtils.isTrue(e.osm.get(label)) ^ negateResult;
+                else if (testForFalseValues)
+                    return OsmUtils.isFalse(e.osm.get(label)) ^ negateResult;
                 else
-                    return e.osm.hasKey(label) ^ exclamationMarkPresent;
+                    return e.osm.hasKey(label) ^ negateResult;
             case LINK:
                 Utils.ensure(false, "Illegal state: KeyCondition not supported in LINK context");
@@ -253,5 +254,5 @@
         @Override
         public String toString() {
-            return "[" + (exclamationMarkPresent ? "!" : "") + label + "]";
+            return "[" + (negateResult ? "!" : "") + label + "]";
         }
     }
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(revision 6512)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(revision 6513)
@@ -345,4 +345,5 @@
     boolean not = false;
     boolean yes = false;
+    boolean no = false;
     String key;
 }
@@ -350,6 +351,7 @@
     ( <EXCLAMATION> { not = true; } )?
     key=tag_key()
+    ( LOOKAHEAD(2) <QUESTION> <EXCLAMATION> { no = true; } )?
     ( <QUESTION> { yes = true; } )?
-    { return Condition.create(key, not, yes, context); }
+    { return Condition.create(key, not, yes, no, context); }
 }
 
Index: /trunk/test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java	(revision 6512)
+++ /trunk/test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java	(revision 6513)
@@ -10,4 +10,5 @@
 import org.openstreetmap.josm.data.osm.Tag;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.tools.TextTagParser;
 
 import java.io.StringReader;
@@ -55,5 +56,5 @@
 
     OsmPrimitive createPrimitiveForAssertion(String assertion) {
-        final String[] x = assertion.split("\\s+");
+        final String[] x = assertion.split("\\s+", 2);
         final OsmPrimitive p = "n".equals(x[0]) || "node".equals(x[0])
                 ? new Node()
@@ -66,7 +67,6 @@
             throw new IllegalArgumentException("Expecting n/node/w/way/r/relation, but got " + x[0]);
         }
-        for (int i = 1; i < x.length; i++) {
-            final Tag tag = Tag.ofString(x[i]);
-            p.put(tag.getKey(), tag.getValue());
+        for (final Map.Entry<String, String> i : TextTagParser.getValidatedTagsFromText(x[1]).entrySet()) {
+            p.put(i.getKey(), i.getValue());
         }
         return p;
@@ -95,6 +95,7 @@
         for (final MapCSSTagChecker.TagCheck check : c.checks) {
             for (final Map.Entry<String, Boolean> i : check.assertions.entrySet()) {
-                if (check.matchesPrimitive(createPrimitiveForAssertion(i.getKey())) != i.getValue()) {
-                    final String error = "Expecting test '" + check.getMessage() + "' to " + (i.getValue() ? "" : "not ") + "match " + i.getKey();
+                final OsmPrimitive p = createPrimitiveForAssertion(i.getKey());
+                if (check.matchesPrimitive(p) != i.getValue()) {
+                    final String error = "Expecting test '" + check.getMessage() + "' to " + (i.getValue() ? "" : "not ") + "match " + i.getKey() + ", i.e., " + p.getKeys();
                     System.err.println(error);
                     assertionErrors.add(error);
