Ignore:
Timestamp:
2020-03-01T23:36:07+01:00 (4 years 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
File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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<>();
Note: See TracChangeset for help on using the changeset viewer.