Changeset 17593 in josm


Ignore:
Timestamp:
2021-03-20T11:50:06+01:00 (4 years ago)
Author:
simon04
Message:

see #20613 - Split KeyCondition/KeyRegexpCondition

Rewrite patterns for prefix/substring/suffix checks.

5.75->2.93% of allocations in MapCSSTagCheckerPerformanceTest.testCity relate to Pattern matching.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/resources/data/validator/deprecated.mapcss

    r17469 r17593  
    14731473/* #17504 */
    14741474*[is_in],
    1475 node[/^is_in:.*$/],
    1476 way[/^is_in:.*$/] {
     1475node[/^is_in:$/],
     1476way[/^is_in:$/] {
    14771477  throwWarning: tr("{0} is deprecated", "{0.key}");
    14781478  group: tr("deprecated tagging");
  • trunk/resources/data/validator/unnecessary.mapcss

    r17209 r17593  
    155155
    156156/* #2760 */
    157 *[/^(gpx|gpxx|gpxd):/] {
     157*[/^gpx:/],
     158*[/^gpxx:/],
     159*[/^gpxd:/] {
    158160  throwWarning: tr("{0} should not be uploaded", "{0.key}");
    159161  group: tr("unnecessary tag");
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionFactory.java

    r17584 r17593  
    105105        switch (context) {
    106106        case PRIMITIVE:
    107             return new KeyCondition(k, not, matchType);
     107            if (KeyMatchType.REGEX == matchType && k.matches("[A-Za-z0-9:_-]+")) {
     108                // optimization: using String.contains avoids allocating a Matcher
     109                return new KeyCondition(k, not, KeyMatchType.ANY_CONTAINS);
     110            } else if (KeyMatchType.REGEX == matchType && k.matches("\\^[A-Za-z0-9:_-]+")) {
     111                // optimization: using String.startsWith avoids allocating a Matcher
     112                return new KeyCondition(k.substring(1), not, KeyMatchType.ANY_STARTS_WITH);
     113            } else if (KeyMatchType.REGEX == matchType && k.matches("[A-Za-z0-9:_-]+\\$")) {
     114                // optimization: using String.endsWith avoids allocating a Matcher
     115                return new KeyCondition(k.substring(0, k.length() - 1), not, KeyMatchType.ANY_ENDS_WITH);
     116            } else if (matchType == KeyMatchType.REGEX) {
     117                return new KeyRegexpCondition(Pattern.compile(k), not);
     118            } else {
     119                return new KeyCondition(k, not, matchType);
     120            }
    108121        case LINK:
    109122            if (matchType != null)
     
    489502         * The key needs to match the given regular expression.
    490503         */
    491         REGEX
     504        REGEX,
     505        /**
     506         * The key needs to contain the given label as substring.
     507         */
     508        ANY_CONTAINS,
     509        /**
     510         * The key needs to start with the given label.
     511         */
     512        ANY_STARTS_WITH,
     513        /**
     514         * The key needs to end with the given label.
     515         */
     516        ANY_ENDS_WITH,
    492517    }
    493518
     
    510535     *                   LINK:       not supported
    511536     * </pre>
     537     * @see KeyRegexpCondition
    512538     */
    513539    public static class KeyCondition implements Condition, ToTagConvertable {
     
    526552         */
    527553        public final KeyMatchType matchType;
    528         /**
    529          * A predicate used to match a the regexp against the key. Only used if the match type is regexp.
    530          */
    531         public final Predicate<String> containsPattern;
    532554
    533555        /**
     
    538560         */
    539561        public KeyCondition(String label, boolean negateResult, KeyMatchType matchType) {
     562            CheckParameterUtil.ensureThat(matchType != KeyMatchType.REGEX, "Use KeyPatternCondition");
    540563            this.label = label;
    541564            this.negateResult = negateResult;
    542565            this.matchType = matchType == null ? KeyMatchType.EQ : matchType;
    543             this.containsPattern = KeyMatchType.REGEX == matchType
    544                     ? Pattern.compile(label).asPredicate()
    545                     : null;
    546566        }
    547567
    548568        @Override
    549569        public boolean applies(Environment e) {
    550             switch(e.getContext()) {
    551             case PRIMITIVE:
    552                 switch (matchType) {
     570            CheckParameterUtil.ensureThat(!e.isLinkContext(), "Illegal state: KeyCondition not supported in LINK context");
     571            switch (matchType) {
    553572                case TRUE:
    554573                    return e.osm.isKeyTrue(label) ^ negateResult;
    555574                case FALSE:
    556575                    return e.osm.isKeyFalse(label) ^ negateResult;
    557                 case REGEX:
    558                     return e.osm.keys().anyMatch(containsPattern) ^ negateResult;
     576                case ANY_CONTAINS:
     577                case ANY_STARTS_WITH:
     578                case ANY_ENDS_WITH:
     579                    return e.osm.keys().anyMatch(keyPredicate()) ^ negateResult;
    559580                default:
    560581                    return e.osm.hasKey(label) ^ negateResult;
    561                 }
    562             case LINK:
    563                 Utils.ensure(false, "Illegal state: KeyCondition not supported in LINK context");
    564                 return false;
    565             default: throw new AssertionError();
    566             }
     582            }
     583        }
     584
     585        private Predicate<String> keyPredicate() {
     586            switch (matchType) {
     587                case ANY_CONTAINS:
     588                    return key -> key.contains(label);
     589                case ANY_STARTS_WITH:
     590                    return key -> key.startsWith(label);
     591                case ANY_ENDS_WITH:
     592                    return key -> key.endsWith(label);
     593                default:
     594                    return null;
     595            }
     596        }
     597
     598        /**
     599         * Get the matched key and the corresponding value.
     600         * <p>
     601         * WARNING: This ignores {@link #negateResult}.
     602         * @param p The primitive to get the value from.
     603         * @return The tag.
     604         */
     605        @Override
     606        public Tag asTag(OsmPrimitive p) {
     607            String key = label;
     608            Predicate<String> keyPredicate = keyPredicate();
     609            if (keyPredicate != null) {
     610                key = p.keys().filter(keyPredicate).findAny().orElse(key);
     611            }
     612            return new Tag(key, p.get(key));
     613        }
     614
     615        @Override
     616        public String toString() {
     617            return '[' + (negateResult ? "!" : "") + label + ']';
     618        }
     619    }
     620
     621    /**
     622     * KeyPatternCondition represents a conditions matching keys based on a pattern.
     623     */
     624    public static class KeyRegexpCondition implements Condition, ToTagConvertable {
     625
     626        /**
     627         * A predicate used to match a the regexp against the key. Only used if the match type is regexp.
     628         */
     629        public final Pattern pattern;
     630        /**
     631         * If we should negate the result of the match.
     632         */
     633        public final boolean negateResult;
     634
     635        /**
     636         * Creates a new KeyPatternCondition
     637         * @param pattern The regular expression for matching keys.
     638         * @param negateResult If we should negate the result.
     639         */
     640        public KeyRegexpCondition(Pattern pattern, boolean negateResult) {
     641            this.negateResult = negateResult;
     642            this.pattern = pattern;
     643        }
     644
     645        @Override
     646        public boolean applies(Environment e) {
     647            CheckParameterUtil.ensureThat(!e.isLinkContext(), "Illegal state: KeyCondition not supported in LINK context");
     648            return e.osm.keys().anyMatch(pattern.asPredicate()) ^ negateResult;
    567649        }
    568650
     
    578660        @Override
    579661        public Tag asTag(OsmPrimitive p) {
    580             String key = label;
    581             if (KeyMatchType.REGEX == matchType) {
    582                 key = p.keys().filter(containsPattern).findAny().orElse(key);
    583             }
     662            String key = p.keys().filter(pattern.asPredicate()).findAny().orElse(pattern.pattern());;
    584663            return new Tag(key, p.get(key));
    585664        }
     
    587666        @Override
    588667        public String toString() {
    589             return '[' + (negateResult ? "!" : "") + label + ']';
     668            return '[' + (negateResult ? "!" : "") + pattern + ']';
    590669        }
    591670    }
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSRuleIndex.java

    r16821 r17593  
    1616import org.openstreetmap.josm.data.osm.Tagged;
    1717import org.openstreetmap.josm.gui.mappaint.mapcss.ConditionFactory.KeyCondition;
    18 import org.openstreetmap.josm.gui.mappaint.mapcss.ConditionFactory.KeyMatchType;
    1918import org.openstreetmap.josm.gui.mappaint.mapcss.ConditionFactory.KeyValueCondition;
    2019import org.openstreetmap.josm.gui.mappaint.mapcss.ConditionFactory.SimpleKeyValueCondition;
     
    190189            if (c instanceof KeyCondition) {
    191190                KeyCondition keyCondition = (KeyCondition) c;
    192                 if (!keyCondition.negateResult && conditionRequiresKeyPresence(keyCondition.matchType)) {
     191                if (!keyCondition.negateResult) {
    193192                    key = keyCondition.label;
    194193                }
     
    201200        }
    202201        return key;
    203     }
    204 
    205     private static boolean conditionRequiresKeyPresence(KeyMatchType matchType) {
    206         return matchType != KeyMatchType.REGEX;
    207202    }
    208203
  • trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/KeyConditionTest.java

    r17275 r17593  
    22package org.openstreetmap.josm.gui.mappaint.mapcss;
    33
     4import static org.junit.jupiter.api.Assertions.assertEquals;
    45import static org.junit.jupiter.api.Assertions.assertFalse;
    56import static org.junit.jupiter.api.Assertions.assertTrue;
     
    1213import org.openstreetmap.josm.data.osm.DataSet;
    1314import org.openstreetmap.josm.data.osm.Node;
     15import org.openstreetmap.josm.data.osm.OsmUtils;
    1416import org.openstreetmap.josm.data.osm.Relation;
    1517import org.openstreetmap.josm.data.osm.RelationMember;
     
    8284        ConditionFactory.createKeyCondition("a key", true, KeyMatchType.TRUE, Context.PRIMITIVE);
    8385
     86        // [/regex/]
     87        Condition c = ConditionFactory.createKeyCondition("foo|bar", false, KeyMatchType.REGEX, Context.PRIMITIVE);
     88        assertTrue(c.applies(new Environment(OsmUtils.createPrimitive("node BARfooBAZ=true"))));
     89        assertFalse(c.applies(new Environment(OsmUtils.createPrimitive("node BARBAZ=true"))));
     90        c = ConditionFactory.createKeyCondition("colour:", false, KeyMatchType.REGEX, Context.PRIMITIVE);
     91        assertEquals(KeyMatchType.ANY_CONTAINS, ((KeyCondition) c).matchType);
     92        assertEquals("colour:", ((KeyCondition) c).label);
     93        assertTrue(c.applies(new Environment(OsmUtils.createPrimitive("node colour:roof=ref"))));
     94        assertFalse(c.applies(new Environment(OsmUtils.createPrimitive("node foo=bar"))));
     95        c = ConditionFactory.createKeyCondition("^wikipedia:", false, KeyMatchType.REGEX, Context.PRIMITIVE);
     96        assertEquals(KeyMatchType.ANY_STARTS_WITH, ((KeyCondition) c).matchType);
     97        assertEquals("wikipedia:", ((KeyCondition) c).label);
     98        assertTrue(c.applies(new Environment(OsmUtils.createPrimitive("node wikipedia:en=a"))));
     99        assertFalse(c.applies(new Environment(OsmUtils.createPrimitive("node wikipedia=a"))));
     100        c = ConditionFactory.createKeyCondition("_name$", false, KeyMatchType.REGEX, Context.PRIMITIVE);
     101        assertEquals(KeyMatchType.ANY_ENDS_WITH, ((KeyCondition) c).matchType);
     102        assertEquals("_name", ((KeyCondition) c).label);
     103        assertTrue(c.applies(new Environment(OsmUtils.createPrimitive("node alt_name=a"))));
     104        assertFalse(c.applies(new Environment(OsmUtils.createPrimitive("node name=a"))));
     105
    84106        // ["a label"]
    85107        ConditionFactory.createKeyCondition("a key", false, null, Context.LINK);
Note: See TracChangeset for help on using the changeset viewer.