From b879830045dc05271413e96f0fd90dff460b1823 Mon Sep 17 00:00:00 2001
From: Hannes Janetzek <hannes.janetzek@gmail.com>
Date: Sun, 9 Mar 2014 16:11:44 +0100
Subject: [PATCH] start ThemeBuilder api

---
 vtm/src/org/oscim/theme/ThemeBuilder.java     | 125 ++++++++++++++++++
 vtm/src/org/oscim/theme/XmlThemeBuilder.java  |   4 +-
 vtm/src/org/oscim/theme/rule/RuleBuilder.java |  65 ++++++---
 .../oscim/theme/rule/SingleKeyMatcher.java    |   4 +-
 .../oscim/theme/rule/SingleValueMatcher.java  |   4 +-
 5 files changed, 181 insertions(+), 21 deletions(-)
 create mode 100644 vtm/src/org/oscim/theme/ThemeBuilder.java

diff --git a/vtm/src/org/oscim/theme/ThemeBuilder.java b/vtm/src/org/oscim/theme/ThemeBuilder.java
new file mode 100644
index 00000000..cdb1481d
--- /dev/null
+++ b/vtm/src/org/oscim/theme/ThemeBuilder.java
@@ -0,0 +1,125 @@
+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.Area;
+import org.oscim.theme.styles.Line;
+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 Line(1, 1, 1))
+		    .pop()
+
+		    .push(RuleBuilder.get().select(Selector.WHEN_MATCHED))
+		    .addStyle(new Area(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);
+
+	}
+}
diff --git a/vtm/src/org/oscim/theme/XmlThemeBuilder.java b/vtm/src/org/oscim/theme/XmlThemeBuilder.java
index bfaf9521..e22f56b1 100644
--- a/vtm/src/org/oscim/theme/XmlThemeBuilder.java
+++ b/vtm/src/org/oscim/theme/XmlThemeBuilder.java
@@ -863,13 +863,13 @@ public class XmlThemeBuilder extends DefaultHandler {
 		return new Extrusion(level, colorSide, colorTop, colorLine, defaultHeight);
 	}
 
