unify RenderTheme 'node'/'way','closed' rules to match any 'element'

This commit is contained in:
Hannes Janetzek 2013-06-07 01:56:41 +02:00
parent 4dfb3eed91
commit 38b3443927
7 changed files with 94 additions and 109 deletions

View File

@ -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<MatchingCacheKey, RenderInstructionItem> cache;
final MatchingCacheKey cacheKey;
@ -99,10 +99,11 @@ public class RenderTheme implements IRenderTheme {
RenderInstructionItem prevItem;
public ElementCache() {
public ElementCache(int type) {
cache = new LRUCache<MatchingCacheKey, RenderInstructionItem>(MATCHING_CACHE_SIZE);
instructionList = new ArrayList<RenderInstruction>(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<RenderInstruction> 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) {

View File

@ -117,12 +117,13 @@ public class RenderThemeHandler extends DefaultHandler {
private Rule mCurrentRule;
private final Stack<Element> mElementStack = new Stack<Element>();
private int mLevel;
private RenderTheme mRenderTheme;
private final Stack<Rule> mRuleStack = new Stack<Rule>();
private final HashMap<String, RenderInstruction> tmpStyleHash =
new HashMap<String, RenderInstruction>(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 {

View File

@ -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;
}

View File

@ -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<String> keyList, List<String> 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;
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -37,40 +37,57 @@ public abstract class Rule {
private static final String STRING_WILDCARD = "*";
private static Rule createRule(Stack<Rule> ruleStack, int element, String keys,
String values, int closed,
byte zoomMin, byte zoomMax) {
List<String> keyList = new ArrayList<String>(Arrays.asList(SPLIT_PATTERN
.split(keys)));
List<String> valueList = new ArrayList<String>(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<String> keyList = null, valueList = null;
boolean negativeRule = false;
boolean exclusionRule = false;
AttributeMatcher keyMatcher, valueMatcher = null;
if (values == null) {
valueMatcher = AnyMatcher.getInstance();
} else {
valueList = new ArrayList<String>(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<String>(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<String> 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<Rule> 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<RenderInstruction> 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<RenderInstruction>(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<RenderInstruction> 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<RenderInstruction> 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);
}
}