Ticket #11382: mapcss-use-bitmap-and-keycache.patch

File mapcss-use-bitmap-and-keycache.patch, 12.2 KB (added by michael2402, 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 {  
    8383        EQ, NEQ, GREATER_OR_EQUAL, GREATER, LESS_OR_EQUAL, LESS,
    8484        REGEX, NREGEX, ONE_OF, BEGINS_WITH, ENDS_WITH, CONTAINS;
    8585
    86         private static final Set<Op> NEGATED_OPS = EnumSet.of(NEQ, NREGEX);
     86        public static final Set<Op> NEGATED_OPS = EnumSet.of(NEQ, NREGEX);
    8787
    8888        public boolean eval(String testString, String prototypeString) {
    8989            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;  
    1212import java.nio.charset.StandardCharsets;
    1313import java.text.MessageFormat;
    1414import java.util.ArrayList;
     15import java.util.BitSet;
    1516import java.util.Collections;
    1617import java.util.HashMap;
    1718import java.util.HashSet;
     19import java.util.Iterator;
    1820import java.util.List;
    1921import java.util.Map;
    2022import java.util.Map.Entry;
    21 import java.util.PriorityQueue;
    2223import java.util.Set;
    2324import java.util.concurrent.locks.ReadWriteLock;
    2425import java.util.concurrent.locks.ReentrantReadWriteLock;
    import org.openstreetmap.josm.gui.mappaint.StyleKeys;  
    4041import org.openstreetmap.josm.gui.mappaint.StyleSetting;
    4142import org.openstreetmap.josm.gui.mappaint.StyleSetting.BooleanStyleSetting;
    4243import org.openstreetmap.josm.gui.mappaint.StyleSource;
     44import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.KeyCondition;
     45import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.KeyMatchType;
     46import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.KeyValueCondition;
     47import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.Op;
    4348import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.SimpleKeyValueCondition;
    4449import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.ChildOrParentSelector;
    4550import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector;
    public class MapCSSStyleSource extends StyleSource {  
    131136     * The order of rules gets mixed up by this and needs to be sorted later.
    132137     */
    133138    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<>();
    136198        /* tag based index */
    137         public final Map<String,Map<String,Set<MapCSSRule>>> index = new HashMap<>();
     199        private final Map<String, MapCSSKeyRules> index = new HashMap<>();
    138200        /* rules without SimpleKeyValueCondition */
    139         public final ArrayList<MapCSSRule> remaining = new ArrayList<>();
     201        private final BitSet remaining = new BitSet();
    140202
     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         */
    141207        public void add(MapCSSRule rule) {
    142208            rules.add(rule);
    143209        }
    144210
    145211        /**
    146212         * Initialize the index.
    147          *
     213         * <p>
    148214         * You must own the write lock of STYLE_SOURCE_LOCK when calling this method.
    149215         */
    150216        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);
    152220                // find the rightmost selector, this must be a GeneralSelector
    153221                Selector selRightmost = r.selector;
    154222                while (selRightmost instanceof ChildOrParentSelector) {
    public class MapCSSStyleSource extends StyleSource {  
    156224                }
    157225                OptimizedGeneralSelector s = (OptimizedGeneralSelector) selRightmost;
    158226                if (s.conds == null) {
    159                     remaining.add(r);
     227                    remaining.set(ruleIndex);
    160228                    continue;
    161229                }
    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                    }
    172242                }
    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                    }
    177259                }
    178                 rulesWithMatchingKeyValue.add(r);
    179260            }
    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;
    181275        }
    182276
    183277        /**
    184278         * 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.
    185281         * @param osm the primitive to match
    186282         * @return a Collection of rules that filters out most of the rules
    187283         * 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.
    190284         */
    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());
    195291                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);
    200294                }
    201295            }
    202             return ruleCandidates;
     296            return new RuleCandidatesIterator(ruleCandidates);
    203297        }
    204298
    205299        /**
    206300         * Clear the index.
    207          *
     301         * <p>
    208302         * You must own the write lock STYLE_SOURCE_LOCK when calling this method.
    209303         */
    210304        public void clear() {
    public class MapCSSStyleSource extends StyleSource {  
    488582        // last used index
    489583        int lastDeclUsed = -1;
    490584
    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();
    494588            env.clearSelectorMatchingInformation();
    495589            env.layer = null;
    496590            String sub = env.layer = r.selector.getSubpart().getId(env);
    public class MapCSSStyleSource extends StyleSource {  
    503597                    continue;
    504598                }
    505599
    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
    507602                lastDeclUsed = r.declaration.idx;
    508603                if ("*".equals(sub)) {
    509604                    for (Entry<String, Cascade> entry : mc.getLayers()) {