Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java	(revision 6546)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java	(revision 6547)
@@ -6,5 +6,4 @@
 import java.text.MessageFormat;
 import java.util.EnumSet;
-import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -16,4 +15,5 @@
 import org.openstreetmap.josm.gui.mappaint.Cascade;
 import org.openstreetmap.josm.gui.mappaint.Environment;
+import org.openstreetmap.josm.tools.Predicates;
 import org.openstreetmap.josm.tools.Utils;
 
@@ -39,11 +39,11 @@
     }
 
-    public static Condition create(String k, boolean not, boolean yes, boolean no, Context context) {
+    public static Condition create(String k, boolean not, KeyMatchType matchType, Context context) {
         switch (context) {
         case PRIMITIVE:
-            return new KeyCondition(k, not, yes, no);
+            return new KeyCondition(k, not, matchType);
         case LINK:
-            if (yes || no)
-                throw new MapCSSException("Question mark operator ''?'' not supported in LINK context");
+            if (matchType != null)
+                throw new MapCSSException("Question mark operator ''?'' and regexp match not supported in LINK context");
             if (not)
                 return new RoleCondition(k, Op.NEQ);
@@ -77,7 +77,6 @@
             case REGEX:
             case NREGEX:
-                Pattern p = Pattern.compile(prototypeString);
-                Matcher m = p.matcher(testString);
-                return REGEX.equals(this) ? m.find() : !m.find();
+                final boolean contains = Pattern.compile(prototypeString).matcher(testString).find();
+                return REGEX.equals(this) ? contains : !contains;
             case ONE_OF:
                 String[] parts = testString.split(";");
@@ -142,7 +141,7 @@
     public static class KeyValueCondition extends Condition {
 
-        public String k;
-        public String v;
-        public Op op;
+        public final String k;
+        public final String v;
+        public final Op op;
 
         /**
@@ -175,6 +174,6 @@
 
     public static class RoleCondition extends Condition {
-        public String role;
-        public Op op;
+        public final String role;
+        public final Op op;
 
         public RoleCondition(String role, Op op) {
@@ -192,6 +191,6 @@
 
     public static class IndexCondition extends Condition {
-        public String index;
-        public Op op;
+        public final String index;
+        public final Op op;
 
         public IndexCondition(String index, Op op) {
@@ -205,4 +204,8 @@
             return op.eval(Integer.toString(env.index + 1), index);
         }
+    }
+
+    public static enum KeyMatchType {
+        EQ, TRUE, FALSE, REGEX
     }
 
@@ -228,14 +231,12 @@
     public static class KeyCondition extends Condition {
 
-        private String label;
-        private boolean negateResult;
-        private boolean testForTrueValues;
-        private boolean testForFalseValues;
-
-        public KeyCondition(String label, boolean negateResult, boolean testForTrueValues, boolean testForFalseValues){
+        public final String label;
+        public final boolean negateResult;
+        public final KeyMatchType matchType;
+
+        public KeyCondition(String label, boolean negateResult, KeyMatchType matchType){
             this.label = label;
             this.negateResult = negateResult;
-            this.testForTrueValues = testForTrueValues;
-            this.testForFalseValues = testForFalseValues;
+            this.matchType = matchType;
         }
 
@@ -244,8 +245,10 @@
             switch(e.getContext()) {
             case PRIMITIVE:
-                if (testForTrueValues)
+                if (KeyMatchType.TRUE.equals(matchType))
                     return OsmUtils.isTrue(e.osm.get(label)) ^ negateResult;
-                else if (testForFalseValues)
+                else if (KeyMatchType.FALSE.equals(matchType))
                     return OsmUtils.isFalse(e.osm.get(label)) ^ negateResult;
+                else if (KeyMatchType.REGEX.equals(matchType))
+                    return Utils.exists(e.osm.keySet(), Predicates.stringContainsPattern(Pattern.compile(label))) ^ negateResult;
                 else
                     return e.osm.hasKey(label) ^ negateResult;
@@ -269,6 +272,6 @@
     public static class PseudoClassCondition extends Condition {
 
-        String id;
-        boolean not;
+        public final String id;
+        public final boolean not;
 
         public PseudoClassCondition(String id, boolean not) {
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(revision 6546)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(revision 6547)
@@ -345,14 +345,17 @@
 {
     boolean not = false;
-    boolean yes = false;
-    boolean no = false;
+    Condition.KeyMatchType matchType = null;;
     String key;
 }
 {
     ( <EXCLAMATION> { not = true; } )?
-    key=tag_key()
-    ( LOOKAHEAD(2) <QUESTION> <EXCLAMATION> { no = true; } )?
-    ( <QUESTION> { yes = true; } )?
-    { return Condition.create(key, not, yes, no, context); }
+    (
+        { matchType = Condition.KeyMatchType.REGEX; } key = regex()
+    |
+        key = tag_key()
+    )
+    ( LOOKAHEAD(2) <QUESTION> <EXCLAMATION> { matchType = Condition.KeyMatchType.FALSE; } )?
+    (              <QUESTION>               { matchType = Condition.KeyMatchType.TRUE;  } )?
+    { return Condition.create(key, not, matchType, context); }
 }
 
Index: trunk/src/org/openstreetmap/josm/tools/Predicates.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/Predicates.java	(revision 6547)
+++ trunk/src/org/openstreetmap/josm/tools/Predicates.java	(revision 6547)
@@ -0,0 +1,48 @@
+package org.openstreetmap.josm.tools;
+
+import java.util.regex.Pattern;
+
+/**
+ * Utility class for creating {@link Predicate}s.
+ */
+public final class Predicates {
+
+    private Predicates() {
+    }
+
+    /**
+     * Returns a {@link Predicate} executing {@link Pattern#matcher(CharSequence)} and {@link java.util.regex.Matcher#matches}.
+     */
+    public static Predicate<String> stringMatchesPattern(final Pattern pattern) {
+        return new Predicate<String>() {
+            @Override
+            public boolean evaluate(String string) {
+                return pattern.matcher(string).matches();
+            }
+        };
+    }
+
+    /**
+     * Returns a {@link Predicate} executing {@link Pattern#matcher(CharSequence)} and {@link java.util.regex.Matcher#find}.
+     */
+    public static Predicate<String> stringContainsPattern(final Pattern pattern) {
+        return new Predicate<String>() {
+            @Override
+            public boolean evaluate(String string) {
+                return pattern.matcher(string).find();
+            }
+        };
+    }
+
+    /**
+     * Returns a {@link Predicate} executing {@link String#contains(CharSequence)}.
+     */
+    public static Predicate<String> stringContains(final String pattern) {
+        return new Predicate<String>() {
+            @Override
+            public boolean evaluate(String string) {
+                return string.contains(pattern);
+            }
+        };
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.groovy
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.groovy	(revision 6546)
+++ trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.groovy	(revision 6547)
@@ -1,4 +1,5 @@
 package org.openstreetmap.josm.gui.mappaint.mapcss
 
+import org.junit.Before
 import org.junit.Test
 import org.openstreetmap.josm.Main
@@ -17,4 +18,13 @@
     }
 
+    protected static Environment getEnvironment(String key, String value) {
+        return new Environment().withPrimitive(getPrimitive(key, value))
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        Main.pref = new Preferences()
+    }
+
     @Test
     public void testEqualCondition() throws Exception {
@@ -24,7 +34,6 @@
         assert "surface".equals(condition.k)
         assert "paved".equals(condition.v)
-        Main.pref = new Preferences()
-        assert condition.applies(new Environment().withPrimitive(getPrimitive("surface", "paved")))
-        assert !condition.applies(new Environment().withPrimitive(getPrimitive("surface", "unpaved")))
+        assert condition.applies(getEnvironment("surface", "paved"))
+        assert !condition.applies(getEnvironment("surface", "unpaved"))
     }
 
@@ -32,9 +41,7 @@
     public void testNotEqualCondition() throws Exception {
         def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface!=paved]")).condition(Condition.Context.PRIMITIVE)
-        assert condition instanceof Condition.KeyValueCondition
         assert Condition.Op.NEQ.equals(condition.op)
-        Main.pref = new Preferences()
-        assert !condition.applies(new Environment().withPrimitive(getPrimitive("surface", "paved")))
-        assert condition.applies(new Environment().withPrimitive(getPrimitive("surface", "unpaved")))
+        assert !condition.applies(getEnvironment("surface", "paved"))
+        assert condition.applies(getEnvironment("surface", "unpaved"))
     }
 
@@ -42,9 +49,7 @@
     public void testRegexCondition() throws Exception {
         def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface=~/paved|unpaved/]")).condition(Condition.Context.PRIMITIVE)
-        assert condition instanceof Condition.KeyValueCondition
         assert Condition.Op.REGEX.equals(condition.op)
-        Main.pref = new Preferences()
-        assert condition.applies(new Environment().withPrimitive(getPrimitive("surface", "unpaved")))
-        assert !condition.applies(new Environment().withPrimitive(getPrimitive("surface", "grass")))
+        assert condition.applies(getEnvironment("surface", "unpaved"))
+        assert !condition.applies(getEnvironment("surface", "grass"))
     }
 
@@ -52,9 +57,52 @@
     public void testNegatedRegexCondition() throws Exception {
         def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface!~/paved|unpaved/]")).condition(Condition.Context.PRIMITIVE)
-        assert condition instanceof Condition.KeyValueCondition
         assert Condition.Op.NREGEX.equals(condition.op)
-        Main.pref = new Preferences()
-        assert !condition.applies(new Environment().withPrimitive(getPrimitive("surface", "unpaved")))
-        assert condition.applies(new Environment().withPrimitive(getPrimitive("surface", "grass")))
+        assert !condition.applies(getEnvironment("surface", "unpaved"))
+        assert condition.applies(getEnvironment("surface", "grass"))
+    }
+
+    @Test
+    public void testStandardKeyCondition() throws Exception {
+        def c1 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[ highway ]")).condition(Condition.Context.PRIMITIVE)
+        assert c1.matchType == null
+        assert c1.applies(getEnvironment("highway", "unclassified"))
+        assert !c1.applies(getEnvironment("railway", "rail"))
+        def c2 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[\"/slash/\"]")).condition(Condition.Context.PRIMITIVE)
+        assert c2.matchType == null
+        assert c2.applies(getEnvironment("/slash/", "yes"))
+        assert !c2.applies(getEnvironment("\"slash\"", "no"))
+    }
+
+    @Test
+    public void testYesNoKeyCondition() throws Exception {
+        def c1 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[oneway?]")).condition(Condition.Context.PRIMITIVE)
+        def c2 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[oneway?!]")).condition(Condition.Context.PRIMITIVE)
+        def c3 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[!oneway?]")).condition(Condition.Context.PRIMITIVE)
+        def c4 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[!oneway?!]")).condition(Condition.Context.PRIMITIVE)
+        def yes = getEnvironment("oneway", "yes")
+        def no = getEnvironment("oneway", "no")
+        def none = getEnvironment("no-oneway", "foo")
+        assert c1.applies(yes)
+        assert !c1.applies(no)
+        assert !c1.applies(none)
+        assert !c2.applies(yes)
+        assert c2.applies(no)
+        assert !c2.applies(none)
+        assert !c3.applies(yes)
+        assert c3.applies(no)
+        assert c3.applies(none)
+        assert c4.applies(yes)
+        assert !c4.applies(no)
+        assert c4.applies(none)
+    }
+
+    @Test
+    public void testRegexKeyCondition() throws Exception {
+        def c1 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[/.*:(backward|forward)\$/]")).condition(Condition.Context.PRIMITIVE)
+        assert Condition.KeyMatchType.REGEX.equals(c1.matchType)
+        assert !c1.applies(getEnvironment("lanes", "3"))
+        assert c1.applies(getEnvironment("lanes:forward", "3"))
+        assert c1.applies(getEnvironment("lanes:backward", "3"))
+        assert !c1.applies(getEnvironment("lanes:foobar", "3"))
     }
 }
