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}