Ticket #11382: mapcss-use-bitmap-and-keycache.patch
File mapcss-use-bitmap-and-keycache.patch, 12.2 KB (added by , 10 years ago) |
---|
-
src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java
diff --git a/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java b/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java index a6806c4..f4eb50c 100644
a b public abstract class Condition { 83 83 EQ, NEQ, GREATER_OR_EQUAL, GREATER, LESS_OR_EQUAL, LESS, 84 84 REGEX, NREGEX, ONE_OF, BEGINS_WITH, ENDS_WITH, CONTAINS; 85 85 86 p rivatestatic final Set<Op> NEGATED_OPS = EnumSet.of(NEQ, NREGEX);86 public static final Set<Op> NEGATED_OPS = EnumSet.of(NEQ, NREGEX); 87 87 88 88 public boolean eval(String testString, String prototypeString) { 89 89 if (testString == null && !NEGATED_OPS.contains(this)) -
src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
diff --git a/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java b/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java index c6f062f..a6c75fc 100644
a b import java.lang.reflect.Field; 12 12 import java.nio.charset.StandardCharsets; 13 13 import java.text.MessageFormat; 14 14 import java.util.ArrayList; 15 import java.util.BitSet; 15 16 import java.util.Collections; 16 17 import java.util.HashMap; 17 18 import java.util.HashSet; 19 import java.util.Iterator; 18 20 import java.util.List; 19 21 import java.util.Map; 20 22 import java.util.Map.Entry; 21 import java.util.PriorityQueue;22 23 import java.util.Set; 23 24 import java.util.concurrent.locks.ReadWriteLock; 24 25 import java.util.concurrent.locks.ReentrantReadWriteLock; … … import org.openstreetmap.josm.gui.mappaint.StyleKeys; 40 41 import org.openstreetmap.josm.gui.mappaint.StyleSetting; 41 42 import org.openstreetmap.josm.gui.mappaint.StyleSetting.BooleanStyleSetting; 42 43 import org.openstreetmap.josm.gui.mappaint.StyleSource; 44 import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.KeyCondition; 45 import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.KeyMatchType; 46 import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.KeyValueCondition; 47 import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.Op; 43 48 import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.SimpleKeyValueCondition; 44 49 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.ChildOrParentSelector; 45 50 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector; … … public class MapCSSStyleSource extends StyleSource { 131 136 * The order of rules gets mixed up by this and needs to be sorted later. 132 137 */ 133 138 public static class MapCSSRuleIndex { 134 /* all rules for this index */ 135 public final List<MapCSSRule> rules = new ArrayList<>(); 139 private final class RuleCandidatesIterator implements Iterator<MapCSSRule> { 140 private final BitSet ruleCandidates; 141 private int next; 142 143 private RuleCandidatesIterator(BitSet ruleCandidates) { 144 this.ruleCandidates = ruleCandidates; 145 next = ruleCandidates.nextSetBit(0); 146 } 147 148 @Override 149 public boolean hasNext() { 150 return next >= 0; 151 } 152 153 @Override 154 public MapCSSRule next() { 155 MapCSSRule rule = rules.get(next); 156 next = ruleCandidates.nextSetBit(next + 1); 157 return rule; 158 } 159 160 @Override 161 public void remove() { 162 throw new UnsupportedOperationException(); 163 } 164 } 165 166 /** 167 * This is a map of all rules that are only applied if the primitive has a given key (and possibly value) 168 */ 169 private final static class MapCSSKeyRules { 170 BitSet generalRules = new BitSet(); 171 HashMap<String, BitSet> specialRules = new HashMap<>(); 172 173 public void addGeneral(int ruleIndex) { 174 generalRules.set(ruleIndex); 175 for (BitSet r : specialRules.values()) { 176 r.set(ruleIndex); 177 } 178 } 179 180 public void addSpecial(String value, int ruleIndex) { 181 BitSet forValue = specialRules.get(value); 182 if (forValue == null) { 183 forValue = new BitSet(); 184 forValue.or(generalRules); 185 specialRules.put(value.intern(), forValue); 186 } 187 forValue.set(ruleIndex); 188 } 189 190 public BitSet get(String value) { 191 BitSet forValue = specialRules.get(value); 192 if (forValue != null) return forValue; else return generalRules; 193 } 194 } 195 196 /* all rules for this index. This is sorted on every #initIndex() */ 197 private final List<MapCSSRule> rules = new ArrayList<>(); 136 198 /* tag based index */ 137 p ublic final Map<String,Map<String,Set<MapCSSRule>>> index = new HashMap<>();199 private final Map<String, MapCSSKeyRules> index = new HashMap<>(); 138 200 /* rules without SimpleKeyValueCondition */ 139 p ublic final ArrayList<MapCSSRule> remaining = new ArrayList<>();201 private final BitSet remaining = new BitSet(); 140 202 203 /** 204 * Add a rule to this index. This needs to be called before {@link #initIndex()} is called. 205 * @param rule The rule to add. 206 */ 141 207 public void add(MapCSSRule rule) { 142 208 rules.add(rule); 143 209 } 144 210 145 211 /** 146 212 * Initialize the index. 147 * 213 * <p> 148 214 * You must own the write lock of STYLE_SOURCE_LOCK when calling this method. 149 215 */ 150 216 public void initIndex() { 151 for (MapCSSRule r: rules) { 217 Collections.sort(rules); 218 for (int ruleIndex = 0; ruleIndex < rules.size(); ruleIndex++) { 219 MapCSSRule r = rules.get(ruleIndex); 152 220 // find the rightmost selector, this must be a GeneralSelector 153 221 Selector selRightmost = r.selector; 154 222 while (selRightmost instanceof ChildOrParentSelector) { … … public class MapCSSStyleSource extends StyleSource { 156 224 } 157 225 OptimizedGeneralSelector s = (OptimizedGeneralSelector) selRightmost; 158 226 if (s.conds == null) { 159 remaining. add(r);227 remaining.set(ruleIndex); 160 228 continue; 161 229 } 162 List<SimpleKeyValueCondition> sk = new ArrayList<>(Utils.filteredCollection(s.conds, SimpleKeyValueCondition.class)); 163 if (sk.isEmpty()) { 164 remaining.add(r); 165 continue; 166 } 167 SimpleKeyValueCondition c = sk.get(sk.size() - 1); 168 Map<String,Set<MapCSSRule>> rulesWithMatchingKey = index.get(c.k); 169 if (rulesWithMatchingKey == null) { 170 rulesWithMatchingKey = new HashMap<>(); 171 index.put(c.k, rulesWithMatchingKey); 230 List<SimpleKeyValueCondition> sk = new ArrayList<>(Utils.filteredCollection(s.conds, 231 SimpleKeyValueCondition.class)); 232 if (!sk.isEmpty()) { 233 SimpleKeyValueCondition c = sk.get(sk.size() - 1); 234 getEntryInIndex(c.k).addSpecial(c.v, ruleIndex); 235 } else { 236 String key = findAnyRequiredKey(s.conds); 237 if (key != null) { 238 getEntryInIndex(key).addGeneral(ruleIndex); 239 } else { 240 remaining.set(ruleIndex); 241 } 172 242 } 173 Set<MapCSSRule> rulesWithMatchingKeyValue = rulesWithMatchingKey.get(c.v); 174 if (rulesWithMatchingKeyValue == null) { 175 rulesWithMatchingKeyValue = new HashSet<>(); 176 rulesWithMatchingKey.put(c.v, rulesWithMatchingKeyValue); 243 } 244 } 245 246 private String findAnyRequiredKey(List<Condition> conds) { 247 String key = null; 248 for (Condition c : conds) { 249 if (c instanceof KeyCondition) { 250 KeyCondition keyCondition = (KeyCondition) c; 251 if (!keyCondition.negateResult && conditionRequiresKeyPresence(keyCondition.matchType)) { 252 key = keyCondition.label; 253 } 254 } else if (c instanceof KeyValueCondition) { 255 KeyValueCondition keyValueCondition = (KeyValueCondition) c; 256 if (!Op.NEGATED_OPS.contains(keyValueCondition)) { 257 key = keyValueCondition.k; 258 } 177 259 } 178 rulesWithMatchingKeyValue.add(r);179 260 } 180 Collections.sort(remaining); 261 return key; 262 } 263 264 private boolean conditionRequiresKeyPresence(KeyMatchType matchType) { 265 return matchType != KeyMatchType.REGEX; 266 } 267 268 private MapCSSKeyRules getEntryInIndex(String key) { 269 MapCSSKeyRules rulesWithMatchingKey = index.get(key); 270 if (rulesWithMatchingKey == null) { 271 rulesWithMatchingKey = new MapCSSKeyRules(); 272 index.put(key.intern(), rulesWithMatchingKey); 273 } 274 return rulesWithMatchingKey; 181 275 } 182 276 183 277 /** 184 278 * Get a subset of all rules that might match the primitive. 279 * <p> 280 * You must have a read lock of STYLE_SOURCE_LOCK when calling this method. 185 281 * @param osm the primitive to match 186 282 * @return a Collection of rules that filters out most of the rules 187 283 * that cannot match, based on the tags of the primitive 188 *189 * You must have a read lock of STYLE_SOURCE_LOCK when calling this method.190 284 */ 191 public PriorityQueue<MapCSSRule> getRuleCandidates(OsmPrimitive osm) { 192 PriorityQueue<MapCSSRule> ruleCandidates = new PriorityQueue<>(remaining); 193 for (Map.Entry<String,String> e : osm.getKeys().entrySet()) { 194 Map<String,Set<MapCSSRule>> v = index.get(e.getKey()); 285 public Iterator<MapCSSRule> getRuleCandidates(OsmPrimitive osm) { 286 final BitSet ruleCandidates = new BitSet(rules.size()); 287 ruleCandidates.or(remaining); 288 289 for (Map.Entry<String, String> e : osm.getKeys().entrySet()) { 290 MapCSSKeyRules v = index.get(e.getKey()); 195 291 if (v != null) { 196 Set<MapCSSRule> rs = v.get(e.getValue()); 197 if (rs != null) { 198 ruleCandidates.addAll(rs); 199 } 292 BitSet rs = v.get(e.getValue()); 293 ruleCandidates.or(rs); 200 294 } 201 295 } 202 return ruleCandidates;296 return new RuleCandidatesIterator(ruleCandidates); 203 297 } 204 298 205 299 /** 206 300 * Clear the index. 207 * 301 * <p> 208 302 * You must own the write lock STYLE_SOURCE_LOCK when calling this method. 209 303 */ 210 304 public void clear() { … … public class MapCSSStyleSource extends StyleSource { 488 582 // last used index 489 583 int lastDeclUsed = -1; 490 584 491 PriorityQueue<MapCSSRule> candidates = matchingRuleIndex.getRuleCandidates(osm);492 MapCSSRule r;493 while ((r = candidates.poll()) != null) {585 Iterator<MapCSSRule> candidates = matchingRuleIndex.getRuleCandidates(osm); 586 while (candidates.hasNext()) { 587 MapCSSRule r = candidates.next(); 494 588 env.clearSelectorMatchingInformation(); 495 589 env.layer = null; 496 590 String sub = env.layer = r.selector.getSubpart().getId(env); … … public class MapCSSStyleSource extends StyleSource { 503 597 continue; 504 598 } 505 599 506 if (r.declaration.idx == lastDeclUsed) continue; // don't apply one declaration more than once 600 if (r.declaration.idx == lastDeclUsed) 601 continue; // don't apply one declaration more than once 507 602 lastDeclUsed = r.declaration.idx; 508 603 if ("*".equals(sub)) { 509 604 for (Entry<String, Cascade> entry : mc.getLayers()) {