diff --git a/src/org/oscim/theme/RenderTheme.java b/src/org/oscim/theme/RenderTheme.java index 293cc3e2..ec653fae 100644 --- a/src/org/oscim/theme/RenderTheme.java +++ b/src/org/oscim/theme/RenderTheme.java @@ -18,10 +18,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.oscim.core.GeometryBuffer.GeometryType; import org.oscim.core.MapElement; import org.oscim.theme.renderinstruction.RenderInstruction; -import org.oscim.theme.rule.Closed; +import org.oscim.theme.rule.Element; import org.oscim.theme.rule.Rule; import org.oscim.utils.LRUCache; import org.xml.sax.Attributes; @@ -91,6 +90,7 @@ public class RenderTheme implements IRenderTheme { private Rule[] mRules; class ElementCache { + final int matchType; final LRUCache cache; final MatchingCacheKey cacheKey; @@ -99,10 +99,11 @@ public class RenderTheme implements IRenderTheme { RenderInstructionItem prevItem; - public ElementCache() { + public ElementCache(int type) { cache = new LRUCache(MATCHING_CACHE_SIZE); instructionList = new ArrayList(4); cacheKey = new MatchingCacheKey(); + matchType = type; } } @@ -121,9 +122,9 @@ public class RenderTheme implements IRenderTheme { mBaseTextSize = baseTextSize; mElementCache = new ElementCache[3]; - for (int i = 0; i < 3; i++) - mElementCache[i] = new ElementCache(); - + mElementCache[0] = new ElementCache(Element.NODE); + mElementCache[1] = new ElementCache(Element.LINE); + mElementCache[2] = new ElementCache(Element.POLY); } /* @@ -214,16 +215,8 @@ public class RenderTheme implements IRenderTheme { List matches = cache.instructionList; matches.clear(); - if (element.type == GeometryType.LINE) { - for (Rule rule : mRules) - rule.matchWay(element.tags, zoomMask, Closed.NO, matches); - } else if (element.type == GeometryType.POLY) { - for (Rule rule : mRules) - rule.matchWay(element.tags, zoomMask, Closed.YES, matches); - } else { - for (Rule rule : mRules) - rule.matchNode(element.tags, zoomMask, matches); - } + for (Rule rule : mRules) + rule.matchElement(cache.matchType, element.tags, zoomMask, matches); int size = matches.size(); if (size > 1) { diff --git a/src/org/oscim/theme/RenderThemeHandler.java b/src/org/oscim/theme/RenderThemeHandler.java index 34f9d694..88c05fdc 100644 --- a/src/org/oscim/theme/RenderThemeHandler.java +++ b/src/org/oscim/theme/RenderThemeHandler.java @@ -117,12 +117,13 @@ public class RenderThemeHandler extends DefaultHandler { private Rule mCurrentRule; private final Stack mElementStack = new Stack(); - private int mLevel; - private RenderTheme mRenderTheme; private final Stack mRuleStack = new Stack(); private final HashMap tmpStyleHash = new HashMap(10); + private int mLevel; + private RenderTheme mRenderTheme; + @Override public void endDocument() { if (mRenderTheme == null) { @@ -130,8 +131,11 @@ public class RenderThemeHandler extends DefaultHandler { } mRenderTheme.complete(mRulesList, mLevel); + mRulesList.clear(); tmpStyleHash.clear(); + mRuleStack.clear(); + mElementStack.clear(); } @Override @@ -153,7 +157,6 @@ public class RenderThemeHandler extends DefaultHandler { Log.d(TAG, exception.getMessage()); } - @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { diff --git a/src/org/oscim/theme/rule/Element.java b/src/org/oscim/theme/rule/Element.java index bfcf8988..ca2dcc71 100644 --- a/src/org/oscim/theme/rule/Element.java +++ b/src/org/oscim/theme/rule/Element.java @@ -15,8 +15,10 @@ */ package org.oscim.theme.rule; -final class Element { +public final class Element { public static final int NODE = 1 << 0; - public static final int WAY = 1 << 1; + public static final int LINE = 1 << 1; + public static final int POLY = 1 << 2; + public static final int WAY = LINE | POLY; public static final int ANY = NODE | WAY; } diff --git a/src/org/oscim/theme/rule/NegativeMatcher.java b/src/org/oscim/theme/rule/NegativeMatcher.java index 11afdf4a..b9f78cb0 100644 --- a/src/org/oscim/theme/rule/NegativeMatcher.java +++ b/src/org/oscim/theme/rule/NegativeMatcher.java @@ -21,6 +21,14 @@ import org.oscim.core.Tag; class NegativeMatcher implements AttributeMatcher { private final String[] mKeyList; private final String[] mValueList; + + // - exclusive negation matches when either KEY is not present + // or KEY is present and any VALUE is NOT present + // + // - non-exclusive negation matches when either KEY is not present + // or KEY is present and any VALUE is present + // + // - TODO 'MUST NOT contain key' private final boolean mExclusive; NegativeMatcher(List keyList, List valueList, boolean exclusive) { @@ -42,25 +50,23 @@ class NegativeMatcher implements AttributeMatcher { @Override public boolean matches(Tag[] tags) { - if (keyListDoesNotContainKeys(tags)) { + if (keyListDoesNotContainKeys(tags)) return true; - } - for (Tag tag : tags) { + for (Tag tag : tags) for (String value : mValueList) if (value == tag.value) return !mExclusive; - } + return mExclusive; } private boolean keyListDoesNotContainKeys(Tag[] tags) { - for (Tag tag : tags) { + for (Tag tag : tags) for (String key : mKeyList) if (key == tag.key) return false; - } return true; } } diff --git a/src/org/oscim/theme/rule/NegativeRule.java b/src/org/oscim/theme/rule/NegativeRule.java index 7a68c8a3..462f4a55 100644 --- a/src/org/oscim/theme/rule/NegativeRule.java +++ b/src/org/oscim/theme/rule/NegativeRule.java @@ -1,5 +1,6 @@ /* * Copyright 2010, 2011, 2012 mapsforge.org + * Copyright 2013 Hannes Janetzek * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software @@ -19,20 +20,14 @@ import org.oscim.core.Tag; class NegativeRule extends Rule { final AttributeMatcher mAttributeMatcher; - NegativeRule(int element, int closed, int zoom, - AttributeMatcher attributeMatcher) { - super(element, closed, zoom); + NegativeRule(int element, int zoom, AttributeMatcher attributeMatcher) { + super(element, zoom); mAttributeMatcher = attributeMatcher; } @Override - boolean matchesNode(Tag[] tags) { - return mAttributeMatcher.matches(tags); - } - - @Override - boolean matchesWay(Tag[] tags) { + boolean matchesTags(Tag[] tags) { return mAttributeMatcher.matches(tags); } } diff --git a/src/org/oscim/theme/rule/PositiveRule.java b/src/org/oscim/theme/rule/PositiveRule.java index a5e64798..90e85b96 100644 --- a/src/org/oscim/theme/rule/PositiveRule.java +++ b/src/org/oscim/theme/rule/PositiveRule.java @@ -20,9 +20,8 @@ class PositiveRule extends Rule { final AttributeMatcher mKeyMatcher; final AttributeMatcher mValueMatcher; - PositiveRule(int element, int closed, int zoom, - AttributeMatcher keyMatcher, AttributeMatcher valueMatcher) { - super(element, closed, zoom); + PositiveRule(int element, int zoom, AttributeMatcher keyMatcher, AttributeMatcher valueMatcher) { + super(element, zoom); if (keyMatcher instanceof AnyMatcher) mKeyMatcher = null; @@ -36,14 +35,8 @@ class PositiveRule extends Rule { } @Override - boolean matchesNode(Tag[] tags) { + boolean matchesTags(Tag[] tags) { return (mKeyMatcher == null || mKeyMatcher.matches(tags)) && (mValueMatcher == null || mValueMatcher.matches(tags)); } - - @Override - boolean matchesWay(Tag[] tags) { - return (mKeyMatcher == null || mKeyMatcher.matches(tags)) && - (mValueMatcher == null || mValueMatcher.matches(tags)); - } } diff --git a/src/org/oscim/theme/rule/Rule.java b/src/org/oscim/theme/rule/Rule.java index 4d9abce4..a78dd201 100644 --- a/src/org/oscim/theme/rule/Rule.java +++ b/src/org/oscim/theme/rule/Rule.java @@ -37,40 +37,57 @@ public abstract class Rule { private static final String STRING_WILDCARD = "*"; private static Rule createRule(Stack ruleStack, int element, String keys, - String values, int closed, - byte zoomMin, byte zoomMax) { - - List keyList = new ArrayList(Arrays.asList(SPLIT_PATTERN - .split(keys))); - List valueList = new ArrayList(Arrays.asList(SPLIT_PATTERN - .split(values))); + String values, byte zoomMin, byte zoomMax) { int zoom = 0; for (int z = zoomMin; z <= zoomMax && z < 32; z++) zoom |= (1 << z); - if (valueList.remove(STRING_NEGATION)) { - AttributeMatcher attributeMatcher = new NegativeMatcher(keyList, valueList, - false); - return new NegativeRule(element, closed, zoom, - attributeMatcher); + List keyList = null, valueList = null; + boolean negativeRule = false; + boolean exclusionRule = false; + + AttributeMatcher keyMatcher, valueMatcher = null; + + if (values == null) { + valueMatcher = AnyMatcher.getInstance(); + } else { + valueList = new ArrayList(Arrays.asList(SPLIT_PATTERN.split(values))); + if (valueList.remove(STRING_NEGATION)) + negativeRule = true; + else if (valueList.remove(STRING_EXCLUSIVE)) + exclusionRule = true; + else { + valueMatcher = getValueMatcher(valueList); + valueMatcher = RuleOptimizer.optimize(valueMatcher, ruleStack); + } } + if (keys == null) { + if (negativeRule || exclusionRule) { + throw new IllegalArgumentException("negative rule requires key"); + } + keyMatcher = AnyMatcher.getInstance(); + } else { + keyList = new ArrayList(Arrays.asList(SPLIT_PATTERN.split(keys))); + keyMatcher = getKeyMatcher(keyList); + if ((keyMatcher instanceof AnyMatcher) && (negativeRule || exclusionRule)) { + throw new IllegalArgumentException("negative rule requires key"); + } - if (valueList.remove(STRING_EXCLUSIVE)) { - AttributeMatcher attributeMatcher = new NegativeMatcher(keyList, valueList, - true); - return new NegativeRule(element, closed, zoom, attributeMatcher); + if (negativeRule) { + AttributeMatcher attributeMatcher = new NegativeMatcher(keyList, valueList, false); + return new NegativeRule(element, zoom, attributeMatcher); + } else if (exclusionRule) { + AttributeMatcher attributeMatcher = new NegativeMatcher(keyList, valueList, true); + return new NegativeRule(element, zoom, attributeMatcher); + } + + keyMatcher = RuleOptimizer.optimize(keyMatcher, ruleStack); } - AttributeMatcher keyMatcher = getKeyMatcher(keyList); - AttributeMatcher valueMatcher = getValueMatcher(valueList); - keyMatcher = RuleOptimizer.optimize(keyMatcher, ruleStack); - valueMatcher = RuleOptimizer.optimize(valueMatcher, ruleStack); - - return new PositiveRule(element, closed, zoom, - keyMatcher, valueMatcher); + return new PositiveRule(element, zoom, keyMatcher, valueMatcher); } private static AttributeMatcher getKeyMatcher(List keyList) { @@ -107,15 +124,8 @@ public abstract class Rule { return attributeMatcher; } - private static void validate(String elementName, String keys, String values, - byte zoomMin, byte zoomMax) { - if (keys == null) { - throw new IllegalArgumentException("missing attribute k for element: " - + elementName); - } else if (values == null) { - throw new IllegalArgumentException("missing attribute v for element: " - + elementName); - } else if (zoomMin < 0) { + private static void validate(byte zoomMin, byte zoomMax) { + if (zoomMin < 0) { throw new IllegalArgumentException("zoom-min must not be negative: " + zoomMin); } else if (zoomMax < 0) { @@ -129,9 +139,9 @@ public abstract class Rule { public static Rule create(String elementName, Attributes attributes, Stack ruleStack) { int element = Element.ANY; + int closed = Closed.ANY; String keys = null; String values = null; - int closed = Closed.ANY; byte zoomMin = 0; byte zoomMax = Byte.MAX_VALUE; @@ -164,8 +174,13 @@ public abstract class Rule { } } - validate(elementName, keys, values, zoomMin, zoomMax); - return createRule(ruleStack, element, keys, values, closed, zoomMin, zoomMax); + if (closed == Closed.YES) + element = Element.POLY; + else if (closed == Closed.NO) + element = Element.LINE; + + validate(zoomMin, zoomMax); + return createRule(ruleStack, element, keys, values, zoomMin, zoomMax); } private ArrayList mRenderInstructions; @@ -176,12 +191,10 @@ public abstract class Rule { final int mZoom; final int mElement; - final int mClosed; - Rule(int element, int closed, int zoom) { + Rule(int type, int zoom) { - mClosed = closed; - mElement = element; + mElement = type; mZoom = zoom; mRenderInstructions = new ArrayList(4); @@ -196,32 +209,12 @@ public abstract class Rule { mSubRules.add(rule); } - abstract boolean matchesNode(Tag[] tags); + abstract boolean matchesTags(Tag[] tags); - abstract boolean matchesWay(Tag[] tags); - - public void matchNode(Tag[] tags, int zoomLevel, + public void matchElement(int type, Tag[] tags, int zoomLevel, List matchingList) { - if (((mElement & Element.NODE) != 0) - && ((mZoom & zoomLevel) != 0) - && matchesNode(tags)) { - for (int i = 0, n = mRenderInstructionArray.length; i < n; i++) - matchingList.add(mRenderInstructionArray[i]); - - for (int i = 0, n = mSubRuleArray.length; i < n; i++) - mSubRuleArray[i].matchNode(tags, zoomLevel, matchingList); - - } - } - - public void matchWay(Tag[] tags, int zoomLevel, - int closed, List matchingList) { - - if (((mElement & Element.WAY) != 0) - && ((mZoom & zoomLevel) != 0) - && ((mClosed & closed) != 0) - && (matchesWay(tags))) { + if (((mElement & type) != 0) && ((mZoom & zoomLevel) != 0) && (matchesTags(tags))) { // add instructions for this rule for (RenderInstruction ri : mRenderInstructionArray) @@ -229,7 +222,7 @@ public abstract class Rule { // check subrules for (Rule subRule : mSubRuleArray) - subRule.matchWay(tags, zoomLevel, closed, matchingList); + subRule.matchElement(type, tags, zoomLevel, matchingList); } }