Changeset 15988 in josm


Ignore:
Timestamp:
2020-03-01T23:36:07+01:00 (3 months ago)
Author:
simon04
Message:

see #18802 - MapCSSRule: support list of selectors

  • allows to drop MapCSSTagChecker.GroupedMapCSSRule
  • remove code duplication in MapCSSStyleSource/MapCSSTagCheckerIndex w.r.t. MapCSSRuleIndex
Location:
trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/scripts/TagInfoExtract.java

    r15985 r15988  
    342342        private List<TagInfoTag> convertStyleSheet() {
    343343            return styleSource.rules.stream()
    344                     .flatMap(rule -> rule.selector.getConditions().stream())
     344                    .flatMap(rule -> rule.selectors.stream())
     345                    .flatMap(selector -> selector.getConditions().stream())
    345346                    .filter(ConditionFactory.SimpleKeyValueCondition.class::isInstance)
    346347                    .map(ConditionFactory.SimpleKeyValueCondition.class::cast)
  • trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java

    r15987 r15988  
    1515import java.util.HashSet;
    1616import java.util.Iterator;
    17 import java.util.LinkedHashMap;
    1817import java.util.LinkedList;
    1918import java.util.List;
     
    2625import java.util.regex.Matcher;
    2726import java.util.regex.Pattern;
     27import java.util.stream.Stream;
    2828
    2929import org.openstreetmap.josm.command.ChangePropertyCommand;
     
    4747import org.openstreetmap.josm.gui.mappaint.MultiCascade;
    4848import org.openstreetmap.josm.gui.mappaint.mapcss.Condition;
    49 import org.openstreetmap.josm.gui.mappaint.mapcss.Declaration;
    5049import org.openstreetmap.josm.gui.mappaint.mapcss.Expression;
    5150import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction;
    5251import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
    5352import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
    54 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource.MapCSSRuleIndex;
    5553import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
    5654import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector;
     
    7674public class MapCSSTagChecker extends Test.TagTest {
    7775    private MapCSSTagCheckerIndex indexData;
     76    final Map<MapCSSRule, MapCSSTagCheckerAndRule> ruleToCheckMap = new HashMap<>();
    7877    private final Set<OsmPrimitive> tested = new HashSet<>();
    7978    private static final Map<IPrimitive, Area> mpAreaCache = new HashMap<>();
    80 
    81     /**
    82     * A grouped MapCSSRule with multiple selectors for a single declaration.
    83     * @see MapCSSRule
    84     */
    85     public static class GroupedMapCSSRule {
    86         /** MapCSS selectors **/
    87         public final List<Selector> selectors;
    88         /** MapCSS declaration **/
    89         public final Declaration declaration;
    90         /** MapCSS source **/
    91         public final String source;
    92 
    93         /**
    94          * Constructs a new {@code GroupedMapCSSRule} with empty source
    95          * @param selectors MapCSS selectors
    96          * @param declaration MapCSS declaration
    97          */
    98         public GroupedMapCSSRule(List<Selector> selectors, Declaration declaration) {
    99             this(selectors, declaration, "");
    100         }
    101 
    102         /**
    103          * Constructs a new {@code GroupedMapCSSRule}.
    104          * @param selectors MapCSS selectors
    105          * @param declaration MapCSS declaration
    106          * @param source the source of the rule
    107          */
    108         public GroupedMapCSSRule(List<Selector> selectors, Declaration declaration, String source) {
    109             this.selectors = selectors;
    110             this.declaration = declaration;
    111             this.source = source;
    112         }
    113 
    114         @Override
    115         public int hashCode() {
    116             return Objects.hash(selectors, declaration);
    117         }
    118 
    119         @Override
    120         public boolean equals(Object obj) {
    121             if (this == obj) return true;
    122             if (obj == null || getClass() != obj.getClass()) return false;
    123             GroupedMapCSSRule that = (GroupedMapCSSRule) obj;
    124             return Objects.equals(selectors, that.selectors) &&
    125                     Objects.equals(declaration, that.declaration);
    126         }
    127 
    128         @Override
    129         public String toString() {
    130             return "GroupedMapCSSRule [selectors=" + selectors + ", declaration=" + declaration + ']';
    131         }
    132     }
    13379
    13480    /**
     
    281227    public static class TagCheck implements Predicate<OsmPrimitive> {
    282228        /** The selector of this {@code TagCheck} */
    283         protected final GroupedMapCSSRule rule;
     229        protected final MapCSSRule rule;
    284230        /** Commands to apply in order to fix a matching primitive */
    285231        protected final List<FixCommand> fixCommands;
     
    296242        protected String group;
    297243
    298         TagCheck(GroupedMapCSSRule rule) {
     244        TagCheck(MapCSSRule rule) {
    299245            this.rule = rule;
    300246            this.fixCommands = new ArrayList<>();
     
    320266        private static final String POSSIBLE_THROWS = "throwError/throwWarning/throwOther";
    321267
    322         static TagCheck ofMapCSSRule(final GroupedMapCSSRule rule, AssertionConsumer assertionConsumer) throws IllegalDataException {
     268        static TagCheck ofMapCSSRule(final MapCSSRule rule, AssertionConsumer assertionConsumer) throws IllegalDataException {
    323269            final TagCheck check = new TagCheck(rule);
    324270            final Map<String, Boolean> assertions = new HashMap<>();
     
    394340
    395341        static ParseResult readMapCSS(Reader css) throws ParseException {
    396             return readMapCSS(css, "", null);
    397         }
    398 
    399         static ParseResult readMapCSS(Reader css, String url, AssertionConsumer assertionConsumer) throws ParseException {
     342            return readMapCSS(css, null);
     343        }
     344
     345        static ParseResult readMapCSS(Reader css, AssertionConsumer assertionConsumer) throws ParseException {
    400346            CheckParameterUtil.ensureParameterNotNull(css, "css");
    401347
     
    407353            // Ignore "meta" rule(s) from external rules of JOSM wiki
    408354            source.removeMetaRules();
    409             // group rules with common declaration block
    410             Map<Declaration, List<Selector>> g = new LinkedHashMap<>();
     355            List<TagCheck> parseChecks = new ArrayList<>();
    411356            for (MapCSSRule rule : source.rules) {
    412                 if (!g.containsKey(rule.declaration)) {
    413                     List<Selector> sels = new ArrayList<>();
    414                     sels.add(rule.selector);
    415                     g.put(rule.declaration, sels);
    416                 } else {
    417                     g.get(rule.declaration).add(rule.selector);
    418                 }
    419             }
    420             List<TagCheck> parseChecks = new ArrayList<>();
    421             for (Map.Entry<Declaration, List<Selector>> map : g.entrySet()) {
    422357                try {
    423                     parseChecks.add(TagCheck.ofMapCSSRule(
    424                             new GroupedMapCSSRule(map.getValue(), map.getKey(), url), assertionConsumer));
     358                    parseChecks.add(TagCheck.ofMapCSSRule(rule, assertionConsumer));
    425359                } catch (IllegalDataException e) {
    426360                    Logging.error("Cannot add MapCss rule: "+e.getMessage());
     
    661595
    662596    static class MapCSSTagCheckerAndRule extends MapCSSTagChecker {
    663         public final GroupedMapCSSRule rule;
    664 
    665         MapCSSTagCheckerAndRule(GroupedMapCSSRule rule) {
     597        public final MapCSSRule rule;
     598        private final TagCheck tagCheck;
     599        private final String source;
     600
     601        MapCSSTagCheckerAndRule(MapCSSRule rule) {
    666602            this.rule = rule;
     603            this.tagCheck = null;
     604            this.source = "";
     605        }
     606
     607        MapCSSTagCheckerAndRule(TagCheck tagCheck, String source) {
     608            this.rule = tagCheck.rule;
     609            this.tagCheck = tagCheck;
     610            this.source = source;
    667611        }
    668612
     
    674618        @Override
    675619        public String getSource() {
    676             return tr("URL / File: {0}", rule.source);
    677         }
     620            return tr("URL / File: {0}", source);
     621        }
     622    }
     623
     624    static MapCSSTagCheckerIndex createMapCSSTagCheckerIndex(MultiMap<String, TagCheck> checks, boolean includeOtherSeverity, boolean allTests) {
     625        final MapCSSTagCheckerIndex index = new MapCSSTagCheckerIndex();
     626        final Stream<MapCSSRule> ruleStream = checks.values().stream()
     627                .flatMap(Collection::stream)
     628                // Ignore "information" level checks if not wanted, unless they also set a MapCSS class
     629                .filter(c -> includeOtherSeverity || Severity.OTHER != c.getSeverity() || !c.setClassExpressions.isEmpty())
     630                .filter(c -> allTests || c.rule.selectors.stream().anyMatch(Selector.ChildOrParentSelector.class::isInstance))
     631                .map(c -> c.rule);
     632        index.buildIndex(ruleStream);
     633        return index;
    678634    }
    679635
     
    687643        final List<TestError> res = new ArrayList<>();
    688644        if (indexData == null) {
    689             indexData = new MapCSSTagCheckerIndex(checks, includeOtherSeverity, MapCSSTagCheckerIndex.ALL_TESTS);
    690         }
    691 
    692         MapCSSRuleIndex matchingRuleIndex = indexData.get(p);
     645            indexData = MapCSSTagCheckerIndex.createMapCSSTagCheckerIndex(checks, includeOtherSeverity, MapCSSTagCheckerIndex.ALL_TESTS);
     646        }
    693647
    694648        Environment env = new Environment(p, new MultiCascade(), Environment.DEFAULT_LAYER, null);
    695649        env.mpAreaCache = mpAreaCache;
    696650
    697         // the declaration indices are sorted, so it suffices to save the last used index
    698         Declaration lastDeclUsed = null;
    699 
    700         Iterator<MapCSSRule> candidates = matchingRuleIndex.getRuleCandidates(p);
     651        Iterator<MapCSSRule> candidates = indexData.getRuleCandidates(p);
    701652        while (candidates.hasNext()) {
    702653            MapCSSRule r = candidates.next();
    703             env.clearSelectorMatchingInformation();
    704             if (r.matches(env)) { // as side effect env.parent will be set (if s is a child selector)
    705                 TagCheck check = indexData.getCheck(r);
     654            for (Selector selector : r.selectors) {
     655                env.clearSelectorMatchingInformation();
     656                if (!selector.matches(env)) { // as side effect env.parent will be set (if s is a child selector)
     657                    continue;
     658                }
     659                MapCSSTagCheckerAndRule test = ruleToCheckMap.computeIfAbsent(r, rule -> checks.entrySet().stream()
     660                        .map(e -> e.getValue().stream()
     661                                // rule.selectors might be different due to MapCSSStyleIndex, however, the declarations are the same object
     662                                .filter(c -> c.rule.declaration == rule.declaration)
     663                                .findFirst()
     664                                .map(c -> new MapCSSTagCheckerAndRule(c, e.getKey()))
     665                                .orElse(null))
     666                        .filter(Objects::nonNull)
     667                        .findFirst()
     668                        .orElse(null));
     669                TagCheck check = test == null ? null : test.tagCheck;
    706670                if (check != null) {
    707                     if (r.declaration == lastDeclUsed)
    708                         continue; // don't apply one declaration more than once
    709                     lastDeclUsed = r.declaration;
    710 
    711671                    r.declaration.execute(env);
    712672                    if (!check.errors.isEmpty()) {
    713                         for (TestError e: check.getErrorsForPrimitive(p, r.selector, env, new MapCSSTagCheckerAndRule(check.rule))) {
     673                        for (TestError e: check.getErrorsForPrimitive(p, selector, env, test)) {
    714674                            addIfNotSimilar(e, res);
    715675                        }
     
    831791            if (zip != null)
    832792                I18n.addTexts(cache.getFile());
    833             result = TagCheck.readMapCSS(reader, url, assertionConsumer);
     793            result = TagCheck.readMapCSS(reader, assertionConsumer);
    834794            checks.remove(url);
    835795            checks.putAll(url, result.parseChecks);
     
    890850        super.setShowElements(true);
    891851        if (indexData == null) {
    892             indexData = new MapCSSTagCheckerIndex(checks, includeOtherSeverityChecks(), MapCSSTagCheckerIndex.ALL_TESTS);
     852            indexData = MapCSSTagCheckerIndex.createMapCSSTagCheckerIndex(checks, includeOtherSeverityChecks(), MapCSSTagCheckerIndex.ALL_TESTS);
    893853        }
    894854        tested.clear();
     
    904864            // rebuild index with a reduced set of rules (those that use ChildOrParentSelector) and thus may have left selectors
    905865            // matching the previously tested elements
    906             indexData = new MapCSSTagCheckerIndex(checks, includeOtherSeverityChecks(), MapCSSTagCheckerIndex.ONLY_SELECTED_TESTS);
     866            indexData = MapCSSTagCheckerIndex.createMapCSSTagCheckerIndex(checks, includeOtherSeverityChecks(), MapCSSTagCheckerIndex.ONLY_SELECTED_TESTS);
    907867
    908868            Set<OsmPrimitive> surrounding = new HashSet<>();
  • trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerIndex.java

    r15987 r15988  
    55
    66import java.text.MessageFormat;
    7 import java.util.ArrayList;
     7import java.util.Collection;
    88import java.util.HashMap;
    9 import java.util.List;
     9import java.util.Iterator;
    1010import java.util.Map;
    11 import java.util.Set;
     11import java.util.stream.Collectors;
     12import java.util.stream.Stream;
    1213
    1314import org.openstreetmap.josm.data.osm.INode;
     15import org.openstreetmap.josm.data.osm.IPrimitive;
    1416import org.openstreetmap.josm.data.osm.IRelation;
    1517import org.openstreetmap.josm.data.osm.IWay;
    16 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1718import org.openstreetmap.josm.data.osm.OsmUtils;
    1819import org.openstreetmap.josm.data.validation.Severity;
    1920import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker.TagCheck;
     21import org.openstreetmap.josm.gui.mappaint.mapcss.Declaration;
    2022import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
    2123import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource.MapCSSRuleIndex;
    2224import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
    23 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector;
    2425import org.openstreetmap.josm.tools.JosmRuntimeException;
    2526import org.openstreetmap.josm.tools.Logging;
     
    3132 *
    3233 */
    33 final class MapCSSTagCheckerIndex {
    34     final Map<MapCSSRule, TagCheck> ruleToCheckMap = new HashMap<>();
     34public final class MapCSSTagCheckerIndex {
     35    final Map<Declaration, TagCheck> ruleToCheckMap = new HashMap<>();
    3536
    3637    static final boolean ALL_TESTS = true;
     
    5758     */
    5859    final MapCSSRuleIndex multipolygonRules = new MapCSSRuleIndex();
    59 
    60     MapCSSTagCheckerIndex(MultiMap<String, TagCheck> checks, boolean includeOtherSeverity, boolean allTests) {
    61         buildIndex(checks, includeOtherSeverity, allTests);
    62     }
    63 
    64     private void buildIndex(MultiMap<String, TagCheck> checks, boolean includeOtherSeverity, boolean allTests) {
    65         List<TagCheck> allChecks = new ArrayList<>();
    66         for (Set<TagCheck> cs : checks.values()) {
    67             allChecks.addAll(cs);
    68         }
    69 
     60    /**
     61     * rules to apply canvas properties
     62     */
     63    final MapCSSRuleIndex canvasRules = new MapCSSRuleIndex();
     64
     65    static MapCSSTagCheckerIndex createMapCSSTagCheckerIndex(MultiMap<String, TagCheck> checks, boolean includeOtherSeverity, boolean allTests) {
     66        final MapCSSTagCheckerIndex index = new MapCSSTagCheckerIndex();
     67        final Stream<MapCSSRule> ruleStream = checks.values().stream()
     68                .flatMap(Collection::stream)
     69                // Ignore "information" level checks if not wanted, unless they also set a MapCSS class
     70                .filter(c -> includeOtherSeverity || Severity.OTHER != c.getSeverity() || !c.setClassExpressions.isEmpty())
     71                .filter(c -> allTests || c.rule.selectors.stream().anyMatch(Selector.ChildOrParentSelector.class::isInstance))
     72                .peek(c -> index.ruleToCheckMap.put(c.rule.declaration, c))
     73                .map(c -> c.rule);
     74        index.buildIndex(ruleStream);
     75        return index;
     76    }
     77
     78    /**
     79     * Clear the index.
     80     * <p>
     81     * You must own the write lock STYLE_SOURCE_LOCK when calling this method.
     82     */
     83    public void clear() {
    7084        ruleToCheckMap.clear();
    7185        nodeRules.clear();
     
    7488        relationRules.clear();
    7589        multipolygonRules.clear();
    76 
     90        canvasRules.clear();
     91    }
     92
     93    /**
     94     * Builds and initializes the index.
     95     * <p>
     96     * You must own the write lock of STYLE_SOURCE_LOCK when calling this method.
     97     */
     98    public void buildIndex(Stream<MapCSSRule> ruleStream) {
     99        clear();
    77100        // optimization: filter rules for different primitive types
    78         for (TagCheck c : allChecks) {
    79             if (!includeOtherSeverity && Severity.OTHER == c.getSeverity()
    80                     && c.setClassExpressions.isEmpty()) {
    81                 // Ignore "information" level checks if not wanted, unless they also set a MapCSS class
    82                 continue;
    83             }
    84 
    85             for (Selector s : c.rule.selectors) {
    86                 boolean hasLeftRightSel = s instanceof Selector.ChildOrParentSelector;
    87                 if (!allTests && !hasLeftRightSel) {
    88                     continue;
    89                 }
    90 
    91                 MapCSSRule optRule = new MapCSSRule(s, c.rule.declaration);
    92 
    93                 ruleToCheckMap.put(optRule, c);
    94                 final String base = s.getBase();
     101        ruleStream.forEach(rule -> {
     102            final Map<String, MapCSSRule> selectorsByBase = rule.selectors.stream()
     103                    .collect(Collectors.groupingBy(Selector::getBase,
     104                            Collectors.collectingAndThen(Collectors.toList(), selectors -> new MapCSSRule(selectors, rule.declaration))));
     105            selectorsByBase.forEach((base, optRule) -> {
    95106                switch (base) {
    96107                case Selector.BASE_NODE:
     
    117128                    break;
    118129                case Selector.BASE_CANVAS:
     130                    canvasRules.add(optRule);
     131                    break;
    119132                case Selector.BASE_META:
    120133                case Selector.BASE_SETTING:
     134                case Selector.BASE_SETTINGS:
    121135                    break;
    122136                default:
     
    125139                    Logging.error(e);
    126140                }
    127             }
    128         }
     141            });
     142        });
     143        initIndex();
     144    }
     145
     146    private void initIndex() {
    129147        nodeRules.initIndex();
    130148        wayRules.initIndex();
     
    132150        relationRules.initIndex();
    133151        multipolygonRules.initIndex();
     152        canvasRules.initIndex();
    134153    }
    135154
    136155    /**
    137156     * Get the index of rules for the given primitive.
    138      * @param p the primitve
     157     * @param p the primitive
    139158     * @return index of rules for the given primitive
    140159     */
    141     public MapCSSRuleIndex get(OsmPrimitive p) {
     160    public MapCSSRuleIndex get(IPrimitive p) {
    142161        if (p instanceof INode) {
    143162            return nodeRules;
     
    151170            if (((IRelation<?>) p).isMultipolygon()) {
    152171                return multipolygonRules;
     172            } else if (p.hasKey("#canvas")) {
     173                return canvasRules;
    153174            } else {
    154175                return relationRules;
     
    160181
    161182    /**
     183     * Get a subset of all rules that might match the primitive. Rules not included in the result are guaranteed to
     184     * not match this primitive.
     185     * <p>
     186     * You must have a read lock of STYLE_SOURCE_LOCK when calling this method.
     187     *
     188     * @param osm the primitive to match
     189     * @return An iterator over possible rules in the right order.
     190     */
     191    public Iterator<MapCSSRule> getRuleCandidates(IPrimitive osm) {
     192        return get(osm).getRuleCandidates(osm);
     193    }
     194
     195    /**
    162196     * return the TagCheck for which the given indexed rule was created.
    163197     * @param rule an indexed rule
     
    165199     */
    166200    public TagCheck getCheck(MapCSSRule rule) {
    167         return ruleToCheckMap.get(rule);
     201        return ruleToCheckMap.get(rule.declaration);
    168202    }
    169203}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj

    r15983 r15988  
    650650    selectors=selectors()
    651651    decl=declaration()
    652     {
    653         for (Selector s : selectors) {
    654             sheet.rules.add(new MapCSSRule(s, decl));
    655         }
     652    {
     653        sheet.rules.add(new MapCSSRule(selectors, decl));
    656654    }
    657655}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSRule.java

    r15984 r15988  
    22package org.openstreetmap.josm.gui.mappaint.mapcss;
    33
     4import java.util.List;
    45import java.util.stream.Collectors;
    56
    67import org.openstreetmap.josm.gui.mappaint.Environment;
     8import org.openstreetmap.josm.tools.Utils;
    79
    810/**
     
    1820     * The selector. If it matches, this rule should be applied
    1921     */
    20     public final Selector selector;
     22    public final List<Selector> selectors;
    2123    /**
    2224     * The instructions for this selector
     
    2628    /**
    2729     * Constructs a new {@code MapCSSRule}.
    28      * @param selector The selector
     30     * @param selectors The selectors
    2931     * @param declaration The declaration
    3032     */
    31     public MapCSSRule(Selector selector, Declaration declaration) {
    32         this.selector = selector;
     33    public MapCSSRule(List<Selector> selectors, Declaration declaration) {
     34        this.selectors = Utils.toUnmodifiableList(selectors);
    3335        this.declaration = declaration;
    3436    }
     
    4446     */
    4547    public boolean matches(Environment env) {
    46         return selector.matches(env);
     48        return selectors.stream().anyMatch(s -> s.matches(env));
    4749    }
    4850
     
    6466    @Override
    6567    public String toString() {
    66         return selector + declaration.instructions.stream()
     68        final String selectorsString = selectors.stream().map(String::valueOf)
     69                .collect(Collectors.joining(",\n"));
     70        final String declarationString = declaration.instructions.stream()
    6771                .map(String::valueOf)
    6872                .collect(Collectors.joining("\n  ", " {\n  ", "\n}"));
     73        return selectorsString + declarationString;
    6974    }
    7075}
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java

    r15987 r15988  
    1414import java.lang.reflect.Field;
    1515import java.nio.charset.StandardCharsets;
    16 import java.text.MessageFormat;
    1716import java.util.ArrayList;
    1817import java.util.BitSet;
    19 import java.util.Collection;
    2018import java.util.Collections;
    2119import java.util.HashMap;
     
    3634
    3735import org.openstreetmap.josm.data.Version;
    38 import org.openstreetmap.josm.data.osm.INode;
    3936import org.openstreetmap.josm.data.osm.IPrimitive;
    40 import org.openstreetmap.josm.data.osm.IRelation;
    41 import org.openstreetmap.josm.data.osm.IWay;
    4237import org.openstreetmap.josm.data.osm.KeyValueVisitor;
    4338import org.openstreetmap.josm.data.osm.Node;
    44 import org.openstreetmap.josm.data.osm.OsmUtils;
    4539import org.openstreetmap.josm.data.osm.Tagged;
    4640import org.openstreetmap.josm.data.preferences.sources.SourceEntry;
     41import org.openstreetmap.josm.data.validation.tests.MapCSSTagCheckerIndex;
    4742import org.openstreetmap.josm.gui.mappaint.Cascade;
    4843import org.openstreetmap.josm.gui.mappaint.Environment;
     
    8984    public final List<MapCSSRule> rules = new ArrayList<>();
    9085    /**
    91      * Rules for nodes
    92      */
    93     public final MapCSSRuleIndex nodeRules = new MapCSSRuleIndex();
    94     /**
    95      * Rules for ways without tag area=no
    96      */
    97     public final MapCSSRuleIndex wayRules = new MapCSSRuleIndex();
    98     /**
    99      * Rules for ways with tag area=no
    100      */
    101     public final MapCSSRuleIndex wayNoAreaRules = new MapCSSRuleIndex();
    102     /**
    103      * Rules for relations that are not multipolygon relations
    104      */
    105     public final MapCSSRuleIndex relationRules = new MapCSSRuleIndex();
    106     /**
    107      * Rules for multipolygon relations
    108      */
    109     public final MapCSSRuleIndex multipolygonRules = new MapCSSRuleIndex();
    110     /**
    111      * rules to apply canvas properties
    112      */
    113     public final MapCSSRuleIndex canvasRules = new MapCSSRuleIndex();
     86     * Index of rules in this style file
     87     */
     88    private final MapCSSTagCheckerIndex ruleIndex = new MapCSSTagCheckerIndex();
    11489
    11590    private Color backgroundColorOverride;
     
    292267            for (int ruleIndex = 0; ruleIndex < rules.size(); ruleIndex++) {
    293268                MapCSSRule r = rules.get(ruleIndex);
    294                 final List<Condition> conditions = r.selector.getConditions();
    295                 if (conditions == null || conditions.isEmpty()) {
    296                     remaining.set(ruleIndex);
    297                     continue;
    298                 }
    299                 Optional<SimpleKeyValueCondition> lastCondition = Utils.filteredCollection(conditions, SimpleKeyValueCondition.class).stream()
    300                         .reduce((first, last) -> last);
    301                 if (lastCondition.isPresent()) {
    302                     getEntryInIndex(lastCondition.get().k).addForKeyAndValue(lastCondition.get().v, ruleIndex);
    303                 } else {
    304                     String key = findAnyRequiredKey(conditions);
    305                     if (key != null) {
    306                         getEntryInIndex(key).addForKey(ruleIndex);
     269                for (Selector selector : r.selectors) {
     270                    final List<Condition> conditions = selector.getConditions();
     271                    if (conditions == null || conditions.isEmpty()) {
     272                        remaining.set(ruleIndex);
     273                        continue;
     274                    }
     275                    Optional<SimpleKeyValueCondition> lastCondition = Utils.filteredCollection(conditions, SimpleKeyValueCondition.class)
     276                            .stream()
     277                            .reduce((first, last) -> last);
     278                    if (lastCondition.isPresent()) {
     279                        getEntryInIndex(lastCondition.get().k).addForKeyAndValue(lastCondition.get().v, ruleIndex);
    307280                    } else {
    308                         remaining.set(ruleIndex);
     281                        String key = findAnyRequiredKey(conditions);
     282                        if (key != null) {
     283                            getEntryInIndex(key).addForKey(ruleIndex);
     284                        } else {
     285                            remaining.set(ruleIndex);
     286                        }
    309287                    }
    310288                }
     
    418396            init();
    419397            rules.clear();
    420             nodeRules.clear();
    421             wayRules.clear();
    422             wayNoAreaRules.clear();
    423             relationRules.clear();
    424             multipolygonRules.clear();
    425             canvasRules.clear();
     398            ruleIndex.clear();
    426399            // remove "areaStyle" pseudo classes intended only for validator (causes StackOverflowError otherwise), see #16183
    427400            removeAreaStylePseudoClass = url == null || !url.contains("validator"); // resource://data/validator/ or xxx.validator.mapcss
     
    461434            }
    462435            // optimization: filter rules for different primitive types
    463             for (MapCSSRule optRule: rules) {
    464                 final String base = optRule.selector.getBase();
    465                 switch (base) {
    466                     case Selector.BASE_NODE:
    467                         nodeRules.add(optRule);
    468                         break;
    469                     case Selector.BASE_WAY:
    470                         wayNoAreaRules.add(optRule);
    471                         wayRules.add(optRule);
    472                         break;
    473                     case Selector.BASE_AREA:
    474                         wayRules.add(optRule);
    475                         multipolygonRules.add(optRule);
    476                         break;
    477                     case Selector.BASE_RELATION:
    478                         relationRules.add(optRule);
    479                         multipolygonRules.add(optRule);
    480                         break;
    481                     case Selector.BASE_ANY:
    482                         nodeRules.add(optRule);
    483                         wayRules.add(optRule);
    484                         wayNoAreaRules.add(optRule);
    485                         relationRules.add(optRule);
    486                         multipolygonRules.add(optRule);
    487                         break;
    488                     case Selector.BASE_CANVAS:
    489                         canvasRules.add(optRule);
    490                         break;
    491                     case Selector.BASE_META:
    492                     case Selector.BASE_SETTING:
    493                     case Selector.BASE_SETTINGS:
    494                         break;
    495                     default:
    496                         final RuntimeException e = new JosmRuntimeException(MessageFormat.format("Unknown MapCSS base selector {0}", base));
    497                         Logging.warn(tr("Failed to parse Mappaint styles from ''{0}''. Error was: {1}", url, e.getMessage()));
    498                         Logging.error(e);
    499                         logError(e);
    500                 }
    501             }
    502             nodeRules.initIndex();
    503             wayRules.initIndex();
    504             wayNoAreaRules.initIndex();
    505             relationRules.initIndex();
    506             multipolygonRules.initIndex();
    507             canvasRules.initIndex();
     436            ruleIndex.buildIndex(rules.stream());
    508437            loaded = true;
    509438        } finally {
     
    588517        // Parse rules
    589518        for (MapCSSRule r : rules) {
    590             if (r.selector instanceof GeneralSelector) {
    591                 GeneralSelector gs = (GeneralSelector) r.selector;
     519            final Selector gs = r.selectors.get(0);
     520            if (gs instanceof GeneralSelector) {
    592521                if (Selector.BASE_SETTING.equals(gs.getBase())) {
    593                     loadSettings(r, gs, env);
     522                    loadSettings(r, ((GeneralSelector) gs), env);
    594523                } else if (Selector.BASE_SETTINGS.equals(gs.getBase())) {
    595                     loadSettings(r, gs, envGroups);
     524                    loadSettings(r, ((GeneralSelector) gs), envGroups);
    596525                }
    597526            }
     
    639568
    640569        for (MapCSSRule r : rules) {
    641             if (r.selector instanceof GeneralSelector) {
    642                 GeneralSelector gs = (GeneralSelector) r.selector;
    643                 if (gs.getBase().equals(type)) {
    644                     if (!gs.matchesConditions(env)) {
    645                         continue;
    646                     }
    647                     r.execute(env);
    648                 }
     570            final boolean matches = r.selectors.stream().anyMatch(gs -> gs instanceof GeneralSelector
     571                    && gs.getBase().equals(type)
     572                    && ((GeneralSelector) gs).matchesConditions(env));
     573            if (matches) {
     574                r.execute(env);
    649575            }
    650576        }
     
    659585    @Override
    660586    public void apply(MultiCascade mc, IPrimitive osm, double scale, boolean pretendWayIsClosed) {
    661         MapCSSRuleIndex matchingRuleIndex;
    662         if (osm instanceof INode) {
    663             matchingRuleIndex = nodeRules;
    664         } else if (osm instanceof IWay) {
    665             if (OsmUtils.isFalse(osm.get("area"))) {
    666                 matchingRuleIndex = wayNoAreaRules;
    667             } else {
    668                 matchingRuleIndex = wayRules;
    669             }
    670         } else if (osm instanceof IRelation) {
    671             if (((IRelation<?>) osm).isMultipolygon()) {
    672                 matchingRuleIndex = multipolygonRules;
    673             } else if (osm.hasKey("#canvas")) {
    674                 matchingRuleIndex = canvasRules;
    675             } else {
    676                 matchingRuleIndex = relationRules;
    677             }
    678         } else {
    679             throw new IllegalArgumentException("Unsupported type: " + osm);
    680         }
    681587
    682588        Environment env = new Environment(osm, mc, null, this);
     
    684590        int lastDeclUsed = -1;
    685591
    686         Iterator<MapCSSRule> candidates = matchingRuleIndex.getRuleCandidates(osm);
     592        Iterator<MapCSSRule> candidates = ruleIndex.getRuleCandidates(osm);
    687593        while (candidates.hasNext()) {
    688594            MapCSSRule r = candidates.next();
    689             env.clearSelectorMatchingInformation();
    690             env.layer = r.selector.getSubpart().getId(env);
    691             String sub = env.layer;
    692             if (r.selector.matches(env)) { // as side effect env.parent will be set (if s is a child selector)
    693                 Selector s = r.selector;
     595            for (Selector s : r.selectors) {
     596                env.clearSelectorMatchingInformation();
     597                env.layer = s.getSubpart().getId(env);
     598                String sub = env.layer;
     599                if (!s.matches(env)) { // as side effect env.parent will be set (if s is a child selector)
     600                    continue;
     601                }
    694602                if (s.getRange().contains(scale)) {
    695603                    mc.range = Range.cut(mc.range, s.getRange());
     
    746654     */
    747655    public void removeMetaRules() {
    748         rules.removeIf(x -> x.selector instanceof GeneralSelector && Selector.BASE_META.equals(x.selector.getBase()));
     656        rules.removeIf(x -> x.selectors.get(0) instanceof GeneralSelector && Selector.BASE_META.equals(x.selectors.get(0).getBase()));
    749657    }
    750658
  • trunk/test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java

    r15981 r15988  
    6767        final MapCSSTagChecker test = new MapCSSTagChecker();
    6868        Set<String> errors = new HashSet<>();
    69         test.checks.putAll("test", TagCheck.readMapCSS(new StringReader(css), "", errors::add).parseChecks);
     69        test.checks.putAll("test", TagCheck.readMapCSS(new StringReader(css), errors::add).parseChecks);
    7070        assertTrue(errors.toString(), errors.isEmpty());
    7171        return test;
  • trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java

    r15102 r15988  
    7474         source.loadStyleSource();
    7575         assertEquals(1, source.rules.size());
    76          return (ChildOrParentSelector) source.rules.get(0).selector;
     76         return (ChildOrParentSelector) source.rules.get(0).selectors.get(0);
    7777    }
    7878
Note: See TracChangeset for help on using the changeset viewer.