Changeset 6547 in josm


Ignore:
Timestamp:
2013-12-27T16:14:28+01:00 (6 years ago)
Author:
simon04
Message:

MapCSS: add regular expression support for key conditions

For instance, [/^addr:/] matches any addr:* key.

Location:
trunk
Files:
1 added
3 edited

Legend:

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

    r6538 r6547  
    66import java.text.MessageFormat;
    77import java.util.EnumSet;
    8 import java.util.regex.Matcher;
    98import java.util.regex.Pattern;
    109
     
    1615import org.openstreetmap.josm.gui.mappaint.Cascade;
    1716import org.openstreetmap.josm.gui.mappaint.Environment;
     17import org.openstreetmap.josm.tools.Predicates;
    1818import org.openstreetmap.josm.tools.Utils;
    1919
     
    3939    }
    4040
    41     public static Condition create(String k, boolean not, boolean yes, boolean no, Context context) {
     41    public static Condition create(String k, boolean not, KeyMatchType matchType, Context context) {
    4242        switch (context) {
    4343        case PRIMITIVE:
    44             return new KeyCondition(k, not, yes, no);
     44            return new KeyCondition(k, not, matchType);
    4545        case LINK:
    46             if (yes || no)
    47                 throw new MapCSSException("Question mark operator ''?'' not supported in LINK context");
     46            if (matchType != null)
     47                throw new MapCSSException("Question mark operator ''?'' and regexp match not supported in LINK context");
    4848            if (not)
    4949                return new RoleCondition(k, Op.NEQ);
     
    7777            case REGEX:
    7878            case NREGEX:
    79                 Pattern p = Pattern.compile(prototypeString);
    80                 Matcher m = p.matcher(testString);
    81                 return REGEX.equals(this) ? m.find() : !m.find();
     79                final boolean contains = Pattern.compile(prototypeString).matcher(testString).find();
     80                return REGEX.equals(this) ? contains : !contains;
    8281            case ONE_OF:
    8382                String[] parts = testString.split(";");
     
    142141    public static class KeyValueCondition extends Condition {
    143142
    144         public String k;
    145         public String v;
    146         public Op op;
     143        public final String k;
     144        public final String v;
     145        public final Op op;
    147146
    148147        /**
     
    175174
    176175    public static class RoleCondition extends Condition {
    177         public String role;
    178         public Op op;
     176        public final String role;
     177        public final Op op;
    179178
    180179        public RoleCondition(String role, Op op) {
     
    192191
    193192    public static class IndexCondition extends Condition {
    194         public String index;
    195         public Op op;
     193        public final String index;
     194        public final Op op;
    196195
    197196        public IndexCondition(String index, Op op) {
     
    205204            return op.eval(Integer.toString(env.index + 1), index);
    206205        }
     206    }
     207
     208    public static enum KeyMatchType {
     209        EQ, TRUE, FALSE, REGEX
    207210    }
    208211
     
    228231    public static class KeyCondition extends Condition {
    229232
    230         private String label;
    231         private boolean negateResult;
    232         private boolean testForTrueValues;
    233         private boolean testForFalseValues;
    234 
    235         public KeyCondition(String label, boolean negateResult, boolean testForTrueValues, boolean testForFalseValues){
     233        public final String label;
     234        public final boolean negateResult;
     235        public final KeyMatchType matchType;
     236
     237        public KeyCondition(String label, boolean negateResult, KeyMatchType matchType){
    236238            this.label = label;
    237239            this.negateResult = negateResult;
    238             this.testForTrueValues = testForTrueValues;
    239             this.testForFalseValues = testForFalseValues;
     240            this.matchType = matchType;
    240241        }
    241242
     
    244245            switch(e.getContext()) {
    245246            case PRIMITIVE:
    246                 if (testForTrueValues)
     247                if (KeyMatchType.TRUE.equals(matchType))
    247248                    return OsmUtils.isTrue(e.osm.get(label)) ^ negateResult;
    248                 else if (testForFalseValues)
     249                else if (KeyMatchType.FALSE.equals(matchType))
    249250                    return OsmUtils.isFalse(e.osm.get(label)) ^ negateResult;
     251                else if (KeyMatchType.REGEX.equals(matchType))
     252                    return Utils.exists(e.osm.keySet(), Predicates.stringContainsPattern(Pattern.compile(label))) ^ negateResult;
    250253                else
    251254                    return e.osm.hasKey(label) ^ negateResult;
     
    269272    public static class PseudoClassCondition extends Condition {
    270273
    271         String id;
    272         boolean not;
     274        public final String id;
     275        public final boolean not;
    273276
    274277        public PseudoClassCondition(String id, boolean not) {
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj

    r6532 r6547  
    345345{
    346346    boolean not = false;
    347     boolean yes = false;
    348     boolean no = false;
     347    Condition.KeyMatchType matchType = null;;
    349348    String key;
    350349}
    351350{
    352351    ( <EXCLAMATION> { not = true; } )?
    353     key=tag_key()
    354     ( LOOKAHEAD(2) <QUESTION> <EXCLAMATION> { no = true; } )?
    355     ( <QUESTION> { yes = true; } )?
    356     { return Condition.create(key, not, yes, no, context); }
     352    (
     353        { matchType = Condition.KeyMatchType.REGEX; } key = regex()
     354    |
     355        key = tag_key()
     356    )
     357    ( LOOKAHEAD(2) <QUESTION> <EXCLAMATION> { matchType = Condition.KeyMatchType.FALSE; } )?
     358    (              <QUESTION>               { matchType = Condition.KeyMatchType.TRUE;  } )?
     359    { return Condition.create(key, not, matchType, context); }
    357360}
    358361
  • trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.groovy

    r6455 r6547  
    11package org.openstreetmap.josm.gui.mappaint.mapcss
    22
     3import org.junit.Before
    34import org.junit.Test
    45import org.openstreetmap.josm.Main
     
    1718    }
    1819
     20    protected static Environment getEnvironment(String key, String value) {
     21        return new Environment().withPrimitive(getPrimitive(key, value))
     22    }
     23
     24    @Before
     25    public void setUp() throws Exception {
     26        Main.pref = new Preferences()
     27    }
     28
    1929    @Test
    2030    public void testEqualCondition() throws Exception {
     
    2434        assert "surface".equals(condition.k)
    2535        assert "paved".equals(condition.v)
    26         Main.pref = new Preferences()
    27         assert condition.applies(new Environment().withPrimitive(getPrimitive("surface", "paved")))
    28         assert !condition.applies(new Environment().withPrimitive(getPrimitive("surface", "unpaved")))
     36        assert condition.applies(getEnvironment("surface", "paved"))
     37        assert !condition.applies(getEnvironment("surface", "unpaved"))
    2938    }
    3039
     
    3241    public void testNotEqualCondition() throws Exception {
    3342        def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface!=paved]")).condition(Condition.Context.PRIMITIVE)
    34         assert condition instanceof Condition.KeyValueCondition
    3543        assert Condition.Op.NEQ.equals(condition.op)
    36         Main.pref = new Preferences()
    37         assert !condition.applies(new Environment().withPrimitive(getPrimitive("surface", "paved")))
    38         assert condition.applies(new Environment().withPrimitive(getPrimitive("surface", "unpaved")))
     44        assert !condition.applies(getEnvironment("surface", "paved"))
     45        assert condition.applies(getEnvironment("surface", "unpaved"))
    3946    }
    4047
     
    4249    public void testRegexCondition() throws Exception {
    4350        def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface=~/paved|unpaved/]")).condition(Condition.Context.PRIMITIVE)
    44         assert condition instanceof Condition.KeyValueCondition
    4551        assert Condition.Op.REGEX.equals(condition.op)
    46         Main.pref = new Preferences()
    47         assert condition.applies(new Environment().withPrimitive(getPrimitive("surface", "unpaved")))
    48         assert !condition.applies(new Environment().withPrimitive(getPrimitive("surface", "grass")))
     52        assert condition.applies(getEnvironment("surface", "unpaved"))
     53        assert !condition.applies(getEnvironment("surface", "grass"))
    4954    }
    5055
     
    5257    public void testNegatedRegexCondition() throws Exception {
    5358        def condition = (Condition.KeyValueCondition) new MapCSSParser(new StringReader("[surface!~/paved|unpaved/]")).condition(Condition.Context.PRIMITIVE)
    54         assert condition instanceof Condition.KeyValueCondition
    5559        assert Condition.Op.NREGEX.equals(condition.op)
    56         Main.pref = new Preferences()
    57         assert !condition.applies(new Environment().withPrimitive(getPrimitive("surface", "unpaved")))
    58         assert condition.applies(new Environment().withPrimitive(getPrimitive("surface", "grass")))
     60        assert !condition.applies(getEnvironment("surface", "unpaved"))
     61        assert condition.applies(getEnvironment("surface", "grass"))
     62    }
     63
     64    @Test
     65    public void testStandardKeyCondition() throws Exception {
     66        def c1 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[ highway ]")).condition(Condition.Context.PRIMITIVE)
     67        assert c1.matchType == null
     68        assert c1.applies(getEnvironment("highway", "unclassified"))
     69        assert !c1.applies(getEnvironment("railway", "rail"))
     70        def c2 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[\"/slash/\"]")).condition(Condition.Context.PRIMITIVE)
     71        assert c2.matchType == null
     72        assert c2.applies(getEnvironment("/slash/", "yes"))
     73        assert !c2.applies(getEnvironment("\"slash\"", "no"))
     74    }
     75
     76    @Test
     77    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)
     82        def yes = getEnvironment("oneway", "yes")
     83        def no = getEnvironment("oneway", "no")
     84        def none = getEnvironment("no-oneway", "foo")
     85        assert c1.applies(yes)
     86        assert !c1.applies(no)
     87        assert !c1.applies(none)
     88        assert !c2.applies(yes)
     89        assert c2.applies(no)
     90        assert !c2.applies(none)
     91        assert !c3.applies(yes)
     92        assert c3.applies(no)
     93        assert c3.applies(none)
     94        assert c4.applies(yes)
     95        assert !c4.applies(no)
     96        assert c4.applies(none)
     97    }
     98
     99    @Test
     100    public void testRegexKeyCondition() throws Exception {
     101        def c1 = (Condition.KeyCondition) new MapCSSParser(new StringReader("[/.*:(backward|forward)\$/]")).condition(Condition.Context.PRIMITIVE)
     102        assert Condition.KeyMatchType.REGEX.equals(c1.matchType)
     103        assert !c1.applies(getEnvironment("lanes", "3"))
     104        assert c1.applies(getEnvironment("lanes:forward", "3"))
     105        assert c1.applies(getEnvironment("lanes:backward", "3"))
     106        assert !c1.applies(getEnvironment("lanes:foobar", "3"))
    59107    }
    60108}
Note: See TracChangeset for help on using the changeset viewer.