-	private static void validateNonNegative(String name, float value) {
+	public static void validateNonNegative(String name, float value) {
 		if (value < 0)
 			throw new ThemeException(name + " must not be negative: "
 			        + value);
 	}
 
-	private static void validateExists(String name, Object obj, String elementName) {
+	public static void validateExists(String name, Object obj, String elementName) {
 		if (obj == null)
 			throw new ThemeException("missing attribute " + name
 			        + " for element: " + elementName);
diff --git a/vtm/src/org/oscim/theme/rule/RuleBuilder.java b/vtm/src/org/oscim/theme/rule/RuleBuilder.java
index 6f80b9b8..00c3cca0 100644
--- a/vtm/src/org/oscim/theme/rule/RuleBuilder.java
+++ b/vtm/src/org/oscim/theme/rule/RuleBuilder.java
@@ -47,13 +47,13 @@ public class RuleBuilder {
 		this.valueMatcher = valueMatcher;
 	}
 
-	private static RuleBuilder createRule(Stack<RuleBuilder> ruleStack, int element, String keys,
-	        String values, byte zoomMin, byte zoomMax, int selector) {
+	public RuleBuilder(boolean positive, AttributeMatcher keyMatcher, AttributeMatcher valueMatcher) {
+		this.positiveRule = positive;
+		this.keyMatcher = keyMatcher;
+		this.valueMatcher = valueMatcher;
+	}
 
-		// zoom-level bitmask
-		int zoom = 0;
-		for (int z = zoomMin; z <= zoomMax && z < 32; z++)
-			zoom |= (1 << z);
+	public static RuleBuilder create(Stack<RuleBuilder> ruleStack, String keys, String values) {
 
 		List<String> keyList = null, valueList = null;
 		boolean negativeRule = false;
@@ -90,16 +90,16 @@ public class RuleBuilder {
 
 			if (negativeRule) {
 				AttributeMatcher m = new NegativeMatcher(keyList, valueList, false);
-				return new RuleBuilder(false, element, zoom, selector, m, null);
+				return new RuleBuilder(false, m, null);
 			} else if (exclusionRule) {
 				AttributeMatcher m = new NegativeMatcher(keyList, valueList, true);
-				return new RuleBuilder(false, element, zoom, selector, m, null);
+				return new RuleBuilder(false, m, null);
 			}
 
 			keyMatcher = RuleOptimizer.optimize(keyMatcher, ruleStack);
 		}
 
-		return new RuleBuilder(true, element, zoom, selector, keyMatcher, valueMatcher);
+		return new RuleBuilder(true, keyMatcher, valueMatcher);
 	}
 
 	private static AttributeMatcher getKeyMatcher(List<String> keyList) {
@@ -137,11 +137,9 @@ public class RuleBuilder {
 	}
 
 	private static void validate(byte zoomMin, byte zoomMax) {
-		if (zoomMin < 0)
-			throw new ThemeException("zoom-min must not be negative: " + zoomMin);
-		else if (zoomMax < 0)
-			throw new ThemeException("zoom-max must not be negative: " + zoomMax);
-		else if (zoomMin > 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);
 	}
 
@@ -196,7 +194,20 @@ public class RuleBuilder {
 
 		validate(zoomMin, zoomMax);
 
-		return createRule(ruleStack, element, keys, values, zoomMin, zoomMax, selector);
+		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) {
+		// zoom-level bitmask
+		zoom = 0;
+		for (int z = zoomMin; z <= zoomMax && z < 32; z++)
+			zoom |= (1 << z);
+
+		return this;
 	}
 
 	public Rule onComplete() {
@@ -233,4 +244,28 @@ public class RuleBuilder {
 		subRules.add(rule);
 	}
 
+	RuleBuilder(boolean positive) {
+		this.positiveRule = positive;
+		this.element = Element.ANY;
+		this.zoom = ~0;
+	}
+
+	public static RuleBuilder get() {
+		return new RuleBuilder(true);
+	}
+
+	public RuleBuilder select(int selector) {
+		this.selector = selector;
+		return this;
+	}
+
+	public RuleBuilder zoom(int zoom) {
+		this.zoom = zoom;
+		return this;
+	}
+
+	public RuleBuilder element(int element) {
+		this.element = element;
+		return this;
+	}
 }
diff --git a/vtm/src/org/oscim/theme/rule/SingleKeyMatcher.java b/vtm/src/org/oscim/theme/rule/SingleKeyMatcher.java
index 12eda8a5..90b26088 100644
--- a/vtm/src/org/oscim/theme/rule/SingleKeyMatcher.java
+++ b/vtm/src/org/oscim/theme/rule/SingleKeyMatcher.java
@@ -19,10 +19,10 @@ package org.oscim.theme.rule;
 
 import org.oscim.core.Tag;
 
-class SingleKeyMatcher implements AttributeMatcher {
+public class SingleKeyMatcher implements AttributeMatcher {
 	private final String mKey;
 
-	SingleKeyMatcher(String key) {
+	public SingleKeyMatcher(String key) {
 		mKey = key.intern();
 	}
 
diff --git a/vtm/src/org/oscim/theme/rule/SingleValueMatcher.java b/vtm/src/org/oscim/theme/rule/SingleValueMatcher.java
index 5327bf56..8dc3ef7b 100644
--- a/vtm/src/org/oscim/theme/rule/SingleValueMatcher.java
+++ b/vtm/src/org/oscim/theme/rule/SingleValueMatcher.java
@@ -19,10 +19,10 @@ package org.oscim.theme.rule;
 
 import org.oscim.core.Tag;
 
-class SingleValueMatcher implements AttributeMatcher {
+public class SingleValueMatcher implements AttributeMatcher {
 	private final String mValue;
 
-	SingleValueMatcher(String value) {
+	public SingleValueMatcher(String value) {
 		mValue = value.intern();
 	}