Changeset 18451 in josm for trunk


Ignore:
Timestamp:
2022-05-16T22:24:59+02:00 (2 years ago)
Author:
taylor.smock
Message:

Fix #22073: Optimized regexes (string starts with, ends with, contains) do not work

This was caused by taking a portion of a regex (for example, _name from
/_name$/), and using that to match against keys. So alt_name would not
match, but _name would. All regex patterns should now be added to the
remaining field in MapCSSRuleIndex, and there now exists a test to ensure
we do not forget to add new non-regex types to the valid key type list.

Location:
trunk
Files:
2 edited

Legend:

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

    r18208 r18451  
    55import java.util.BitSet;
    66import java.util.Collections;
     7import java.util.EnumSet;
    78import java.util.HashMap;
    89import java.util.Iterator;
     
    121122    }
    122123
     124    /** Valid key types for indexing (see {@link ConditionFactory.KeyMatchType}) */
     125    private static final EnumSet<ConditionFactory.KeyMatchType> VALID_INDEX_KEY_TYPES = EnumSet.of(
     126            ConditionFactory.KeyMatchType.EQ, ConditionFactory.KeyMatchType.TRUE, ConditionFactory.KeyMatchType.FALSE);
     127
    123128    /**
    124129     * All rules this index is for. Once this index is built, this list is sorted.
     
    187192        String key = null;
    188193        for (Condition c : conds) {
    189             if (c instanceof KeyCondition) {
     194            if (c instanceof KeyCondition && VALID_INDEX_KEY_TYPES.contains(((KeyCondition) c).matchType)) {
    190195                KeyCondition keyCondition = (KeyCondition) c;
    191196                if (!keyCondition.negateResult) {
  • trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/KeyConditionTest.java

    r17593 r18451  
    77import static org.junit.jupiter.api.Assertions.fail;
    88
     9import java.util.ArrayList;
     10import java.util.Collection;
     11import java.util.Collections;
     12import java.util.EnumSet;
     13import java.util.Iterator;
     14import java.util.List;
     15import java.util.regex.Pattern;
     16
    917import org.junit.jupiter.api.BeforeEach;
    1018import org.junit.jupiter.api.Test;
    1119import org.junit.jupiter.api.extension.RegisterExtension;
     20import org.junit.jupiter.params.ParameterizedTest;
     21import org.junit.jupiter.params.provider.EnumSource;
     22import org.openstreetmap.josm.TestUtils;
    1223import org.openstreetmap.josm.data.coor.LatLon;
    1324import org.openstreetmap.josm.data.osm.DataSet;
     
    1728import org.openstreetmap.josm.data.osm.RelationMember;
    1829import org.openstreetmap.josm.gui.mappaint.Environment;
     30import org.openstreetmap.josm.gui.mappaint.Range;
    1931import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.Context;
    2032import org.openstreetmap.josm.gui.mappaint.mapcss.ConditionFactory.KeyCondition;
     
    149161        assertTrue(cond.applies(e));
    150162    }
     163
     164    /**
     165     * Ensure that we are accounting for all necessary {@link ConditionFactory.KeyMatchType} are accounted for.
     166     * If this fails, and the key should not be fully matched against (i.e., it is a regex), please modify
     167     * {@link MapCSSRuleIndex#findAnyRequiredKey}.
     168     *
     169     * Non-regression test for JOSM #22073.
     170     */
     171    @ParameterizedTest
     172    @EnumSource(ConditionFactory.KeyMatchType.class)
     173    void testNonRegression22073(final KeyMatchType keyMatchType) {
     174        final EnumSet<ConditionFactory.KeyMatchType> current = EnumSet.of(KeyMatchType.EQ, KeyMatchType.FALSE, KeyMatchType.TRUE,
     175                KeyMatchType.REGEX, KeyMatchType.ANY_CONTAINS, KeyMatchType.ANY_ENDS_WITH, KeyMatchType.ANY_STARTS_WITH);
     176        assertTrue(current.contains(keyMatchType), "Is this type supposed to be matched against a whole key?");
     177
     178        final boolean fullKey = EnumSet.of(KeyMatchType.EQ, KeyMatchType.TRUE, KeyMatchType.FALSE).contains(keyMatchType);
     179        final MapCSSRuleIndex index = new MapCSSRuleIndex();
     180        final Condition condition = keyMatchType != KeyMatchType.REGEX
     181                ? new KeyCondition("highway", false, keyMatchType)
     182                : new ConditionFactory.KeyRegexpCondition(Pattern.compile("highway"), false);
     183        index.add(new MapCSSRule(Collections.singletonList(new Selector.GeneralSelector("*", Range.ZERO_TO_INFINITY,
     184                Collections.singletonList(condition), null)), null));
     185        index.initIndex();
     186        final Node testNode = TestUtils.newNode("highway=traffic_calming");
     187        // First get all the "remaining" candidates by passing a non-tagged node
     188        final Collection<MapCSSRule> remaining = convertIterator(index.getRuleCandidates(new Node(LatLon.ZERO)));
     189        // Then get all the matches for the test node
     190        final Collection<MapCSSRule> matches = convertIterator(index.getRuleCandidates(testNode));
     191        // Finally, remove the remaining rules from the matches
     192        matches.removeIf(remaining::contains);
     193        assertEquals(fullKey, !matches.isEmpty());
     194    }
     195
     196    private static <T> Collection<T> convertIterator(Iterator<T> iterator) {
     197        final List<T> rList = new ArrayList<>();
     198        iterator.forEachRemaining(rList::add);
     199        return rList;
     200    }
    151201}
Note: See TracChangeset for help on using the changeset viewer.