Merge branch 'rule_refactor'
This commit is contained in:
commit
5131d2efd7
126
vtm-playground/src/org/oscim/test/ThemeBuilder.java
Normal file
126
vtm-playground/src/org/oscim/test/ThemeBuilder.java
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package org.oscim.test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import org.oscim.theme.RenderTheme;
|
||||||
|
import org.oscim.theme.rule.Rule;
|
||||||
|
import org.oscim.theme.rule.Rule.Element;
|
||||||
|
import org.oscim.theme.rule.RuleBuilder;
|
||||||
|
import org.oscim.theme.rule.RuleBuilder.RuleType;
|
||||||
|
import org.oscim.theme.styles.AreaStyle.AreaBuilder;
|
||||||
|
import org.oscim.theme.styles.LineStyle.LineBuilder;
|
||||||
|
import org.oscim.theme.styles.RenderStyle;
|
||||||
|
import org.oscim.theme.styles.TextStyle.TextBuilder;
|
||||||
|
|
||||||
|
public class ThemeBuilder {
|
||||||
|
protected final ArrayList<RuleBuilder> mRulesList = new ArrayList<RuleBuilder>();
|
||||||
|
protected final Stack<RuleBuilder> mRuleStack = new Stack<RuleBuilder>();
|
||||||
|
|
||||||
|
protected int mLevels = 0;
|
||||||
|
protected int mMapBackground = 0xffffffff;
|
||||||
|
protected float mBaseTextSize = 1;
|
||||||
|
|
||||||
|
protected RuleBuilder mCurrentRule;
|
||||||
|
|
||||||
|
public RenderTheme build() {
|
||||||
|
int[] layer = new int[1];
|
||||||
|
|
||||||
|
Rule[] rules = new Rule[mRulesList.size()];
|
||||||
|
for (int i = 0, n = rules.length; i < n; i++)
|
||||||
|
rules[i] = mRulesList.get(i).onComplete(layer);
|
||||||
|
|
||||||
|
RenderTheme theme = new RenderTheme(mMapBackground, mBaseTextSize, rules, mLevels);
|
||||||
|
|
||||||
|
mRulesList.clear();
|
||||||
|
mRuleStack.clear();
|
||||||
|
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThemeBuilder pop() {
|
||||||
|
|
||||||
|
mRuleStack.pop();
|
||||||
|
if (mRuleStack.empty()) {
|
||||||
|
mRulesList.add(mCurrentRule);
|
||||||
|
} else {
|
||||||
|
mCurrentRule = mRuleStack.peek();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThemeBuilder push(RuleBuilder rule) {
|
||||||
|
if (!mRuleStack.empty())
|
||||||
|
mCurrentRule.addSubRule(rule);
|
||||||
|
|
||||||
|
mCurrentRule = rule;
|
||||||
|
mRuleStack.push(mCurrentRule);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThemeBuilder rules(RuleBuilder... rb) {
|
||||||
|
for (RuleBuilder r : rb) {
|
||||||
|
mRulesList.add(r);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RuleBuilder pushParse(String keys, String values) {
|
||||||
|
|
||||||
|
return RuleBuilder.create(keys, values)
|
||||||
|
.zoom(~0)
|
||||||
|
.element(Element.ANY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThemeBuilder addStyle(RenderStyle style) {
|
||||||
|
mCurrentRule.addStyle(style);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void rules() {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
public static LineBuilder line(int color, float width) {
|
||||||
|
return new LineBuilder()
|
||||||
|
.color(color)
|
||||||
|
.width(width);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AreaBuilder area(int color) {
|
||||||
|
return new AreaBuilder()
|
||||||
|
.color(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextBuilder wayText(float size, int color) {
|
||||||
|
return new TextBuilder()
|
||||||
|
.setFontSize(size)
|
||||||
|
.setColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextBuilder nodeText(float size, int color) {
|
||||||
|
return new TextBuilder()
|
||||||
|
.setFontSize(size)
|
||||||
|
.setColor(color)
|
||||||
|
.setCaption(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RuleBuilder matchKey(String key) {
|
||||||
|
return new RuleBuilder(RuleType.POSITIVE,
|
||||||
|
new String[] { key },
|
||||||
|
new String[] {});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RuleBuilder matchValue(String value) {
|
||||||
|
return new RuleBuilder(RuleType.POSITIVE,
|
||||||
|
new String[] {},
|
||||||
|
new String[] { value });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RuleBuilder matchKeyValue(String key, String value) {
|
||||||
|
return new RuleBuilder(RuleType.POSITIVE,
|
||||||
|
new String[] { key },
|
||||||
|
new String[] { value });
|
||||||
|
}
|
||||||
|
}
|
61
vtm-playground/src/org/oscim/test/ThemeBuilderTest.java
Normal file
61
vtm-playground/src/org/oscim/test/ThemeBuilderTest.java
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package org.oscim.test;
|
||||||
|
|
||||||
|
import org.oscim.backend.canvas.Color;
|
||||||
|
import org.oscim.backend.canvas.Paint.Cap;
|
||||||
|
import org.oscim.gdx.GdxMap;
|
||||||
|
import org.oscim.gdx.GdxMapApp;
|
||||||
|
import org.oscim.layers.tile.vector.VectorTileLayer;
|
||||||
|
import org.oscim.layers.tile.vector.labeling.LabelLayer;
|
||||||
|
import org.oscim.theme.RenderTheme;
|
||||||
|
import org.oscim.tiling.source.oscimap4.OSciMap4TileSource;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class ThemeBuilderTest extends GdxMap {
|
||||||
|
|
||||||
|
final Logger log = LoggerFactory.getLogger(MeshTest.class);
|
||||||
|
|
||||||
|
static class MyTheme extends ThemeBuilder {
|
||||||
|
public MyTheme() {
|
||||||
|
rules(
|
||||||
|
matchKeyValue("natural", "water")
|
||||||
|
.style(area(Color.BLUE)),
|
||||||
|
|
||||||
|
matchKeyValue("landuse", "forest")
|
||||||
|
.style(area(Color.GREEN)),
|
||||||
|
|
||||||
|
matchKeyValue("landuse", "residential")
|
||||||
|
.style(area(Color.LTGRAY)),
|
||||||
|
|
||||||
|
matchKey("highway")
|
||||||
|
.rules(matchValue("residential")
|
||||||
|
.style(line(Color.DKGRAY, 1.2f),
|
||||||
|
line(Color.WHITE, 1.1f)
|
||||||
|
.cap(Cap.ROUND)))
|
||||||
|
|
||||||
|
.style(line(Color.BLACK, 1)
|
||||||
|
.blur(0.5f)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createLayers() {
|
||||||
|
|
||||||
|
VectorTileLayer l = mMap.setBaseMap(new OSciMap4TileSource());
|
||||||
|
|
||||||
|
RenderTheme t = new MyTheme().build();
|
||||||
|
|
||||||
|
mMap.setTheme(t);
|
||||||
|
//mMap.setTheme(VtmThemes.DEFAULT);
|
||||||
|
|
||||||
|
mMap.layers().add(new LabelLayer(mMap, l));
|
||||||
|
|
||||||
|
mMap.setMapPosition(53.08, 8.82, 1 << 17);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
GdxMapApp.init();
|
||||||
|
GdxMapApp.run(new ThemeBuilderTest(), null, 400);
|
||||||
|
}
|
||||||
|
}
|
@ -22,8 +22,8 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.oscim.core.GeometryBuffer.GeometryType;
|
import org.oscim.core.GeometryBuffer.GeometryType;
|
||||||
import org.oscim.core.TagSet;
|
import org.oscim.core.TagSet;
|
||||||
import org.oscim.theme.rule.Element;
|
|
||||||
import org.oscim.theme.rule.Rule;
|
import org.oscim.theme.rule.Rule;
|
||||||
|
import org.oscim.theme.rule.Rule.Element;
|
||||||
import org.oscim.theme.rule.Rule.RuleVisitor;
|
import org.oscim.theme.rule.Rule.RuleVisitor;
|
||||||
import org.oscim.theme.styles.RenderStyle;
|
import org.oscim.theme.styles.RenderStyle;
|
||||||
import org.oscim.utils.LRUCache;
|
import org.oscim.utils.LRUCache;
|
||||||
|
@ -1,125 +0,0 @@
|
|||||||
package org.oscim.theme;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
import org.oscim.core.GeometryBuffer.GeometryType;
|
|
||||||
import org.oscim.core.Tag;
|
|
||||||
import org.oscim.core.TagSet;
|
|
||||||
import org.oscim.theme.rule.Element;
|
|
||||||
import org.oscim.theme.rule.Rule;
|
|
||||||
import org.oscim.theme.rule.RuleBuilder;
|
|
||||||
import org.oscim.theme.rule.Selector;
|
|
||||||
import org.oscim.theme.rule.SingleKeyMatcher;
|
|
||||||
import org.oscim.theme.rule.SingleValueMatcher;
|
|
||||||
import org.oscim.theme.styles.AreaStyle;
|
|
||||||
import org.oscim.theme.styles.LineStyle;
|
|
||||||
import org.oscim.theme.styles.RenderStyle;
|
|
||||||
|
|
||||||
public class ThemeBuilder {
|
|
||||||
protected final ArrayList<RuleBuilder> mRulesList = new ArrayList<RuleBuilder>();
|
|
||||||
protected final Stack<RuleBuilder> mRuleStack = new Stack<RuleBuilder>();
|
|
||||||
|
|
||||||
protected int mLevels = 0;
|
|
||||||
protected int mMapBackground = 0xffffffff;
|
|
||||||
protected float mBaseTextSize = 1;
|
|
||||||
|
|
||||||
protected RuleBuilder mCurrentRule;
|
|
||||||
|
|
||||||
protected RenderTheme build() {
|
|
||||||
|
|
||||||
Rule[] rules = new Rule[mRulesList.size()];
|
|
||||||
for (int i = 0, n = rules.length; i < n; i++)
|
|
||||||
rules[i] = mRulesList.get(i).onComplete();
|
|
||||||
|
|
||||||
RenderTheme theme = new RenderTheme(mMapBackground, mBaseTextSize, rules, mLevels);
|
|
||||||
|
|
||||||
mRulesList.clear();
|
|
||||||
mRuleStack.clear();
|
|
||||||
|
|
||||||
return theme;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ThemeBuilder pop() {
|
|
||||||
|
|
||||||
mRuleStack.pop();
|
|
||||||
if (mRuleStack.empty()) {
|
|
||||||
mRulesList.add(mCurrentRule);
|
|
||||||
} else {
|
|
||||||
mCurrentRule = mRuleStack.peek();
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ThemeBuilder push(RuleBuilder rule) {
|
|
||||||
if (!mRuleStack.empty())
|
|
||||||
mCurrentRule.addSubRule(rule);
|
|
||||||
|
|
||||||
mCurrentRule = rule;
|
|
||||||
mRuleStack.push(mCurrentRule);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ThemeBuilder push(String key, String value) {
|
|
||||||
RuleBuilder b = new RuleBuilder(true, Element.ANY, ~0, 0,
|
|
||||||
key == null ? null : new SingleKeyMatcher(key),
|
|
||||||
value == null ? null : new SingleValueMatcher(value));
|
|
||||||
push(b);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RuleBuilder pushParse(String keys, String values) {
|
|
||||||
|
|
||||||
return RuleBuilder.create(mRuleStack, keys, values)
|
|
||||||
.zoom(~0)
|
|
||||||
.element(Element.ANY);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ThemeBuilder addStyle(RenderStyle style) {
|
|
||||||
mCurrentRule.addStyle(style);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
|
|
||||||
ThemeBuilder b = new ThemeBuilder();
|
|
||||||
|
|
||||||
//b.pushParse("highway", "residential|primary|motorway")
|
|
||||||
|
|
||||||
b.push(RuleBuilder.get().select(Selector.FIRST))
|
|
||||||
.push("highway", null)
|
|
||||||
.addStyle(new LineStyle(1, 1, 1))
|
|
||||||
.pop()
|
|
||||||
|
|
||||||
.push(RuleBuilder.get().select(Selector.WHEN_MATCHED))
|
|
||||||
.addStyle(new AreaStyle(1, 1))
|
|
||||||
.pop()
|
|
||||||
.pop();
|
|
||||||
|
|
||||||
RenderTheme t = b.build();
|
|
||||||
TagSet tags = new TagSet(1);
|
|
||||||
RenderStyle[] styles;
|
|
||||||
|
|
||||||
tags.add(new Tag("ahighway", "residential"));
|
|
||||||
styles = t.matchElement(GeometryType.LINE, tags, 1);
|
|
||||||
System.out.println(Arrays.deepToString(styles));
|
|
||||||
|
|
||||||
// tags.clear();
|
|
||||||
// tags.add(new Tag("highway", "motorway"));
|
|
||||||
// styles = t.matchElement(GeometryType.LINE, tags, 1);
|
|
||||||
// out.println(styles);
|
|
||||||
//
|
|
||||||
// tags.clear();
|
|
||||||
// tags.add(new Tag("sup", "wup"));
|
|
||||||
// styles = t.matchElement(GeometryType.LINE, tags, 1);
|
|
||||||
// out.println(styles);
|
|
||||||
//
|
|
||||||
// tags.clear();
|
|
||||||
// tags.add(new Tag("highway", "motorway"));
|
|
||||||
// styles = t.matchElement(GeometryType.LINE, tags, 1);
|
|
||||||
// out.println(styles);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -40,6 +40,8 @@ import org.oscim.renderer.atlas.TextureRegion;
|
|||||||
import org.oscim.renderer.elements.TextureItem;
|
import org.oscim.renderer.elements.TextureItem;
|
||||||
import org.oscim.theme.IRenderTheme.ThemeException;
|
import org.oscim.theme.IRenderTheme.ThemeException;
|
||||||
import org.oscim.theme.rule.Rule;
|
import org.oscim.theme.rule.Rule;
|
||||||
|
import org.oscim.theme.rule.Rule.Closed;
|
||||||
|
import org.oscim.theme.rule.Rule.Selector;
|
||||||
import org.oscim.theme.rule.RuleBuilder;
|
import org.oscim.theme.rule.RuleBuilder;
|
||||||
import org.oscim.theme.styles.AreaStyle;
|
import org.oscim.theme.styles.AreaStyle;
|
||||||
import org.oscim.theme.styles.AreaStyle.AreaBuilder;
|
import org.oscim.theme.styles.AreaStyle.AreaBuilder;
|
||||||
@ -153,7 +155,7 @@ public class XmlThemeBuilder extends DefaultHandler {
|
|||||||
|
|
||||||
Rule[] rules = new Rule[mRulesList.size()];
|
Rule[] rules = new Rule[mRulesList.size()];
|
||||||
for (int i = 0, n = rules.length; i < n; i++)
|
for (int i = 0, n = rules.length; i < n; i++)
|
||||||
rules[i] = mRulesList.get(i).onComplete();
|
rules[i] = mRulesList.get(i).onComplete(null);
|
||||||
|
|
||||||
mRenderTheme = new RenderTheme(mMapBackground, mBaseTextSize, rules, mLevels);
|
mRenderTheme = new RenderTheme(mMapBackground, mBaseTextSize, rules, mLevels);
|
||||||
|
|
||||||
@ -199,7 +201,7 @@ public class XmlThemeBuilder extends DefaultHandler {
|
|||||||
|
|
||||||
} else if (ELEMENT_NAME_MATCH.equals(localName)) {
|
} else if (ELEMENT_NAME_MATCH.equals(localName)) {
|
||||||
checkState(localName, Element.RULE);
|
checkState(localName, Element.RULE);
|
||||||
RuleBuilder rule = RuleBuilder.create(localName, attributes, mRuleStack);
|
RuleBuilder rule = createRule(localName, attributes, mRuleStack);
|
||||||
if (!mRuleStack.empty()) {
|
if (!mRuleStack.empty()) {
|
||||||
mCurrentRule.addSubRule(rule);
|
mCurrentRule.addSubRule(rule);
|
||||||
}
|
}
|
||||||
@ -292,6 +294,67 @@ public class XmlThemeBuilder extends DefaultHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RuleBuilder createRule(String localName, Attributes attributes,
|
||||||
|
Stack<RuleBuilder> ruleStack) {
|
||||||
|
int element = Rule.Element.ANY;
|
||||||
|
int closed = Closed.ANY;
|
||||||
|
String keys = null;
|
||||||
|
String values = null;
|
||||||
|
byte zoomMin = 0;
|
||||||
|
byte zoomMax = Byte.MAX_VALUE;
|
||||||
|
int selector = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < attributes.getLength(); ++i) {
|
||||||
|
String name = attributes.getLocalName(i);
|
||||||
|
String value = attributes.getValue(i);
|
||||||
|
|
||||||
|
if ("e".equals(name)) {
|
||||||
|
String val = value.toUpperCase();
|
||||||
|
if ("WAY".equals(val))
|
||||||
|
element = Rule.Element.WAY;
|
||||||
|
else if ("NODE".equals(val))
|
||||||
|
element = Rule.Element.NODE;
|
||||||
|
} else if ("k".equals(name)) {
|
||||||
|
keys = value;
|
||||||
|
} else if ("v".equals(name)) {
|
||||||
|
values = value;
|
||||||
|
} else if ("closed".equals(name)) {
|
||||||
|
String val = value.toUpperCase();
|
||||||
|
if ("YES".equals(val))
|
||||||
|
closed = Closed.YES;
|
||||||
|
else if ("NO".equals(val))
|
||||||
|
closed = Closed.NO;
|
||||||
|
} else if ("zoom-min".equals(name)) {
|
||||||
|
zoomMin = Byte.parseByte(value);
|
||||||
|
} else if ("zoom-max".equals(name)) {
|
||||||
|
zoomMax = Byte.parseByte(value);
|
||||||
|
} else if ("select".equals(name)) {
|
||||||
|
if ("first".equals(value))
|
||||||
|
selector |= Selector.FIRST;
|
||||||
|
if ("when-matched".equals(value))
|
||||||
|
selector |= Selector.WHEN_MATCHED;
|
||||||
|
} else {
|
||||||
|
XmlThemeBuilder.logUnknownAttribute(localName, name, value, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closed == Closed.YES)
|
||||||
|
element = Rule.Element.POLY;
|
||||||
|
else if (closed == Closed.NO)
|
||||||
|
element = Rule.Element.LINE;
|
||||||
|
|
||||||
|
XmlThemeBuilder.validateNonNegative("zoom-min", zoomMin);
|
||||||
|
XmlThemeBuilder.validateNonNegative("zoom-max", zoomMax);
|
||||||
|
if (zoomMin > zoomMax)
|
||||||
|
throw new ThemeException("zoom-min must be less or equal zoom-max: " + zoomMin);
|
||||||
|
|
||||||
|
RuleBuilder b = RuleBuilder.create(keys, values);
|
||||||
|
b.setZoom(zoomMin, zoomMax);
|
||||||
|
b.element(element);
|
||||||
|
b.select(selector);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
private TextureRegion getAtlasRegion(String src) {
|
private TextureRegion getAtlasRegion(String src) {
|
||||||
if (mTextureAtlas == null)
|
if (mTextureAtlas == null)
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
|
||||||
* Copyright 2013 Hannes Janetzek
|
|
||||||
*
|
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.oscim.theme.rule;
|
|
||||||
|
|
||||||
import org.oscim.core.Tag;
|
|
||||||
|
|
||||||
final class AnyMatcher implements AttributeMatcher {
|
|
||||||
private static final AnyMatcher INSTANCE = new AnyMatcher();
|
|
||||||
|
|
||||||
static AnyMatcher getInstance() {
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Private constructor to prevent instantiation from other classes.
|
|
||||||
*/
|
|
||||||
private AnyMatcher() {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCoveredBy(AttributeMatcher attributeMatcher) {
|
|
||||||
return attributeMatcher == this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(Tag[] tags) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
|
||||||
* Copyright 2013 Hannes Janetzek
|
|
||||||
*
|
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.oscim.theme.rule;
|
|
||||||
|
|
||||||
import org.oscim.core.Tag;
|
|
||||||
|
|
||||||
interface AttributeMatcher {
|
|
||||||
boolean isCoveredBy(AttributeMatcher attributeMatcher);
|
|
||||||
|
|
||||||
boolean matches(Tag[] tags);
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
|
||||||
* Copyright 2013 Hannes Janetzek
|
|
||||||
*
|
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.oscim.theme.rule;
|
|
||||||
|
|
||||||
public final class Closed {
|
|
||||||
public static final int NO = 1 << 0;
|
|
||||||
public static final int YES = 1 << 1;
|
|
||||||
public static final int ANY = NO | YES;
|
|
||||||
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
/* Copyright 2013 Hannes Janetzek
|
|
||||||
*
|
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.oscim.theme.rule;
|
|
||||||
|
|
||||||
public final class Element {
|
|
||||||
public static final int NODE = 1 << 0;
|
|
||||||
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;
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
|
||||||
* Copyright 2013 Hannes Janetzek
|
|
||||||
*
|
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.oscim.theme.rule;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.oscim.core.Tag;
|
|
||||||
|
|
||||||
class MultiKeyMatcher implements AttributeMatcher {
|
|
||||||
private final String[] mKeys;
|
|
||||||
|
|
||||||
MultiKeyMatcher(List<String> keys) {
|
|
||||||
mKeys = new String[keys.size()];
|
|
||||||
for (int i = 0, n = mKeys.length; i < n; ++i) {
|
|
||||||
mKeys[i] = keys.get(i).intern();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCoveredBy(AttributeMatcher attributeMatcher) {
|
|
||||||
if (attributeMatcher == this) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Tag[] tags = new Tag[mKeys.length];
|
|
||||||
int i = 0;
|
|
||||||
for (String key : mKeys) {
|
|
||||||
tags[i++] = new Tag(key, null);
|
|
||||||
}
|
|
||||||
return attributeMatcher.matches(tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(Tag[] tags) {
|
|
||||||
for (Tag tag : tags)
|
|
||||||
for (String key : mKeys)
|
|
||||||
if (key == tag.key)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
|
||||||
* Copyright 2013 Hannes Janetzek
|
|
||||||
*
|
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.oscim.theme.rule;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.oscim.core.Tag;
|
|
||||||
|
|
||||||
class MultiValueMatcher implements AttributeMatcher {
|
|
||||||
private final String[] mValues;
|
|
||||||
|
|
||||||
MultiValueMatcher(List<String> values) {
|
|
||||||
mValues = new String[values.size()];
|
|
||||||
for (int i = 0, n = mValues.length; i < n; ++i) {
|
|
||||||
mValues[i] = values.get(i).intern();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCoveredBy(AttributeMatcher attributeMatcher) {
|
|
||||||
if (attributeMatcher == this) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Tag[] tags = new Tag[mValues.length];
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (String val : mValues) {
|
|
||||||
tags[i++] = new Tag(null, val);
|
|
||||||
}
|
|
||||||
return attributeMatcher.matches(tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(Tag[] tags) {
|
|
||||||
for (Tag tag : tags)
|
|
||||||
for (String val : mValues)
|
|
||||||
if (val == tag.value)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
|
||||||
* Copyright 2013 Hannes Janetzek
|
|
||||||
*
|
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.oscim.theme.rule;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
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
|
|
||||||
//
|
|
||||||
// (\) 'except negation' matches when KEY is present
|
|
||||||
// none items of VALUE is present (TODO).
|
|
||||||
// (can be emulated by <m k="a"><m k=a v="-|b|c">...</m></m>)
|
|
||||||
//
|
|
||||||
// (~) 'non-exclusive negation' matches when either KEY is not present
|
|
||||||
// or KEY is present and any VALUE is present
|
|
||||||
//
|
|
||||||
private final boolean mExclusive;
|
|
||||||
|
|
||||||
NegativeMatcher(List<String> keyList, List<String> valueList, boolean exclusive) {
|
|
||||||
mKeyList = new String[keyList.size()];
|
|
||||||
for (int i = 0; i < mKeyList.length; i++)
|
|
||||||
mKeyList[i] = keyList.get(i).intern();
|
|
||||||
|
|
||||||
mValueList = new String[valueList.size()];
|
|
||||||
for (int i = 0; i < mValueList.length; i++)
|
|
||||||
mValueList[i] = valueList.get(i).intern();
|
|
||||||
|
|
||||||
mExclusive = exclusive;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCoveredBy(AttributeMatcher attributeMatcher) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(Tag[] tags) {
|
|
||||||
if (keyListDoesNotContainKeys(tags))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
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 (String key : mKeyList)
|
|
||||||
if (key == tag.key)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
|
||||||
* Copyright 2013 Hannes Janetzek
|
|
||||||
*
|
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.oscim.theme.rule;
|
|
||||||
|
|
||||||
import org.oscim.core.Tag;
|
|
||||||
import org.oscim.theme.styles.RenderStyle;
|
|
||||||
|
|
||||||
class NegativeRule extends Rule {
|
|
||||||
final AttributeMatcher mAttributeMatcher;
|
|
||||||
|
|
||||||
NegativeRule(int element, int zoom, int selector, AttributeMatcher attributeMatcher,
|
|
||||||
Rule[] subRules, RenderStyle[] styles) {
|
|
||||||
super(element, zoom, selector, subRules, styles);
|
|
||||||
|
|
||||||
mAttributeMatcher = attributeMatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean matchesTags(Tag[] tags) {
|
|
||||||
return mAttributeMatcher.matches(tags);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
|
||||||
* Copyright 2013 Hannes Janetzek
|
|
||||||
*
|
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.oscim.theme.rule;
|
|
||||||
|
|
||||||
import org.oscim.core.Tag;
|
|
||||||
import org.oscim.theme.styles.RenderStyle;
|
|
||||||
|
|
||||||
class PositiveRule extends Rule {
|
|
||||||
final AttributeMatcher mKeyMatcher;
|
|
||||||
final AttributeMatcher mValueMatcher;
|
|
||||||
|
|
||||||
PositiveRule(int element, int zoom, int selector, AttributeMatcher keyMatcher,
|
|
||||||
AttributeMatcher valueMatcher, Rule[] subRules, RenderStyle[] styles) {
|
|
||||||
super(element, zoom, selector, subRules, styles);
|
|
||||||
|
|
||||||
if (keyMatcher instanceof AnyMatcher)
|
|
||||||
mKeyMatcher = null;
|
|
||||||
else
|
|
||||||
mKeyMatcher = keyMatcher;
|
|
||||||
|
|
||||||
if (valueMatcher instanceof AnyMatcher)
|
|
||||||
mValueMatcher = null;
|
|
||||||
else
|
|
||||||
mValueMatcher = valueMatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean matchesTags(Tag[] tags) {
|
|
||||||
return (mKeyMatcher == null || mKeyMatcher.matches(tags))
|
|
||||||
&& (mValueMatcher == null || mValueMatcher.matches(tags));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013 Hannes Janetzek
|
* Copyright 2014 Hannes Janetzek
|
||||||
*
|
*
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||||
*
|
*
|
||||||
@ -19,9 +19,30 @@ package org.oscim.theme.rule;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.oscim.core.Tag;
|
import org.oscim.core.Tag;
|
||||||
|
import org.oscim.theme.rule.RuleBuilder.RuleType;
|
||||||
import org.oscim.theme.styles.RenderStyle;
|
import org.oscim.theme.styles.RenderStyle;
|
||||||
|
|
||||||
public abstract class Rule {
|
public class Rule {
|
||||||
|
public final class Element {
|
||||||
|
public static final int NODE = 1 << 0;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Closed {
|
||||||
|
public static final int NO = 1 << 0;
|
||||||
|
public static final int YES = 1 << 1;
|
||||||
|
public static final int ANY = NO | YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Selector {
|
||||||
|
public static final int ANY = 0;
|
||||||
|
public static final int FIRST = 1 << 0;
|
||||||
|
public static final int WHEN_MATCHED = 1 << 1;
|
||||||
|
}
|
||||||
|
|
||||||
public final static RenderStyle[] EMPTY_STYLE = new RenderStyle[0];
|
public final static RenderStyle[] EMPTY_STYLE = new RenderStyle[0];
|
||||||
public final static Rule[] EMPTY_RULES = new Rule[0];
|
public final static Rule[] EMPTY_RULES = new Rule[0];
|
||||||
|
|
||||||
@ -44,13 +65,15 @@ public abstract class Rule {
|
|||||||
selectWhenMatched = (selector & Selector.WHEN_MATCHED) != 0;
|
selectWhenMatched = (selector & Selector.WHEN_MATCHED) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract boolean matchesTags(Tag[] tags);
|
boolean matchesTags(Tag[] tags) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean matchElement(int type, Tag[] tags, int zoomLevel, List<RenderStyle> result) {
|
public boolean matchElement(int type, Tag[] tags, int zoomLevel, List<RenderStyle> result) {
|
||||||
|
if (((element & type) == 0) || ((zoom & zoomLevel) == 0) || !matchesTags(tags))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (((element & type) != 0) && ((zoom & zoomLevel) != 0) && (matchesTags(tags))) {
|
|
||||||
boolean matched = false;
|
boolean matched = false;
|
||||||
|
|
||||||
if (subRules != EMPTY_RULES) {
|
if (subRules != EMPTY_RULES) {
|
||||||
if (selectFirstMatch) {
|
if (selectFirstMatch) {
|
||||||
/* only add first matching rule and when-matched rules iff a
|
/* only add first matching rule and when-matched rules iff a
|
||||||
@ -84,14 +107,10 @@ public abstract class Rule {
|
|||||||
for (RenderStyle ri : styles)
|
for (RenderStyle ri : styles)
|
||||||
result.add(ri);
|
result.add(ri);
|
||||||
|
|
||||||
/* this rule did not match */
|
/* this rule did match */
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this rule did not match */
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
for (RenderStyle ri : styles)
|
for (RenderStyle ri : styles)
|
||||||
ri.dispose();
|
ri.dispose();
|
||||||
@ -150,4 +169,163 @@ public abstract class Rule {
|
|||||||
public void apply(RuleVisitor v) {
|
public void apply(RuleVisitor v) {
|
||||||
v.apply(this);
|
v.apply(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class PositiveRuleK extends Rule {
|
||||||
|
private final String mKey;
|
||||||
|
|
||||||
|
PositiveRuleK(int element, int zoom, int selector, String key,
|
||||||
|
Rule[] subRules, RenderStyle[] styles) {
|
||||||
|
|
||||||
|
super(element, zoom, selector, subRules, styles);
|
||||||
|
mKey = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean matchesTags(Tag[] tags) {
|
||||||
|
for (Tag tag : tags)
|
||||||
|
if (mKey == tag.key)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PositiveRuleV extends Rule {
|
||||||
|
private final String mValue;
|
||||||
|
|
||||||
|
PositiveRuleV(int element, int zoom, int selector, String value,
|
||||||
|
Rule[] subRules, RenderStyle[] styles) {
|
||||||
|
super(element, zoom, selector, subRules, styles);
|
||||||
|
mValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean matchesTags(Tag[] tags) {
|
||||||
|
for (Tag tag : tags)
|
||||||
|
if (mValue == tag.value)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PositiveRuleKV extends Rule {
|
||||||
|
private final String mKey;
|
||||||
|
private final String mValue;
|
||||||
|
|
||||||
|
PositiveRuleKV(int element, int zoom, int selector,
|
||||||
|
String key, String value,
|
||||||
|
Rule[] subRules, RenderStyle[] styles) {
|
||||||
|
super(element, zoom, selector, subRules, styles);
|
||||||
|
mKey = key;
|
||||||
|
mValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean matchesTags(Tag[] tags) {
|
||||||
|
for (Tag tag : tags)
|
||||||
|
if (mKey == tag.key)
|
||||||
|
return (mValue == tag.value);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PositiveRuleMultiKV extends Rule {
|
||||||
|
private final String mKeys[];
|
||||||
|
private final String mValues[];
|
||||||
|
|
||||||
|
PositiveRuleMultiKV(int element, int zoom, int selector,
|
||||||
|
String keys[], String values[],
|
||||||
|
Rule[] subRules, RenderStyle[] styles) {
|
||||||
|
|
||||||
|
super(element, zoom, selector, subRules, styles);
|
||||||
|
if (keys.length == 0)
|
||||||
|
mKeys = null;
|
||||||
|
else
|
||||||
|
mKeys = keys;
|
||||||
|
|
||||||
|
if (values.length == 0)
|
||||||
|
mValues = null;
|
||||||
|
else
|
||||||
|
mValues = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean matchesTags(Tag[] tags) {
|
||||||
|
if (mKeys == null) {
|
||||||
|
for (Tag tag : tags) {
|
||||||
|
for (String value : mValues) {
|
||||||
|
if (value == tag.value)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Tag tag : tags)
|
||||||
|
for (String key : mKeys) {
|
||||||
|
if (key == tag.key) {
|
||||||
|
if (mValues == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (String value : mValues) {
|
||||||
|
if (value == tag.value)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class NegativeRule extends Rule {
|
||||||
|
public final String[] keys;
|
||||||
|
public final String[] values;
|
||||||
|
|
||||||
|
/* (-) 'exclusive negation' matches when either KEY is not present
|
||||||
|
* or KEY is present and any VALUE is NOT present
|
||||||
|
*
|
||||||
|
* (\) 'except negation' matches when KEY is present
|
||||||
|
* none items of VALUE is present (TODO).
|
||||||
|
* (can be emulated by <m k="a"><m k=a v="-|b|c">...</m></m>)
|
||||||
|
*
|
||||||
|
* (~) 'non-exclusive negation' matches when either KEY is not present
|
||||||
|
* or KEY is present and any VALUE is present */
|
||||||
|
|
||||||
|
public final boolean exclusive;
|
||||||
|
|
||||||
|
NegativeRule(RuleType type, int element, int zoom, int selector,
|
||||||
|
String[] keys, String[] values,
|
||||||
|
Rule[] subRules, RenderStyle[] styles) {
|
||||||
|
super(element, zoom, selector, subRules, styles);
|
||||||
|
|
||||||
|
this.keys = keys;
|
||||||
|
this.values = values;
|
||||||
|
this.exclusive = type == RuleType.EXCLUDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matchesTags(Tag[] tags) {
|
||||||
|
if (!containsKeys(tags))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (Tag tag : tags)
|
||||||
|
for (String value : values)
|
||||||
|
if (value == tag.value)
|
||||||
|
return !exclusive;
|
||||||
|
|
||||||
|
return exclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsKeys(Tag[] tags) {
|
||||||
|
for (Tag tag : tags)
|
||||||
|
for (String key : keys)
|
||||||
|
if (key == tag.key)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,204 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 Hannes Janetzek
|
||||||
|
*
|
||||||
|
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along with
|
||||||
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
package org.oscim.theme.rule;
|
package org.oscim.theme.rule;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
import org.oscim.theme.IRenderTheme.ThemeException;
|
import org.oscim.theme.IRenderTheme.ThemeException;
|
||||||
import org.oscim.theme.XmlThemeBuilder;
|
import org.oscim.theme.rule.Rule.Element;
|
||||||
|
import org.oscim.theme.rule.Rule.NegativeRule;
|
||||||
|
import org.oscim.theme.rule.Rule.PositiveRuleK;
|
||||||
|
import org.oscim.theme.rule.Rule.PositiveRuleKV;
|
||||||
|
import org.oscim.theme.rule.Rule.PositiveRuleMultiKV;
|
||||||
|
import org.oscim.theme.rule.Rule.PositiveRuleV;
|
||||||
import org.oscim.theme.styles.RenderStyle;
|
import org.oscim.theme.styles.RenderStyle;
|
||||||
import org.xml.sax.Attributes;
|
import org.oscim.theme.styles.RenderStyle.StyleBuilder;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class RuleBuilder {
|
public class RuleBuilder {
|
||||||
boolean positiveRule;
|
final static Logger log = LoggerFactory.getLogger(RuleBuilder.class);
|
||||||
|
|
||||||
|
private final static String[] EMPTY_KV = {};
|
||||||
|
|
||||||
|
public enum RuleType {
|
||||||
|
POSITIVE,
|
||||||
|
NEGATIVE,
|
||||||
|
EXCLUDE
|
||||||
|
}
|
||||||
|
|
||||||
int zoom;
|
int zoom;
|
||||||
int element;
|
int element;
|
||||||
int selector;
|
int selector;
|
||||||
|
RuleType type;
|
||||||
|
|
||||||
AttributeMatcher keyMatcher;
|
String keys[];
|
||||||
AttributeMatcher valueMatcher;
|
String values[];
|
||||||
|
|
||||||
ArrayList<RenderStyle> renderStyles = new ArrayList<RenderStyle>(4);
|
ArrayList<RenderStyle> renderStyles = new ArrayList<RenderStyle>(4);
|
||||||
ArrayList<RuleBuilder> subRules = new ArrayList<RuleBuilder>(4);
|
ArrayList<RuleBuilder> subRules = new ArrayList<RuleBuilder>(4);
|
||||||
|
StyleBuilder[] styleBuilder;
|
||||||
private static final Map<List<String>, AttributeMatcher> MATCHERS_CACHE_KEY =
|
|
||||||
new HashMap<List<String>, AttributeMatcher>();
|
|
||||||
private static final Map<List<String>, AttributeMatcher> MATCHERS_CACHE_VALUE =
|
|
||||||
new HashMap<List<String>, AttributeMatcher>();
|
|
||||||
|
|
||||||
private static final String STRING_NEGATION = "~";
|
private static final String STRING_NEGATION = "~";
|
||||||
private static final String STRING_EXCLUSIVE = "-";
|
private static final String STRING_EXCLUSIVE = "-";
|
||||||
private static final String STRING_WILDCARD = "*";
|
private static final String SEPARATOR = "\\|";
|
||||||
|
|
||||||
private static final int SELECT_FIRST = 1 << 0;
|
//private static final String STRING_WILDCARD = "*";
|
||||||
private static final int SELECT_WHEN_MATCHED = 1 << 1;
|
|
||||||
|
|
||||||
public RuleBuilder(boolean positive, int element, int zoom, int selector,
|
public RuleBuilder(RuleType type, int element, int zoom, int selector,
|
||||||
AttributeMatcher keyMatcher, AttributeMatcher valueMatcher) {
|
String[] keys, String[] values) {
|
||||||
this.positiveRule = positive;
|
this.type = type;
|
||||||
this.element = element;
|
this.element = element;
|
||||||
this.zoom = zoom;
|
this.zoom = zoom;
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.keyMatcher = keyMatcher;
|
this.keys = keys;
|
||||||
this.valueMatcher = valueMatcher;
|
this.values = values;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RuleBuilder(boolean positive, AttributeMatcher keyMatcher, AttributeMatcher valueMatcher) {
|
public RuleBuilder(RuleType type, String[] keys, String[] values) {
|
||||||
this.positiveRule = positive;
|
this.element = Element.ANY;
|
||||||
this.keyMatcher = keyMatcher;
|
this.zoom = ~0;
|
||||||
this.valueMatcher = valueMatcher;
|
this.type = type;
|
||||||
|
this.keys = keys;
|
||||||
|
this.values = values;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RuleBuilder create(Stack<RuleBuilder> ruleStack, String keys, String values) {
|
public RuleBuilder() {
|
||||||
|
this.type = RuleType.POSITIVE;
|
||||||
|
this.element = Element.ANY;
|
||||||
|
this.zoom = ~0;
|
||||||
|
this.keys = EMPTY_KV;
|
||||||
|
this.values = EMPTY_KV;
|
||||||
|
}
|
||||||
|
|
||||||
List<String> keyList = null, valueList = null;
|
public static RuleBuilder create(String keys, String values) {
|
||||||
boolean negativeRule = false;
|
|
||||||
boolean exclusionRule = false;
|
|
||||||
|
|
||||||
AttributeMatcher keyMatcher, valueMatcher = null;
|
String[] keyList = EMPTY_KV;
|
||||||
|
String[] valueList = EMPTY_KV;
|
||||||
|
RuleType type = RuleType.POSITIVE;
|
||||||
|
|
||||||
if (values == null) {
|
if (values != null) {
|
||||||
valueMatcher = AnyMatcher.getInstance();
|
if (values.startsWith(STRING_NEGATION)) {
|
||||||
|
type = RuleType.NEGATIVE;
|
||||||
|
if (values.length() > 2)
|
||||||
|
valueList = values.substring(2)
|
||||||
|
.split(SEPARATOR);
|
||||||
|
} else if (values.startsWith(STRING_EXCLUSIVE)) {
|
||||||
|
type = RuleType.EXCLUDE;
|
||||||
|
if (values.length() > 2)
|
||||||
|
valueList = values.substring(2)
|
||||||
|
.split(SEPARATOR);
|
||||||
} else {
|
} else {
|
||||||
valueList = new ArrayList<String>(Arrays.asList(values.split("\\|")));
|
valueList = values.split(SEPARATOR);
|
||||||
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 (keys != null) {
|
||||||
if (negativeRule || exclusionRule) {
|
keyList = keys.split(SEPARATOR);
|
||||||
throw new ThemeException("negative rule requires key");
|
|
||||||
}
|
}
|
||||||
keyMatcher = AnyMatcher.getInstance();
|
|
||||||
} else {
|
|
||||||
keyList = new ArrayList<String>(Arrays.asList(keys.split("\\|")));
|
|
||||||
keyMatcher = getKeyMatcher(keyList);
|
|
||||||
|
|
||||||
if ((keyMatcher instanceof AnyMatcher) && (negativeRule || exclusionRule)) {
|
if (type != RuleType.POSITIVE) {
|
||||||
|
if (keyList == null || keyList.length == 0)
|
||||||
throw new ThemeException("negative rule requires key");
|
throw new ThemeException("negative rule requires key");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (negativeRule) {
|
return new RuleBuilder(type, keyList, valueList);
|
||||||
AttributeMatcher m = new NegativeMatcher(keyList, valueList, false);
|
|
||||||
return new RuleBuilder(false, m, null);
|
|
||||||
} else if (exclusionRule) {
|
|
||||||
AttributeMatcher m = new NegativeMatcher(keyList, valueList, true);
|
|
||||||
return new RuleBuilder(false, m, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
keyMatcher = RuleOptimizer.optimize(keyMatcher, ruleStack);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RuleBuilder(true, keyMatcher, valueMatcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AttributeMatcher getKeyMatcher(List<String> keyList) {
|
|
||||||
if (STRING_WILDCARD.equals(keyList.get(0))) {
|
|
||||||
return AnyMatcher.getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
AttributeMatcher attributeMatcher = MATCHERS_CACHE_KEY.get(keyList);
|
|
||||||
if (attributeMatcher == null) {
|
|
||||||
if (keyList.size() == 1) {
|
|
||||||
attributeMatcher = new SingleKeyMatcher(keyList.get(0));
|
|
||||||
} else {
|
|
||||||
attributeMatcher = new MultiKeyMatcher(keyList);
|
|
||||||
}
|
|
||||||
MATCHERS_CACHE_KEY.put(keyList, attributeMatcher);
|
|
||||||
}
|
|
||||||
return attributeMatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AttributeMatcher getValueMatcher(List<String> valueList) {
|
|
||||||
if (STRING_WILDCARD.equals(valueList.get(0))) {
|
|
||||||
return AnyMatcher.getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
AttributeMatcher attributeMatcher = MATCHERS_CACHE_VALUE.get(valueList);
|
|
||||||
if (attributeMatcher == null) {
|
|
||||||
if (valueList.size() == 1) {
|
|
||||||
attributeMatcher = new SingleValueMatcher(valueList.get(0));
|
|
||||||
} else {
|
|
||||||
attributeMatcher = new MultiValueMatcher(valueList);
|
|
||||||
}
|
|
||||||
MATCHERS_CACHE_VALUE.put(valueList, attributeMatcher);
|
|
||||||
}
|
|
||||||
return attributeMatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void validate(byte zoomMin, byte zoomMax) {
|
|
||||||
XmlThemeBuilder.validateNonNegative("zoom-min", zoomMin);
|
|
||||||
XmlThemeBuilder.validateNonNegative("zoom-max", zoomMax);
|
|
||||||
if (zoomMin > zoomMax)
|
|
||||||
throw new ThemeException("zoom-min must be less or equal zoom-max: " + zoomMin);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RuleBuilder create(String elementName, Attributes attributes,
|
|
||||||
Stack<RuleBuilder> ruleStack) {
|
|
||||||
int element = Element.ANY;
|
|
||||||
int closed = Closed.ANY;
|
|
||||||
String keys = null;
|
|
||||||
String values = null;
|
|
||||||
byte zoomMin = 0;
|
|
||||||
byte zoomMax = Byte.MAX_VALUE;
|
|
||||||
int selector = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < attributes.getLength(); ++i) {
|
|
||||||
String name = attributes.getLocalName(i);
|
|
||||||
String value = attributes.getValue(i);
|
|
||||||
|
|
||||||
if ("e".equals(name)) {
|
|
||||||
String val = value.toUpperCase();
|
|
||||||
if ("WAY".equals(val))
|
|
||||||
element = Element.WAY;
|
|
||||||
else if ("NODE".equals(val))
|
|
||||||
element = Element.NODE;
|
|
||||||
} else if ("k".equals(name)) {
|
|
||||||
keys = value;
|
|
||||||
} else if ("v".equals(name)) {
|
|
||||||
values = value;
|
|
||||||
} else if ("closed".equals(name)) {
|
|
||||||
String val = value.toUpperCase();
|
|
||||||
if ("YES".equals(val))
|
|
||||||
closed = Closed.YES;
|
|
||||||
else if ("NO".equals(val))
|
|
||||||
closed = Closed.NO;
|
|
||||||
} else if ("zoom-min".equals(name)) {
|
|
||||||
zoomMin = Byte.parseByte(value);
|
|
||||||
} else if ("zoom-max".equals(name)) {
|
|
||||||
zoomMax = Byte.parseByte(value);
|
|
||||||
} else if ("select".equals(name)) {
|
|
||||||
if ("first".equals(value))
|
|
||||||
selector |= SELECT_FIRST;
|
|
||||||
if ("when-matched".equals(value))
|
|
||||||
selector |= SELECT_WHEN_MATCHED;
|
|
||||||
} else {
|
|
||||||
XmlThemeBuilder.logUnknownAttribute(elementName, name, value, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (closed == Closed.YES)
|
|
||||||
element = Element.POLY;
|
|
||||||
else if (closed == Closed.NO)
|
|
||||||
element = Element.LINE;
|
|
||||||
|
|
||||||
validate(zoomMin, zoomMax);
|
|
||||||
|
|
||||||
RuleBuilder b = create(ruleStack, keys, values);
|
|
||||||
b.setZoom(zoomMin, zoomMax);
|
|
||||||
b.element = element;
|
|
||||||
b.selector = selector;
|
|
||||||
return b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RuleBuilder setZoom(byte zoomMin, byte zoomMax) {
|
public RuleBuilder setZoom(byte zoomMin, byte zoomMax) {
|
||||||
@ -210,13 +128,17 @@ public class RuleBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rule onComplete() {
|
public Rule onComplete(int[] level) {
|
||||||
MATCHERS_CACHE_KEY.clear();
|
|
||||||
MATCHERS_CACHE_VALUE.clear();
|
|
||||||
|
|
||||||
RenderStyle[] styles = null;
|
RenderStyle[] styles = null;
|
||||||
Rule[] rules = null;
|
Rule[] rules = null;
|
||||||
|
|
||||||
|
if (styleBuilder != null)
|
||||||
|
for (StyleBuilder style : styleBuilder) {
|
||||||
|
renderStyles.add(style.level(level[0]).build());
|
||||||
|
level[0] += 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (renderStyles.size() > 0) {
|
if (renderStyles.size() > 0) {
|
||||||
styles = new RenderStyle[renderStyles.size()];
|
styles = new RenderStyle[renderStyles.size()];
|
||||||
renderStyles.toArray(styles);
|
renderStyles.toArray(styles);
|
||||||
@ -225,33 +147,61 @@ public class RuleBuilder {
|
|||||||
if (subRules.size() > 0) {
|
if (subRules.size() > 0) {
|
||||||
rules = new Rule[subRules.size()];
|
rules = new Rule[subRules.size()];
|
||||||
for (int i = 0; i < rules.length; i++)
|
for (int i = 0; i < rules.length; i++)
|
||||||
rules[i] = subRules.get(i).onComplete();
|
rules[i] = subRules.get(i).onComplete(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (positiveRule)
|
int numKeys = keys.length;
|
||||||
return new PositiveRule(element, zoom, selector, keyMatcher,
|
int numVals = values.length;
|
||||||
valueMatcher, rules, styles);
|
|
||||||
else
|
if (numKeys == 0 && numVals == 0)
|
||||||
return new NegativeRule(element, zoom, selector, keyMatcher,
|
return new Rule(element, zoom, selector, rules, styles);
|
||||||
|
|
||||||
|
for (int i = 0; i < numVals; i++)
|
||||||
|
values[i] = values[i].intern();
|
||||||
|
|
||||||
|
for (int i = 0; i < numKeys; i++)
|
||||||
|
keys[i] = keys[i].intern();
|
||||||
|
|
||||||
|
if (type != RuleType.POSITIVE)
|
||||||
|
return new NegativeRule(type, element, zoom, selector,
|
||||||
|
keys, values, rules, styles);
|
||||||
|
|
||||||
|
if (numKeys == 1 && numKeys == 0)
|
||||||
|
return new PositiveRuleK(element, zoom, selector, keys[0],
|
||||||
rules, styles);
|
rules, styles);
|
||||||
|
|
||||||
|
if (numKeys == 0 && numVals == 1)
|
||||||
|
return new PositiveRuleV(element, zoom, selector, values[0],
|
||||||
|
rules, styles);
|
||||||
|
|
||||||
|
if (numKeys == 1 && numVals == 1)
|
||||||
|
return new PositiveRuleKV(element, zoom, selector,
|
||||||
|
keys[0], values[0], rules, styles);
|
||||||
|
|
||||||
|
return new PositiveRuleMultiKV(element, zoom, selector,
|
||||||
|
keys, values, rules, styles);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addStyle(RenderStyle style) {
|
public RuleBuilder addStyle(RenderStyle style) {
|
||||||
renderStyles.add(style);
|
renderStyles.add(style);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSubRule(RuleBuilder rule) {
|
public RuleBuilder addSubRule(RuleBuilder rule) {
|
||||||
subRules.add(rule);
|
subRules.add(rule);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuleBuilder(boolean positive) {
|
public RuleBuilder style(StyleBuilder... styles) {
|
||||||
this.positiveRule = positive;
|
styleBuilder = styles;
|
||||||
this.element = Element.ANY;
|
return this;
|
||||||
this.zoom = ~0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RuleBuilder get() {
|
public RuleBuilder rules(RuleBuilder... rules) {
|
||||||
return new RuleBuilder(true);
|
for (RuleBuilder rule : rules)
|
||||||
|
subRules.add(rule);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RuleBuilder select(int selector) {
|
public RuleBuilder select(int selector) {
|
||||||
|
@ -1,133 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
|
||||||
* Copyright 2013 Hannes Janetzek
|
|
||||||
*
|
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.oscim.theme.rule;
|
|
||||||
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
final class RuleOptimizer {
|
|
||||||
|
|
||||||
private static AttributeMatcher optimizeKeyMatcher(AttributeMatcher attributeMatcher,
|
|
||||||
Stack<RuleBuilder> ruleStack) {
|
|
||||||
for (int i = 0, n = ruleStack.size(); i < n; ++i) {
|
|
||||||
if (ruleStack.get(i).positiveRule) {
|
|
||||||
RuleBuilder positiveRule = ruleStack.get(i);
|
|
||||||
|
|
||||||
if (positiveRule.keyMatcher != null
|
|
||||||
&& positiveRule.keyMatcher.isCoveredBy(attributeMatcher)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return attributeMatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AttributeMatcher optimizeValueMatcher(
|
|
||||||
AttributeMatcher attributeMatcher, Stack<RuleBuilder> ruleStack) {
|
|
||||||
for (int i = 0, n = ruleStack.size(); i < n; ++i) {
|
|
||||||
if (ruleStack.get(i).positiveRule) {
|
|
||||||
RuleBuilder positiveRule = ruleStack.get(i);
|
|
||||||
|
|
||||||
if (positiveRule.valueMatcher != null
|
|
||||||
&& positiveRule.valueMatcher.isCoveredBy(attributeMatcher)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return attributeMatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
static AttributeMatcher optimize(AttributeMatcher attributeMatcher,
|
|
||||||
Stack<RuleBuilder> ruleStack) {
|
|
||||||
if (attributeMatcher instanceof AnyMatcher)
|
|
||||||
return attributeMatcher;// return null;
|
|
||||||
else if (attributeMatcher instanceof NegativeMatcher) {
|
|
||||||
return attributeMatcher;
|
|
||||||
} else if (attributeMatcher instanceof SingleKeyMatcher) {
|
|
||||||
return optimizeKeyMatcher(attributeMatcher, ruleStack);
|
|
||||||
} else if (attributeMatcher instanceof SingleValueMatcher) {
|
|
||||||
return optimizeValueMatcher(attributeMatcher, ruleStack);
|
|
||||||
} else if (attributeMatcher instanceof MultiKeyMatcher) {
|
|
||||||
return optimizeKeyMatcher(attributeMatcher, ruleStack);
|
|
||||||
} else if (attributeMatcher instanceof MultiValueMatcher) {
|
|
||||||
return optimizeValueMatcher(attributeMatcher, ruleStack);
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("unknown AttributeMatcher: "
|
|
||||||
+ attributeMatcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static ClosedMatcher optimize(ClosedMatcher closedMatcher, Stack<Rule> ruleStack) {
|
|
||||||
// if (closedMatcher == null) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (closedMatcher instanceof AnyMatcher) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for (int i = 0, n = ruleStack.size(); i < n; ++i) {
|
|
||||||
// ClosedMatcher matcher = ruleStack.get(i).mClosedMatcher;
|
|
||||||
// if (matcher == null)
|
|
||||||
// return null;
|
|
||||||
//
|
|
||||||
// if (matcher.isCoveredBy(closedMatcher)) {
|
|
||||||
// return null; // AnyMatcher.getInstance();
|
|
||||||
//
|
|
||||||
// } else if (!closedMatcher.isCoveredBy(ruleStack.get(i).mClosedMatcher)) {
|
|
||||||
// LOG.warning("unreachable rule (closed)");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return closedMatcher;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// static ElementMatcher optimize(ElementMatcher elementMatcher, Stack<Rule> ruleStack) {
|
|
||||||
//
|
|
||||||
// if (elementMatcher == null) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (elementMatcher instanceof AnyMatcher) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for (int i = 0, n = ruleStack.size(); i < n; ++i) {
|
|
||||||
// ElementMatcher matcher = ruleStack.get(i).mElementMatcher;
|
|
||||||
//
|
|
||||||
// if (matcher == null)
|
|
||||||
// return null;
|
|
||||||
//
|
|
||||||
// if (matcher.isCoveredBy(elementMatcher)) {
|
|
||||||
// return null; // AnyMatcher.getInstance();
|
|
||||||
//
|
|
||||||
// } else if (!elementMatcher.isCoveredBy(ruleStack.get(i).mElementMatcher)) {
|
|
||||||
// LOG.warning("unreachable rule (e)");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return elementMatcher;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Private constructor to prevent instantiation from other classes.
|
|
||||||
*/
|
|
||||||
private RuleOptimizer() {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package org.oscim.theme.rule;
|
|
||||||
|
|
||||||
public class Selector {
|
|
||||||
|
|
||||||
public static final int ANY = 0;
|
|
||||||
public static final int FIRST = 1 << 0;
|
|
||||||
public static final int WHEN_MATCHED = 1 << 1;
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
|
||||||
* Copyright 2013 Hannes Janetzek
|
|
||||||
*
|
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.oscim.theme.rule;
|
|
||||||
|
|
||||||
import org.oscim.core.Tag;
|
|
||||||
|
|
||||||
public class SingleKeyMatcher implements AttributeMatcher {
|
|
||||||
private final String mKey;
|
|
||||||
|
|
||||||
public SingleKeyMatcher(String key) {
|
|
||||||
mKey = key.intern();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCoveredBy(AttributeMatcher attributeMatcher) {
|
|
||||||
Tag[] tags = { new Tag(mKey, null) };
|
|
||||||
|
|
||||||
return attributeMatcher == this || attributeMatcher.matches(tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(Tag[] tags) {
|
|
||||||
for (Tag tag : tags)
|
|
||||||
if (mKey == tag.key)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
|
||||||
* Copyright 2013 Hannes Janetzek
|
|
||||||
*
|
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along with
|
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.oscim.theme.rule;
|
|
||||||
|
|
||||||
import org.oscim.core.Tag;
|
|
||||||
|
|
||||||
public class SingleValueMatcher implements AttributeMatcher {
|
|
||||||
private final String mValue;
|
|
||||||
|
|
||||||
public SingleValueMatcher(String value) {
|
|
||||||
mValue = value.intern();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCoveredBy(AttributeMatcher attributeMatcher) {
|
|
||||||
Tag[] tags = { new Tag(null, mValue) };
|
|
||||||
|
|
||||||
return attributeMatcher == this || attributeMatcher.matches(tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(Tag[] tags) {
|
|
||||||
for (Tag tag : tags)
|
|
||||||
if (mValue == tag.value)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -93,7 +93,7 @@ public class AreaStyle extends RenderStyle {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AreaStyle current() {
|
public AreaStyle current() {
|
||||||
return (AreaStyle) (mCurrent == null ? this : mCurrent);
|
return (AreaStyle) mCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -104,7 +104,7 @@ public class AreaStyle extends RenderStyle {
|
|||||||
renderCallback.renderWay(outline, level + 1);
|
renderCallback.renderWay(outline, level + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AreaBuilder {
|
public static class AreaBuilder implements StyleBuilder {
|
||||||
public int level;
|
public int level;
|
||||||
public String style;
|
public String style;
|
||||||
public LineStyle outline;
|
public LineStyle outline;
|
||||||
|
@ -54,6 +54,6 @@ public final class CircleStyle extends RenderStyle {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CircleStyle current() {
|
public CircleStyle current() {
|
||||||
return (CircleStyle) (mCurrent == null ? this : mCurrent);
|
return (CircleStyle) mCurrent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ public class ExtrusionStyle extends RenderStyle {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExtrusionStyle current() {
|
public ExtrusionStyle current() {
|
||||||
return (ExtrusionStyle) (mCurrent == null ? this : mCurrent);
|
return (ExtrusionStyle) mCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int level;
|
private final int level;
|
||||||
|
@ -95,10 +95,10 @@ public final class LineStyle extends RenderStyle {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LineStyle current() {
|
public LineStyle current() {
|
||||||
return (LineStyle) (mCurrent == null ? this : mCurrent);
|
return (LineStyle) mCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final static class LineBuilder {
|
public final static class LineBuilder implements StyleBuilder {
|
||||||
public int level;
|
public int level;
|
||||||
|
|
||||||
public String style;
|
public String style;
|
||||||
@ -203,5 +203,10 @@ public final class LineStyle extends RenderStyle {
|
|||||||
public LineStyle build() {
|
public LineStyle build() {
|
||||||
return new LineStyle(this);
|
return new LineStyle(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LineBuilder cap(Cap cap) {
|
||||||
|
this.cap = cap;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,13 @@ import org.oscim.theme.IRenderTheme.Callback;
|
|||||||
*/
|
*/
|
||||||
public abstract class RenderStyle {
|
public abstract class RenderStyle {
|
||||||
|
|
||||||
RenderStyle mCurrent;
|
public interface StyleBuilder {
|
||||||
|
RenderStyle build();
|
||||||
|
|
||||||
|
StyleBuilder level(int level);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderStyle mCurrent = this;
|
||||||
RenderStyle mNext;
|
RenderStyle mNext;
|
||||||
boolean update;
|
boolean update;
|
||||||
|
|
||||||
|
@ -47,6 +47,6 @@ public final class SymbolStyle extends RenderStyle {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SymbolStyle current() {
|
public SymbolStyle current() {
|
||||||
return (SymbolStyle) (mCurrent == null ? this : mCurrent);
|
return (SymbolStyle) mCurrent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import org.oscim.theme.IRenderTheme.Callback;
|
|||||||
|
|
||||||
public final class TextStyle extends RenderStyle {
|
public final class TextStyle extends RenderStyle {
|
||||||
|
|
||||||
public static class TextBuilder {
|
public static class TextBuilder implements StyleBuilder {
|
||||||
|
|
||||||
public String style;
|
public String style;
|
||||||
public float fontSize;
|
public float fontSize;
|
||||||
@ -134,6 +134,11 @@ public final class TextStyle extends RenderStyle {
|
|||||||
this.strokeWidth = strokeWidth;
|
this.strokeWidth = strokeWidth;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TextBuilder level(int level) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextStyle(TextBuilder tb) {
|
TextStyle(TextBuilder tb) {
|
||||||
@ -193,7 +198,7 @@ public final class TextStyle extends RenderStyle {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TextStyle current() {
|
public TextStyle current() {
|
||||||
return (TextStyle) (mCurrent == null ? this : mCurrent);
|
return (TextStyle) mCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user