001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.validation.tests;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.text.MessageFormat;
007import java.util.ArrayList;
008import java.util.HashMap;
009import java.util.List;
010import java.util.Map;
011import java.util.Set;
012
013import org.openstreetmap.josm.data.osm.INode;
014import org.openstreetmap.josm.data.osm.IRelation;
015import org.openstreetmap.josm.data.osm.IWay;
016import org.openstreetmap.josm.data.osm.OsmPrimitive;
017import org.openstreetmap.josm.data.osm.OsmUtils;
018import org.openstreetmap.josm.data.validation.Severity;
019import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker.TagCheck;
020import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
021import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource.MapCSSRuleIndex;
022import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
023import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector;
024import org.openstreetmap.josm.tools.JosmRuntimeException;
025import org.openstreetmap.josm.tools.Logging;
026import org.openstreetmap.josm.tools.MultiMap;
027
028/**
029 * Helper class for {@link MapCSSTagChecker} to store indexes of rules
030 * @author Gerd
031 *
032 */
033final class MapCSSTagCheckerIndex {
034    final Map<MapCSSRule, TagCheck> ruleToCheckMap = new HashMap<>();
035
036    static final boolean ALL_TESTS = true;
037    static final boolean ONLY_SELECTED_TESTS = false;
038
039    /**
040     * Rules for nodes
041     */
042    final MapCSSRuleIndex nodeRules = new MapCSSRuleIndex();
043    /**
044     * Rules for ways without tag area=no
045     */
046    final MapCSSRuleIndex wayRules = new MapCSSRuleIndex();
047    /**
048     * Rules for ways with tag area=no
049     */
050    final MapCSSRuleIndex wayNoAreaRules = new MapCSSRuleIndex();
051    /**
052     * Rules for relations that are not multipolygon relations
053     */
054    final MapCSSRuleIndex relationRules = new MapCSSRuleIndex();
055    /**
056     * Rules for multipolygon relations
057     */
058    final MapCSSRuleIndex multipolygonRules = new MapCSSRuleIndex();
059
060    MapCSSTagCheckerIndex(MultiMap<String, TagCheck> checks, boolean includeOtherSeverity, boolean allTests) {
061        buildIndex(checks, includeOtherSeverity, allTests);
062    }
063
064    private void buildIndex(MultiMap<String, TagCheck> checks, boolean includeOtherSeverity, boolean allTests) {
065        List<TagCheck> allChecks = new ArrayList<>();
066        for (Set<TagCheck> cs : checks.values()) {
067            allChecks.addAll(cs);
068        }
069
070        ruleToCheckMap.clear();
071        nodeRules.clear();
072        wayRules.clear();
073        wayNoAreaRules.clear();
074        relationRules.clear();
075        multipolygonRules.clear();
076
077        // optimization: filter rules for different primitive types
078        for (TagCheck c : allChecks) {
079            if (!includeOtherSeverity && Severity.OTHER == c.getSeverity()
080                    && c.setClassExpressions.isEmpty()) {
081                // Ignore "information" level checks if not wanted, unless they also set a MapCSS class
082                continue;
083            }
084
085            for (Selector s : c.rule.selectors) {
086                // find the rightmost selector, this must be a GeneralSelector
087                boolean hasLeftRightSel = false;
088                Selector selRightmost = s;
089                while (selRightmost instanceof Selector.ChildOrParentSelector) {
090                    hasLeftRightSel = true;
091                    selRightmost = ((Selector.ChildOrParentSelector) selRightmost).right;
092                }
093                if (!allTests && !hasLeftRightSel) {
094                    continue;
095                }
096
097                MapCSSRule optRule = new MapCSSRule(s.optimizedBaseCheck(), c.rule.declaration);
098
099                ruleToCheckMap.put(optRule, c);
100                final String base = ((GeneralSelector) selRightmost).getBase();
101                switch (base) {
102                case Selector.BASE_NODE:
103                    nodeRules.add(optRule);
104                    break;
105                case Selector.BASE_WAY:
106                    wayNoAreaRules.add(optRule);
107                    wayRules.add(optRule);
108                    break;
109                case Selector.BASE_AREA:
110                    wayRules.add(optRule);
111                    multipolygonRules.add(optRule);
112                    break;
113                case Selector.BASE_RELATION:
114                    relationRules.add(optRule);
115                    multipolygonRules.add(optRule);
116                    break;
117                case Selector.BASE_ANY:
118                    nodeRules.add(optRule);
119                    wayRules.add(optRule);
120                    wayNoAreaRules.add(optRule);
121                    relationRules.add(optRule);
122                    multipolygonRules.add(optRule);
123                    break;
124                case Selector.BASE_CANVAS:
125                case Selector.BASE_META:
126                case Selector.BASE_SETTING:
127                    break;
128                default:
129                    final RuntimeException e = new JosmRuntimeException(MessageFormat.format("Unknown MapCSS base selector {0}", base));
130                    Logging.warn(tr("Failed to index validator rules. Error was: {0}", e.getMessage()));
131                    Logging.error(e);
132                }
133            }
134        }
135        nodeRules.initIndex();
136        wayRules.initIndex();
137        wayNoAreaRules.initIndex();
138        relationRules.initIndex();
139        multipolygonRules.initIndex();
140    }
141
142    /**
143     * Get the index of rules for the given primitive.
144     * @param p the primitve
145     * @return index of rules for the given primitive
146     */
147    public MapCSSRuleIndex get(OsmPrimitive p) {
148        if (p instanceof INode) {
149            return nodeRules;
150        } else if (p instanceof IWay) {
151            if (OsmUtils.isFalse(p.get("area"))) {
152                return wayNoAreaRules;
153            } else {
154                return wayRules;
155            }
156        } else if (p instanceof IRelation) {
157            if (((IRelation<?>) p).isMultipolygon()) {
158                return multipolygonRules;
159            } else {
160                return relationRules;
161            }
162        } else {
163            throw new IllegalArgumentException("Unsupported type: " + p);
164        }
165    }
166
167    /**
168     * return the TagCheck for which the given indexed rule was created.
169     * @param rule an indexed rule
170     * @return the original TagCheck
171     */
172    public TagCheck getCheck(MapCSSRule rule) {
173        return ruleToCheckMap.get(rule);
174    }
175}