Changeset 16357 in josm
- Timestamp:
- 2020-04-19T14:54:56+02:00 (5 years ago)
- Location:
- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss
- Files:
-
- 1 added
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleIndex.java
r16172 r16357 17 17 import org.openstreetmap.josm.data.osm.IWay; 18 18 import org.openstreetmap.josm.data.osm.OsmUtils; 19 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource.MapCSSRuleIndex;20 19 import org.openstreetmap.josm.tools.JosmRuntimeException; 21 20 import org.openstreetmap.josm.tools.Logging; -
trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
r16000 r16357 15 15 import java.nio.charset.StandardCharsets; 16 16 import java.util.ArrayList; 17 import java.util.BitSet;18 import java.util.Collections;19 import java.util.HashMap;20 17 import java.util.HashSet; 21 18 import java.util.Iterator; 22 19 import java.util.List; 23 20 import java.util.Locale; 24 import java.util.Map;25 21 import java.util.Map.Entry; 26 import java.util.NoSuchElementException;27 import java.util.Optional;28 22 import java.util.Set; 29 23 import java.util.concurrent.locks.ReadWriteLock; … … 35 29 import org.openstreetmap.josm.data.Version; 36 30 import org.openstreetmap.josm.data.osm.IPrimitive; 37 import org.openstreetmap.josm.data.osm.KeyValueVisitor;38 31 import org.openstreetmap.josm.data.osm.Node; 39 import org.openstreetmap.josm.data.osm.Tagged;40 32 import org.openstreetmap.josm.data.preferences.sources.SourceEntry; 41 33 import org.openstreetmap.josm.gui.mappaint.Cascade; … … 48 40 import org.openstreetmap.josm.gui.mappaint.StyleSettingFactory; 49 41 import org.openstreetmap.josm.gui.mappaint.StyleSource; 50 import org.openstreetmap.josm.gui.mappaint.mapcss.ConditionFactory.KeyCondition;51 import org.openstreetmap.josm.gui.mappaint.mapcss.ConditionFactory.KeyMatchType;52 import org.openstreetmap.josm.gui.mappaint.mapcss.ConditionFactory.KeyValueCondition;53 import org.openstreetmap.josm.gui.mappaint.mapcss.ConditionFactory.SimpleKeyValueCondition;54 42 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector; 55 43 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser; … … 131 119 SUPPORTED_KEYS.add(lt.prefix + StyleKeys.REAL_WIDTH); 132 120 SUPPORTED_KEYS.add(lt.prefix + StyleKeys.WIDTH); 133 }134 }135 136 /**137 * A collection of {@link MapCSSRule}s, that are indexed by tag key and value.138 *139 * Speeds up the process of finding all rules that match a certain primitive.140 *141 * Rules with a {@link SimpleKeyValueCondition} [key=value] or rules that require a specific key to be set are142 * indexed. Now you only need to loop the tags of a primitive to retrieve the possibly matching rules.143 *144 * To use this index, you need to {@link #add(MapCSSRule)} all rules to it. You then need to call145 * {@link #initIndex()}. Afterwards, you can use {@link #getRuleCandidates(IPrimitive)} to get an iterator over146 * all rules that might be applied to that primitive.147 */148 public static class MapCSSRuleIndex {149 /**150 * This is an iterator over all rules that are marked as possible in the bitset.151 *152 * @author Michael Zangl153 */154 private final class RuleCandidatesIterator implements Iterator<MapCSSRule>, KeyValueVisitor {155 private final BitSet ruleCandidates;156 private int next;157 158 private RuleCandidatesIterator(BitSet ruleCandidates) {159 this.ruleCandidates = ruleCandidates;160 }161 162 @Override163 public boolean hasNext() {164 return next >= 0 && next < rules.size();165 }166 167 @Override168 public MapCSSRule next() {169 if (!hasNext())170 throw new NoSuchElementException();171 MapCSSRule rule = rules.get(next);172 next = ruleCandidates.nextSetBit(next + 1);173 return rule;174 }175 176 @Override177 public void remove() {178 throw new UnsupportedOperationException();179 }180 181 @Override182 public void visitKeyValue(Tagged p, String key, String value) {183 MapCSSKeyRules v = index.get(key);184 if (v != null) {185 BitSet rs = v.get(value);186 ruleCandidates.or(rs);187 }188 }189 190 /**191 * Call this before using the iterator.192 */193 public void prepare() {194 next = ruleCandidates.nextSetBit(0);195 }196 }197 198 /**199 * This is a map of all rules that are only applied if the primitive has a given key (and possibly value)200 *201 * @author Michael Zangl202 */203 private static final class MapCSSKeyRules {204 /**205 * The indexes of rules that might be applied if this tag is present and the value has no special handling.206 */207 BitSet generalRules = new BitSet();208 209 /**210 * A map that sores the indexes of rules that might be applied if the key=value pair is present on this211 * primitive. This includes all key=* rules.212 */213 Map<String, BitSet> specialRules = new HashMap<>();214 215 public void addForKey(int ruleIndex) {216 generalRules.set(ruleIndex);217 for (BitSet r : specialRules.values()) {218 r.set(ruleIndex);219 }220 }221 222 public void addForKeyAndValue(String value, int ruleIndex) {223 BitSet forValue = specialRules.get(value);224 if (forValue == null) {225 forValue = new BitSet();226 forValue.or(generalRules);227 specialRules.put(value.intern(), forValue);228 }229 forValue.set(ruleIndex);230 }231 232 public BitSet get(String value) {233 BitSet forValue = specialRules.get(value);234 if (forValue != null) return forValue; else return generalRules;235 }236 }237 238 /**239 * All rules this index is for. Once this index is built, this list is sorted.240 */241 private final List<MapCSSRule> rules = new ArrayList<>();242 /**243 * All rules that only apply when the given key is present.244 */245 private final Map<String, MapCSSKeyRules> index = new HashMap<>();246 /**247 * Rules that do not require any key to be present. Only the index in the {@link #rules} array is stored.248 */249 private final BitSet remaining = new BitSet();250 251 /**252 * Add a rule to this index. This needs to be called before {@link #initIndex()} is called.253 * @param rule The rule to add.254 */255 public void add(MapCSSRule rule) {256 rules.add(rule);257 }258 259 /**260 * Initialize the index.261 * <p>262 * You must own the write lock of STYLE_SOURCE_LOCK when calling this method.263 */264 public void initIndex() {265 Collections.sort(rules);266 for (int ruleIndex = 0; ruleIndex < rules.size(); ruleIndex++) {267 MapCSSRule r = rules.get(ruleIndex);268 for (Selector selector : r.selectors) {269 Selector selRightmost = selector;270 while (selRightmost instanceof Selector.ChildOrParentSelector) {271 selRightmost = ((Selector.ChildOrParentSelector) selRightmost).right;272 }273 final List<Condition> conditions = selRightmost.getConditions();274 if (conditions == null || conditions.isEmpty()) {275 remaining.set(ruleIndex);276 continue;277 }278 Optional<SimpleKeyValueCondition> lastCondition = Utils.filteredCollection(conditions, SimpleKeyValueCondition.class)279 .stream()280 .reduce((first, last) -> last);281 if (lastCondition.isPresent()) {282 getEntryInIndex(lastCondition.get().k).addForKeyAndValue(lastCondition.get().v, ruleIndex);283 } else {284 String key = findAnyRequiredKey(conditions);285 if (key != null) {286 getEntryInIndex(key).addForKey(ruleIndex);287 } else {288 remaining.set(ruleIndex);289 }290 }291 }292 }293 }294 295 /**296 * Search for any key that condition might depend on.297 *298 * @param conds The conditions to search through.299 * @return An arbitrary key this rule depends on or <code>null</code> if there is no such key.300 */301 private static String findAnyRequiredKey(List<Condition> conds) {302 String key = null;303 for (Condition c : conds) {304 if (c instanceof KeyCondition) {305 KeyCondition keyCondition = (KeyCondition) c;306 if (!keyCondition.negateResult && conditionRequiresKeyPresence(keyCondition.matchType)) {307 key = keyCondition.label;308 }309 } else if (c instanceof KeyValueCondition) {310 KeyValueCondition keyValueCondition = (KeyValueCondition) c;311 if (keyValueCondition.requiresExactKeyMatch()) {312 key = keyValueCondition.k;313 }314 }315 }316 return key;317 }318 319 private static boolean conditionRequiresKeyPresence(KeyMatchType matchType) {320 return matchType != KeyMatchType.REGEX;321 }322 323 private MapCSSKeyRules getEntryInIndex(String key) {324 MapCSSKeyRules rulesWithMatchingKey = index.get(key);325 if (rulesWithMatchingKey == null) {326 rulesWithMatchingKey = new MapCSSKeyRules();327 index.put(key.intern(), rulesWithMatchingKey);328 }329 return rulesWithMatchingKey;330 }331 332 /**333 * Get a subset of all rules that might match the primitive. Rules not included in the result are guaranteed to334 * not match this primitive.335 * <p>336 * You must have a read lock of STYLE_SOURCE_LOCK when calling this method.337 *338 * @param osm the primitive to match339 * @return An iterator over possible rules in the right order.340 * @since 13810 (signature)341 */342 public Iterator<MapCSSRule> getRuleCandidates(IPrimitive osm) {343 final BitSet ruleCandidates = new BitSet(rules.size());344 ruleCandidates.or(remaining);345 346 final RuleCandidatesIterator candidatesIterator = new RuleCandidatesIterator(ruleCandidates);347 osm.visitKeys(candidatesIterator);348 candidatesIterator.prepare();349 return candidatesIterator;350 }351 352 /**353 * Clear the index.354 * <p>355 * You must own the write lock STYLE_SOURCE_LOCK when calling this method.356 */357 public void clear() {358 rules.clear();359 index.clear();360 remaining.clear();361 121 } 362 122 }
Note:
See TracChangeset
for help on using the changeset viewer.