Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java	(revision 6559)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java	(revision 6560)
@@ -22,5 +22,5 @@
     abstract public boolean applies(Environment e);
 
-    public static Condition create(String k, String v, Op op, Context context, boolean considerValAsKey) {
+    public static Condition createKeyValueCondition(String k, String v, Op op, Context context, boolean considerValAsKey) {
         switch (context) {
         case PRIMITIVE:
@@ -41,5 +41,5 @@
     }
 
-    public static Condition create(String k, boolean not, KeyMatchType matchType, Context context) {
+    public static Condition createKeyCondition(String k, boolean not, KeyMatchType matchType, Context context) {
         switch (context) {
         case PRIMITIVE:
@@ -57,9 +57,13 @@
     }
 
-    public static Condition create(String id, boolean not, Context context) {
+    public static Condition createPseudoClassCondition(String id, boolean not, Context context) {
         return new PseudoClassCondition(id, not);
     }
 
-    public static Condition create(Expression e, Context context) {
+    public static Condition createClassCondition(String id, boolean not, Context context) {
+        return new ClassCondition(id, not);
+    }
+
+    public static Condition createExpressionCondition(Expression e, Context context) {
         return new ExpressionCondition(e);
     }
@@ -275,4 +279,25 @@
     }
 
+    public static class ClassCondition extends Condition {
+
+        public final String id;
+        public final boolean not;
+
+        public ClassCondition(String id, boolean not) {
+            this.id = id;
+            this.not = not;
+        }
+
+        @Override
+        public boolean applies(Environment env) {
+            return not ^ env.mc.getCascade(env.layer).containsKey(id);
+        }
+
+        @Override
+        public String toString() {
+            return (not ? "!" : "") + "." + id;
+        }
+    }
+
     public static class PseudoClassCondition extends Condition {
 
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java	(revision 6559)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java	(revision 6560)
@@ -84,4 +84,9 @@
         Environment env;
 
+        /**
+         * Identity function for compatibility with MapCSS specification.
+         * @param o any object
+         * @return {@code o} unchanged
+         */
         public static Object eval(Object o) {
             return o;
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(revision 6559)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(revision 6560)
@@ -42,5 +42,6 @@
 TOKEN:
 {
-    < IDENT: ["a"-"z","A"-"Z","_"] ( ["a"-"z","A"-"Z","_","-","0"-"9"] )* >
+    < SET: ("set" | "SET") >
+|   < IDENT: ["a"-"z","A"-"Z","_"] ( ["a"-"z","A"-"Z","_","-","0"-"9"] )* >
 |   < UINT: ["1"-"9"] ( ["0"-"9"] )* >
 |   < UFLOAT: ( ["0"-"9"] )+ ( "." ( ["0"-"9"] )+ )? >
@@ -78,4 +79,5 @@
 |   < DOLLAR: "$" >
 |   < CARET: "^" >
+|   < FULLSTOP: "." >
 |   < COMMENT_START: "/*" > : COMMENT
 |   < UNEXPECTED_CHAR : ~[] > // avoid TokenMgrErrors because they are hard to recover from
@@ -271,5 +273,5 @@
     (
         ( <GREATER> { parentSelector = false; } | <LESS> { parentSelector = true; } ) 
-        ( ( c=condition(Context.LINK) | c=pseudoclass(Context.LINK) ) { conditions.add(c); } )*
+        ( ( c=condition(Context.LINK) | c=class_or_pseudoclass(Context.LINK) ) { conditions.add(c); } )*
         { selLink = new LinkSelector(conditions); }
         w()
@@ -290,5 +292,5 @@
     ( base=<IDENT> | base=<STAR> )
     ( r=zoom() )?
-    ( ( c=condition(Context.PRIMITIVE) | c=pseudoclass(Context.PRIMITIVE) ) { conditions.add(c); } )*
+    ( ( c=condition(Context.PRIMITIVE) | c=class_or_pseudoclass(Context.PRIMITIVE) ) { conditions.add(c); } )*
     ( sub=subpart() )?
     { return new GeneralSelector(base.image, r, conditions, sub); }
@@ -327,5 +329,5 @@
             c=simple_key_value_condition(context) s() <RSQUARE> { return c; }
         |
-            e=expression() <RSQUARE> { return Condition.create(e, context); }
+            e=expression() <RSQUARE> { return Condition.createExpressionCondition(e, context); }
     )
 }
@@ -357,5 +359,5 @@
     ( LOOKAHEAD(2) <QUESTION> <EXCLAMATION> { matchType = Condition.KeyMatchType.FALSE; } )?
     (              <QUESTION>               { matchType = Condition.KeyMatchType.TRUE;  } )?
-    { return Condition.create(key, not, matchType, context); }
+    { return Condition.createKeyCondition(key, not, matchType, context); }
 }
 
@@ -418,17 +420,24 @@
             f=float_() { val=Float.toString(f); }
     )
-    { return Condition.create(key, val, op, context, considerValAsKey); }
-}
-
-Condition pseudoclass(Context context) :
+    { return Condition.createKeyValueCondition(key, val, op, context, considerValAsKey); }
+}
+
+Condition class_or_pseudoclass(Context context) :
 {
     Token t;
     boolean not = false;
+    boolean pseudo;
 }
 {
     ( <EXCLAMATION> { not = true; } )? 
-    <COLON>
+    (
+        <FULLSTOP> { pseudo = false; }
+    |
+        <COLON> { pseudo = true; }
+    )
     t=<IDENT>
-    { return Condition.create(t.image, not, context); }
+    { return pseudo
+        ? Condition.createPseudoClassCondition(t.image, not, context)
+        : Condition.createClassCondition(t.image, not, context); }
 }
 
@@ -448,9 +457,16 @@
     Instruction i;
     Token key;
-    Object val;
+    Object val = null;
 }
 {
     <LBRACE> w()
     (
+        (
+        <SET> w() key=<IDENT> w()
+        ( <EQUAL> val=expression() )?
+        { ins.add(new Instruction.AssignmentInstruction(key.image, val == null ? true : val)); }
+        ( <RBRACE> { return ins; } | <SEMICOLON> w() )
+        )
+        |
         key=<IDENT> w() <COLON> w()
         (
Index: /trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.groovy
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.groovy	(revision 6559)
+++ /trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.groovy	(revision 6560)
@@ -8,4 +8,5 @@
 import org.openstreetmap.josm.data.osm.Way
 import org.openstreetmap.josm.gui.mappaint.Environment
+import org.openstreetmap.josm.gui.mappaint.MultiCascade
 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser
 
@@ -22,4 +23,8 @@
     }
 
+    protected static MapCSSParser getParser(String stringToParse) {
+        return new MapCSSParser(new StringReader(stringToParse));
+    }
+
     @Before
     public void setUp() throws Exception {
@@ -28,6 +33,51 @@
 
     @Test
+    public void testKothicStylesheets() throws Exception {
+        new MapCSSParser(new URL("http://kothic.googlecode.com/hg/src/styles/default.mapcss").openStream(), "UTF-8")
+        new MapCSSParser(new URL("http://kothic.googlecode.com/hg/src/styles/mapink.mapcss").openStream(), "UTF-8")
+    }
+
+    @Test
+    public void testDeclarations() {
+        getParser("{ opacity: 0.5; color: rgb(1.0, 0.0, 0.0); }").declaration()
+        getParser("{ set tag=value; }").declaration() //set a tag
+        getParser("{ set tag; }").declaration() // set a tag to 'yes'
+        getParser("{ opacity: eval(\"tag('population')/100000\"); }").declaration()
+        getParser("{ set width_in_metres=eval(\"tag('lanes')*3\"); }").declaration()
+    }
+
+    @Test
+    public void testClassCondition() throws Exception {
+        def conditions = ((Selector.GeneralSelector) getParser("way[name=X].highway:closed").selector()).conds
+        assert conditions.get(0) instanceof Condition.KeyValueCondition
+        assert conditions.get(0).applies(getEnvironment("name", "X"))
+        assert conditions.get(1) instanceof Condition.ClassCondition
+        assert conditions.get(2) instanceof Condition.PseudoClassCondition
+    }
+
+    @Test
+    public void testClassMatching() throws Exception {
+        def css = new MapCSSStyleSource("")
+        getParser("" +
+                "way[highway=footway] { set path; color: #FF6644; width: 2; }\n" +
+                "way[highway=path]    { set path; color: brown; width: 2; }\n" +
+                "way[\"set\"=escape]  {  }\n" +
+                "way.path             { text:auto; text-color: green; text-position: line; text-offset: 5; }\n" +
+                "way!.path            { color: orange; }\n"
+        ).sheet(css)
+        assert css.getErrors().isEmpty()
+        def mc1 = new MultiCascade()
+        css.apply(mc1, getPrimitive("highway", "path"), 1, null, false);
+        assert "green".equals(mc1.getCascade("default").get("text-color", null, String.class))
+        assert "brown".equals(mc1.getCascade("default").get("color", null, String.class))
+        def mc2 = new MultiCascade()
+        css.apply(mc2, getPrimitive("highway", "residential"), 1, null, false);
+        assert "orange".equals(mc2.getCascade("default").get("color", null, String.class))
+        assert mc2.getCascade("default").get("text-color", null, String.class) == null
+    }
+
+    @Test
     public void testEqualCondition() throws Exception {
-        def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface=paved]")).condition(Condition.Context.PRIMITIVE)
+        def condition = (Condition.KeyValueCondition) getParser("[surface=paved]").condition(Condition.Context.PRIMITIVE)
         assert condition instanceof Condition.KeyValueCondition
         assert Condition.Op.EQ.equals(condition.op)
@@ -40,5 +90,5 @@
     @Test
     public void testNotEqualCondition() throws Exception {
-        def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface!=paved]")).condition(Condition.Context.PRIMITIVE)
+        def condition = (Condition.KeyValueCondition) getParser("[surface!=paved]").condition(Condition.Context.PRIMITIVE)
         assert Condition.Op.NEQ.equals(condition.op)
         assert !condition.applies(getEnvironment("surface", "paved"))
@@ -48,5 +98,5 @@
     @Test
     public void testRegexCondition() throws Exception {
-        def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface=~/paved|unpaved/]")).condition(Condition.Context.PRIMITIVE)
+        def condition = (Condition.KeyValueCondition) getParser("[surface=~/paved|unpaved/]").condition(Condition.Context.PRIMITIVE)
         assert Condition.Op.REGEX.equals(condition.op)
         assert condition.applies(getEnvironment("surface", "unpaved"))
@@ -56,5 +106,5 @@
     @Test
     public void testNegatedRegexCondition() throws Exception {
-        def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface!~/paved|unpaved/]")).condition(Condition.Context.PRIMITIVE)
+        def condition = (Condition.KeyValueCondition) getParser("[surface!~/paved|unpaved/]").condition(Condition.Context.PRIMITIVE)
         assert Condition.Op.NREGEX.equals(condition.op)
         assert !condition.applies(getEnvironment("surface", "unpaved"))
@@ -64,9 +114,9 @@
     @Test
     public void testStandardKeyCondition() throws Exception {
-        def c1 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[ highway ]")).condition(Condition.Context.PRIMITIVE)
+        def c1 = (Condition.KeyCondition) getParser("[ 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)
+        def c2 = (Condition.KeyCondition) getParser("[\"/slash/\"]").condition(Condition.Context.PRIMITIVE)
         assert c2.matchType == null
         assert c2.applies(getEnvironment("/slash/", "yes"))
@@ -76,8 +126,8 @@
     @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 c1 = (Condition.KeyCondition) getParser("[oneway?]").condition(Condition.Context.PRIMITIVE)
+        def c2 = (Condition.KeyCondition) getParser("[oneway?!]").condition(Condition.Context.PRIMITIVE)
+        def c3 = (Condition.KeyCondition) getParser("[!oneway?]").condition(Condition.Context.PRIMITIVE)
+        def c4 = (Condition.KeyCondition) getParser("[!oneway?!]").condition(Condition.Context.PRIMITIVE)
         def yes = getEnvironment("oneway", "yes")
         def no = getEnvironment("oneway", "no")
@@ -99,5 +149,5 @@
     @Test
     public void testRegexKeyCondition() throws Exception {
-        def c1 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[/.*:(backward|forward)\$/]")).condition(Condition.Context.PRIMITIVE)
+        def c1 = (Condition.KeyCondition) getParser("[/.*:(backward|forward)\$/]").condition(Condition.Context.PRIMITIVE)
         assert Condition.KeyMatchType.REGEX.equals(c1.matchType)
         assert !c1.applies(getEnvironment("lanes", "3"))
@@ -109,5 +159,5 @@
     @Test
     public void testKeyKeyCondition() throws Exception {
-        def c1 = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[foo = *bar]")).condition(Condition.Context.PRIMITIVE)
+        def c1 = (Condition.KeyValueCondition) getParser("[foo = *bar]").condition(Condition.Context.PRIMITIVE)
         def w1 = new Way()
         w1.put("foo", "123")
@@ -116,5 +166,5 @@
         w1.put("bar", "123")
         assert c1.applies(new Environment().withPrimitive(w1))
-        def c2 = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[foo =~ */bar/]")).condition(Condition.Context.PRIMITIVE)
+        def c2 = (Condition.KeyValueCondition) getParser("[foo =~ */bar/]").condition(Condition.Context.PRIMITIVE)
         def w2 = new Way(w1)
         w2.put("bar", "[0-9]{3}")
