Changeset 14469 in josm


Ignore:
Timestamp:
2018-11-30T10:43:39+01:00 (8 weeks ago)
Author:
GerdP
Message:

fix #17021 Use MapCSSRuleIndex to speed up MapCSSTagChecker

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java

    r14421 r14469  
    1717import java.util.HashMap;
    1818import java.util.HashSet;
     19import java.util.Iterator;
    1920import java.util.LinkedHashMap;
    2021import java.util.LinkedHashSet;
     
    3637import org.openstreetmap.josm.command.SequenceCommand;
    3738import org.openstreetmap.josm.data.osm.DataSet;
     39import org.openstreetmap.josm.data.osm.INode;
     40import org.openstreetmap.josm.data.osm.IRelation;
     41import org.openstreetmap.josm.data.osm.IWay;
    3842import org.openstreetmap.josm.data.osm.OsmPrimitive;
    3943import org.openstreetmap.josm.data.osm.OsmUtils;
     
    5559import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule.Declaration;
    5660import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
     61import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource.MapCSSRuleIndex;
    5762import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
    5863import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.AbstractSelector;
    5964import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector;
     65import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.OptimizedGeneralSelector;
    6066import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser;
    6167import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.ParseException;
    6268import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.TokenMgrError;
     69import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    6370import org.openstreetmap.josm.io.CachedFile;
    6471import org.openstreetmap.josm.io.FileWatcher;
     
    6875import org.openstreetmap.josm.tools.CheckParameterUtil;
    6976import org.openstreetmap.josm.tools.I18n;
     77import org.openstreetmap.josm.tools.JosmRuntimeException;
    7078import org.openstreetmap.josm.tools.Logging;
    7179import org.openstreetmap.josm.tools.MultiMap;
     
    7785 */
    7886public class MapCSSTagChecker extends Test.TagTest {
    79 
    80     /**
    81      * A grouped MapCSSRule with multiple selectors for a single declaration.
    82      * @see MapCSSRule
     87    IndexData indexData;
     88
     89    /**
     90     * Helper class to store indexes of rules.
     91     * @author Gerd
     92     *
    8393     */
     94    private static class IndexData {
     95        final Map<MapCSSRule, TagCheck> ruleToCheckMap = new HashMap<>();
     96
     97        /**
     98         * Rules for nodes
     99         */
     100        final MapCSSRuleIndex nodeRules = new MapCSSRuleIndex();
     101        /**
     102         * Rules for ways without tag area=no
     103         */
     104        final MapCSSRuleIndex wayRules = new MapCSSRuleIndex();
     105        /**
     106         * Rules for ways with tag area=no
     107         */
     108        final MapCSSRuleIndex wayNoAreaRules = new MapCSSRuleIndex();
     109        /**
     110         * Rules for relations that are not multipolygon relations
     111         */
     112        final MapCSSRuleIndex relationRules = new MapCSSRuleIndex();
     113        /**
     114         * Rules for multipolygon relations
     115         */
     116        final MapCSSRuleIndex multipolygonRules = new MapCSSRuleIndex();
     117
     118        IndexData(MultiMap<String, TagCheck> checks) {
     119            buildIndex(checks);
     120        }
     121
     122        private void buildIndex(MultiMap<String, TagCheck> checks) {
     123            List<TagCheck> allChecks = new ArrayList<>();
     124            for (Set<TagCheck> cs : checks.values()) {
     125                allChecks.addAll(cs);
     126            }
     127
     128            ruleToCheckMap.clear();
     129            nodeRules.clear();
     130            wayRules.clear();
     131            wayNoAreaRules.clear();
     132            relationRules.clear();
     133            multipolygonRules.clear();
     134
     135            // optimization: filter rules for different primitive types
     136            for (TagCheck c : allChecks) {
     137                for (Selector s : c.rule.selectors) {
     138                    // find the rightmost selector, this must be a GeneralSelector
     139                    Selector selRightmost = s;
     140                    while (selRightmost instanceof Selector.ChildOrParentSelector) {
     141                        selRightmost = ((Selector.ChildOrParentSelector) selRightmost).right;
     142                    }
     143                    MapCSSRule optRule = new MapCSSRule(s.optimizedBaseCheck(), c.rule.declaration);
     144
     145                    ruleToCheckMap.put(optRule, c);
     146                    final String base = ((GeneralSelector) selRightmost).getBase();
     147                    switch (base) {
     148                    case Selector.BASE_NODE:
     149                        nodeRules.add(optRule);
     150                        break;
     151                    case Selector.BASE_WAY:
     152                        wayNoAreaRules.add(optRule);
     153                        wayRules.add(optRule);
     154                        break;
     155                    case Selector.BASE_AREA:
     156                        wayRules.add(optRule);
     157                        multipolygonRules.add(optRule);
     158                        break;
     159                    case Selector.BASE_RELATION:
     160                        relationRules.add(optRule);
     161                        multipolygonRules.add(optRule);
     162                        break;
     163                    case Selector.BASE_ANY:
     164                        nodeRules.add(optRule);
     165                        wayRules.add(optRule);
     166                        wayNoAreaRules.add(optRule);
     167                        relationRules.add(optRule);
     168                        multipolygonRules.add(optRule);
     169                        break;
     170                    case Selector.BASE_CANVAS:
     171                    case Selector.BASE_META:
     172                    case Selector.BASE_SETTING:
     173                        break;
     174                    default:
     175                        final RuntimeException e = new JosmRuntimeException(MessageFormat.format("Unknown MapCSS base selector {0}", base));
     176                        Logging.warn(tr("Failed to index validator rules. Error was: {0}", e.getMessage()));
     177                        Logging.error(e);
     178                    }
     179                }
     180            }
     181            nodeRules.initIndex();
     182            wayRules.initIndex();
     183            wayNoAreaRules.initIndex();
     184            relationRules.initIndex();
     185            multipolygonRules.initIndex();
     186        }
     187
     188        /**
     189         * Get the index of rules for the given primitive.
     190         * @param p the primitve
     191         * @return index of rules for the given primitive
     192         */
     193        public MapCSSRuleIndex get(OsmPrimitive p) {
     194            if (p instanceof INode) {
     195                return nodeRules;
     196            } else if (p instanceof IWay) {
     197                if (OsmUtils.isFalse(p.get("area"))) {
     198                    return wayNoAreaRules;
     199                } else {
     200                    return wayRules;
     201                }
     202            } else if (p instanceof IRelation) {
     203                if (((IRelation<?>) p).isMultipolygon()) {
     204                    return multipolygonRules;
     205                } else {
     206                    return relationRules;
     207                }
     208            } else {
     209                throw new IllegalArgumentException("Unsupported type: " + p);
     210            }
     211        }
     212
     213        /**
     214         * return the TagCheck for which the given indexed rule was created.
     215         * @param rule an indexed rule
     216         * @return the original TagCheck
     217         */
     218        public TagCheck getCheck(MapCSSRule rule) {
     219            return ruleToCheckMap.get(rule);
     220        }
     221    }
     222
     223    /**
     224    * A grouped MapCSSRule with multiple selectors for a single declaration.
     225    * @see MapCSSRule
     226    */
    84227    public static class GroupedMapCSSRule {
    85228        /** MapCSS selectors **/
     
    430573         * @return argument value, can be {@code null}
    431574         */
    432         static String determineArgument(Selector.GeneralSelector matchingSelector, int index, String type, OsmPrimitive p) {
     575        static String determineArgument(OptimizedGeneralSelector matchingSelector, int index, String type, OsmPrimitive p) {
    433576            try {
    434577                final Condition c = matchingSelector.getConditions().get(index);
     
    462605            if (s != null && matchingSelector instanceof Selector.ChildOrParentSelector) {
    463606                return insertArguments(((Selector.ChildOrParentSelector) matchingSelector).right, s, p);
    464             } else if (s == null || !(matchingSelector instanceof GeneralSelector)) {
     607            } else if (s == null || !(matchingSelector instanceof Selector.OptimizedGeneralSelector)) {
    465608                return s;
    466609            }
     
    468611            final StringBuffer sb = new StringBuffer();
    469612            while (m.find()) {
    470                 final String argument = determineArgument((Selector.GeneralSelector) matchingSelector,
     613                final String argument = determineArgument((Selector.OptimizedGeneralSelector) matchingSelector,
    471614                        Integer.parseInt(m.group(1)), m.group(2), p);
    472615                try {
     
    678821     */
    679822    public synchronized Collection<TestError> getErrorsForPrimitive(OsmPrimitive p, boolean includeOtherSeverity) {
    680         return getErrorsForPrimitive(p, includeOtherSeverity, checks.values());
     823        final List<TestError> res = new ArrayList<>();
     824        if (indexData == null)
     825            indexData = new IndexData(checks);
     826
     827        MapCSSRuleIndex matchingRuleIndex = indexData.get(p);
     828
     829        Environment env = new Environment(p, new MultiCascade(), Environment.DEFAULT_LAYER, null);
     830        // the declaration indices are sorted, so it suffices to save the last used index
     831        Declaration lastDeclUsed = null;
     832
     833        Iterator<MapCSSRule> candidates = matchingRuleIndex.getRuleCandidates(p);
     834        while (candidates.hasNext()) {
     835            MapCSSRule r = candidates.next();
     836            env.clearSelectorMatchingInformation();
     837            if (r.selector.matches(env)) { // as side effect env.parent will be set (if s is a child selector)
     838                TagCheck check = indexData.getCheck(r);
     839                if (check != null) {
     840                    boolean ignoreError = Severity.OTHER == check.getSeverity() && !includeOtherSeverity;
     841                    // Do not run "information" level checks if not wanted, unless they also set a MapCSS class
     842                    if (ignoreError && check.setClassExpressions.isEmpty()) {
     843                        continue;
     844                    }
     845                    if (r.declaration == lastDeclUsed)
     846                        continue; // don't apply one declaration more than once
     847                    lastDeclUsed = r.declaration;
     848
     849                    r.declaration.execute(env);
     850                    if (!ignoreError && !check.errors.isEmpty()) {
     851                        final TestError error = check.getErrorForPrimitive(p, r.selector, env, new MapCSSTagCheckerAndRule(check.rule));
     852                        if (error != null) {
     853                            res.add(error);
     854                        }
     855                    }
     856
     857                }
     858            }
     859        }
     860        return res;
    681861    }
    682862
     
    737917            checks.remove(url);
    738918            checks.putAll(url, result.parseChecks);
     919            indexData = null;
    739920            // Check assertions, useful for development of local files
    740921            if (Config.getPref().getBoolean("validator.check_assert_local_rules", false) && Utils.isLocalUrl(url)) {
     
    750931    public synchronized void initialize() throws Exception {
    751932        checks.clear();
     933        indexData = null;
    752934        for (SourceEntry source : new ValidatorPrefHelper().get()) {
    753935            if (!source.active) {
     
    8421024        }
    8431025    }
     1026
     1027    @Override
     1028    public void startTest(ProgressMonitor progressMonitor) {
     1029        super.startTest(progressMonitor);
     1030        if (indexData == null) {
     1031            indexData = new IndexData(checks);
     1032        }
     1033    }
     1034
     1035    @Override
     1036    public void endTest() {
     1037        super.endTest();
     1038        // no need to keep the index, it is quickly build and doubles the memory needs
     1039        indexData = null;
     1040    }
     1041
    8441042}
Note: See TracChangeset for help on using the changeset viewer.