Changeset 6560 in josm for trunk


Ignore:
Timestamp:
2013-12-29T12:03:21+01:00 (11 years ago)
Author:
simon04
Message:

see #9485 - MapCSS: add support for set class_name instruction and .class_name condition (which is specified in the MapCSS specification)

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java

    r6554 r6560  
    2222    abstract public boolean applies(Environment e);
    2323
    24     public static Condition create(String k, String v, Op op, Context context, boolean considerValAsKey) {
     24    public static Condition createKeyValueCondition(String k, String v, Op op, Context context, boolean considerValAsKey) {
    2525        switch (context) {
    2626        case PRIMITIVE:
     
    4141    }
    4242
    43     public static Condition create(String k, boolean not, KeyMatchType matchType, Context context) {
     43    public static Condition createKeyCondition(String k, boolean not, KeyMatchType matchType, Context context) {
    4444        switch (context) {
    4545        case PRIMITIVE:
     
    5757    }
    5858
    59     public static Condition create(String id, boolean not, Context context) {
     59    public static Condition createPseudoClassCondition(String id, boolean not, Context context) {
    6060        return new PseudoClassCondition(id, not);
    6161    }
    6262
    63     public static Condition create(Expression e, Context context) {
     63    public static Condition createClassCondition(String id, boolean not, Context context) {
     64        return new ClassCondition(id, not);
     65    }
     66
     67    public static Condition createExpressionCondition(Expression e, Context context) {
    6468        return new ExpressionCondition(e);
    6569    }
     
    275279    }
    276280
     281    public static class ClassCondition extends Condition {
     282
     283        public final String id;
     284        public final boolean not;
     285
     286        public ClassCondition(String id, boolean not) {
     287            this.id = id;
     288            this.not = not;
     289        }
     290
     291        @Override
     292        public boolean applies(Environment env) {
     293            return not ^ env.mc.getCascade(env.layer).containsKey(id);
     294        }
     295
     296        @Override
     297        public String toString() {
     298            return (not ? "!" : "") + "." + id;
     299        }
     300    }
     301
    277302    public static class PseudoClassCondition extends Condition {
    278303
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java

    r6538 r6560  
    8484        Environment env;
    8585
     86        /**
     87         * Identity function for compatibility with MapCSS specification.
     88         * @param o any object
     89         * @return {@code o} unchanged
     90         */
    8691        public static Object eval(Object o) {
    8792            return o;
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj

    r6554 r6560  
    4242TOKEN:
    4343{
    44     < IDENT: ["a"-"z","A"-"Z","_"] ( ["a"-"z","A"-"Z","_","-","0"-"9"] )* >
     44    < SET: ("set" | "SET") >
     45|   < IDENT: ["a"-"z","A"-"Z","_"] ( ["a"-"z","A"-"Z","_","-","0"-"9"] )* >
    4546|   < UINT: ["1"-"9"] ( ["0"-"9"] )* >
    4647|   < UFLOAT: ( ["0"-"9"] )+ ( "." ( ["0"-"9"] )+ )? >
     
    7879|   < DOLLAR: "$" >
    7980|   < CARET: "^" >
     81|   < FULLSTOP: "." >
    8082|   < COMMENT_START: "/*" > : COMMENT
    8183|   < UNEXPECTED_CHAR : ~[] > // avoid TokenMgrErrors because they are hard to recover from
     
    271273    (
    272274        ( <GREATER> { parentSelector = false; } | <LESS> { parentSelector = true; } )
    273         ( ( c=condition(Context.LINK) | c=pseudoclass(Context.LINK) ) { conditions.add(c); } )*
     275        ( ( c=condition(Context.LINK) | c=class_or_pseudoclass(Context.LINK) ) { conditions.add(c); } )*
    274276        { selLink = new LinkSelector(conditions); }
    275277        w()
     
    290292    ( base=<IDENT> | base=<STAR> )
    291293    ( r=zoom() )?
    292     ( ( c=condition(Context.PRIMITIVE) | c=pseudoclass(Context.PRIMITIVE) ) { conditions.add(c); } )*
     294    ( ( c=condition(Context.PRIMITIVE) | c=class_or_pseudoclass(Context.PRIMITIVE) ) { conditions.add(c); } )*
    293295    ( sub=subpart() )?
    294296    { return new GeneralSelector(base.image, r, conditions, sub); }
     
    327329            c=simple_key_value_condition(context) s() <RSQUARE> { return c; }
    328330        |
    329             e=expression() <RSQUARE> { return Condition.create(e, context); }
     331            e=expression() <RSQUARE> { return Condition.createExpressionCondition(e, context); }
    330332    )
    331333}
     
    357359    ( LOOKAHEAD(2) <QUESTION> <EXCLAMATION> { matchType = Condition.KeyMatchType.FALSE; } )?
    358360    (              <QUESTION>               { matchType = Condition.KeyMatchType.TRUE;  } )?
    359     { return Condition.create(key, not, matchType, context); }
     361    { return Condition.createKeyCondition(key, not, matchType, context); }
    360362}
    361363
     
    418420            f=float_() { val=Float.toString(f); }
    419421    )
    420     { return Condition.create(key, val, op, context, considerValAsKey); }
    421 }
    422 
    423 Condition pseudoclass(Context context) :
     422    { return Condition.createKeyValueCondition(key, val, op, context, considerValAsKey); }
     423}
     424
     425Condition class_or_pseudoclass(Context context) :
    424426{
    425427    Token t;
    426428    boolean not = false;
     429    boolean pseudo;
    427430}
    428431{
    429432    ( <EXCLAMATION> { not = true; } )?
    430     <COLON>
     433    (
     434        <FULLSTOP> { pseudo = false; }
     435    |
     436        <COLON> { pseudo = true; }
     437    )
    431438    t=<IDENT>
    432     { return Condition.create(t.image, not, context); }
     439    { return pseudo
     440        ? Condition.createPseudoClassCondition(t.image, not, context)
     441        : Condition.createClassCondition(t.image, not, context); }
    433442}
    434443
     
    448457    Instruction i;
    449458    Token key;
    450     Object val;
     459    Object val = null;
    451460}
    452461{
    453462    <LBRACE> w()
    454463    (
     464        (
     465        <SET> w() key=<IDENT> w()
     466        ( <EQUAL> val=expression() )?
     467        { ins.add(new Instruction.AssignmentInstruction(key.image, val == null ? true : val)); }
     468        ( <RBRACE> { return ins; } | <SEMICOLON> w() )
     469        )
     470        |
    455471        key=<IDENT> w() <COLON> w()
    456472        (
  • trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.groovy

    r6554 r6560  
    88import org.openstreetmap.josm.data.osm.Way
    99import org.openstreetmap.josm.gui.mappaint.Environment
     10import org.openstreetmap.josm.gui.mappaint.MultiCascade
    1011import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser
    1112
     
    2223    }
    2324
     25    protected static MapCSSParser getParser(String stringToParse) {
     26        return new MapCSSParser(new StringReader(stringToParse));
     27    }
     28
    2429    @Before
    2530    public void setUp() throws Exception {
     
    2833
    2934    @Test
     35    public void testKothicStylesheets() throws Exception {
     36        new MapCSSParser(new URL("http://kothic.googlecode.com/hg/src/styles/default.mapcss").openStream(), "UTF-8")
     37        new MapCSSParser(new URL("http://kothic.googlecode.com/hg/src/styles/mapink.mapcss").openStream(), "UTF-8")
     38    }
     39
     40    @Test
     41    public void testDeclarations() {
     42        getParser("{ opacity: 0.5; color: rgb(1.0, 0.0, 0.0); }").declaration()
     43        getParser("{ set tag=value; }").declaration() //set a tag
     44        getParser("{ set tag; }").declaration() // set a tag to 'yes'
     45        getParser("{ opacity: eval(\"tag('population')/100000\"); }").declaration()
     46        getParser("{ set width_in_metres=eval(\"tag('lanes')*3\"); }").declaration()
     47    }
     48
     49    @Test
     50    public void testClassCondition() throws Exception {
     51        def conditions = ((Selector.GeneralSelector) getParser("way[name=X].highway:closed").selector()).conds
     52        assert conditions.get(0) instanceof Condition.KeyValueCondition
     53        assert conditions.get(0).applies(getEnvironment("name", "X"))
     54        assert conditions.get(1) instanceof Condition.ClassCondition
     55        assert conditions.get(2) instanceof Condition.PseudoClassCondition
     56    }
     57
     58    @Test
     59    public void testClassMatching() throws Exception {
     60        def css = new MapCSSStyleSource("")
     61        getParser("" +
     62                "way[highway=footway] { set path; color: #FF6644; width: 2; }\n" +
     63                "way[highway=path]    { set path; color: brown; width: 2; }\n" +
     64                "way[\"set\"=escape]  {  }\n" +
     65                "way.path             { text:auto; text-color: green; text-position: line; text-offset: 5; }\n" +
     66                "way!.path            { color: orange; }\n"
     67        ).sheet(css)
     68        assert css.getErrors().isEmpty()
     69        def mc1 = new MultiCascade()
     70        css.apply(mc1, getPrimitive("highway", "path"), 1, null, false);
     71        assert "green".equals(mc1.getCascade("default").get("text-color", null, String.class))
     72        assert "brown".equals(mc1.getCascade("default").get("color", null, String.class))
     73        def mc2 = new MultiCascade()
     74        css.apply(mc2, getPrimitive("highway", "residential"), 1, null, false);
     75        assert "orange".equals(mc2.getCascade("default").get("color", null, String.class))
     76        assert mc2.getCascade("default").get("text-color", null, String.class) == null
     77    }
     78
     79    @Test
    3080    public void testEqualCondition() throws Exception {
    31         def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface=paved]")).condition(Condition.Context.PRIMITIVE)
     81        def condition = (Condition.KeyValueCondition) getParser("[surface=paved]").condition(Condition.Context.PRIMITIVE)
    3282        assert condition instanceof Condition.KeyValueCondition
    3383        assert Condition.Op.EQ.equals(condition.op)
     
    4090    @Test
    4191    public void testNotEqualCondition() throws Exception {
    42         def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface!=paved]")).condition(Condition.Context.PRIMITIVE)
     92        def condition = (Condition.KeyValueCondition) getParser("[surface!=paved]").condition(Condition.Context.PRIMITIVE)
    4393        assert Condition.Op.NEQ.equals(condition.op)
    4494        assert !condition.applies(getEnvironment("surface", "paved"))
     
    4898    @Test
    4999    public void testRegexCondition() throws Exception {
    50         def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface=~/paved|unpaved/]")).condition(Condition.Context.PRIMITIVE)
     100        def condition = (Condition.KeyValueCondition) getParser("[surface=~/paved|unpaved/]").condition(Condition.Context.PRIMITIVE)
    51101        assert Condition.Op.REGEX.equals(condition.op)
    52102        assert condition.applies(getEnvironment("surface", "unpaved"))
     
    56106    @Test
    57107    public void testNegatedRegexCondition() throws Exception {
    58         def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface!~/paved|unpaved/]")).condition(Condition.Context.PRIMITIVE)
     108        def condition = (Condition.KeyValueCondition) getParser("[surface!~/paved|unpaved/]").condition(Condition.Context.PRIMITIVE)
    59109        assert Condition.Op.NREGEX.equals(condition.op)
    60110        assert !condition.applies(getEnvironment("surface", "unpaved"))
     
    64114    @Test
    65115    public void testStandardKeyCondition() throws Exception {
    66         def c1 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[ highway ]")).condition(Condition.Context.PRIMITIVE)
     116        def c1 = (Condition.KeyCondition) getParser("[ highway ]").condition(Condition.Context.PRIMITIVE)
    67117        assert c1.matchType == null
    68118        assert c1.applies(getEnvironment("highway", "unclassified"))
    69119        assert !c1.applies(getEnvironment("railway", "rail"))
    70         def c2 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[\"/slash/\"]")).condition(Condition.Context.PRIMITIVE)
     120        def c2 = (Condition.KeyCondition) getParser("[\"/slash/\"]").condition(Condition.Context.PRIMITIVE)
    71121        assert c2.matchType == null
    72122        assert c2.applies(getEnvironment("/slash/", "yes"))
     
    76126    @Test
    77127    public void testYesNoKeyCondition() throws Exception {
    78         def c1 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[oneway?]")).condition(Condition.Context.PRIMITIVE)
    79         def c2 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[oneway?!]")).condition(Condition.Context.PRIMITIVE)
    80         def c3 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[!oneway?]")).condition(Condition.Context.PRIMITIVE)
    81         def c4 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[!oneway?!]")).condition(Condition.Context.PRIMITIVE)
     128        def c1 = (Condition.KeyCondition) getParser("[oneway?]").condition(Condition.Context.PRIMITIVE)
     129        def c2 = (Condition.KeyCondition) getParser("[oneway?!]").condition(Condition.Context.PRIMITIVE)
     130        def c3 = (Condition.KeyCondition) getParser("[!oneway?]").condition(Condition.Context.PRIMITIVE)
     131        def c4 = (Condition.KeyCondition) getParser("[!oneway?!]").condition(Condition.Context.PRIMITIVE)
    82132        def yes = getEnvironment("oneway", "yes")
    83133        def no = getEnvironment("oneway", "no")
     
    99149    @Test
    100150    public void testRegexKeyCondition() throws Exception {
    101         def c1 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[/.*:(backward|forward)\$/]")).condition(Condition.Context.PRIMITIVE)
     151        def c1 = (Condition.KeyCondition) getParser("[/.*:(backward|forward)\$/]").condition(Condition.Context.PRIMITIVE)
    102152        assert Condition.KeyMatchType.REGEX.equals(c1.matchType)
    103153        assert !c1.applies(getEnvironment("lanes", "3"))
     
    109159    @Test
    110160    public void testKeyKeyCondition() throws Exception {
    111         def c1 = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[foo = *bar]")).condition(Condition.Context.PRIMITIVE)
     161        def c1 = (Condition.KeyValueCondition) getParser("[foo = *bar]").condition(Condition.Context.PRIMITIVE)
    112162        def w1 = new Way()
    113163        w1.put("foo", "123")
     
    116166        w1.put("bar", "123")
    117167        assert c1.applies(new Environment().withPrimitive(w1))
    118         def c2 = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[foo =~ */bar/]")).condition(Condition.Context.PRIMITIVE)
     168        def c2 = (Condition.KeyValueCondition) getParser("[foo =~ */bar/]").condition(Condition.Context.PRIMITIVE)
    119169        def w2 = new Way(w1)
    120170        w2.put("bar", "[0-9]{3}")
Note: See TracChangeset for help on using the changeset viewer.