/*
* 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;
import java.util.ArrayList;
import org.oscim.theme.IRenderTheme.ThemeException;
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.StyleBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RuleBuilder {
final static Logger log = LoggerFactory.getLogger(RuleBuilder.class);
private final static String[] EMPTY_KV = {};
public enum RuleType {
POSITIVE,
NEGATIVE,
EXCLUDE
}
int zoom;
int element;
int selector;
RuleType type;
String keys[];
String values[];
ArrayList<RenderStyle> renderStyles = new ArrayList<RenderStyle>(4);
ArrayList<RuleBuilder> subRules = new ArrayList<RuleBuilder>(4);
StyleBuilder<?>[] styleBuilder;
private static final String STRING_NEGATION = "~";
private static final String STRING_EXCLUSIVE = "-";
private static final String SEPARATOR = "\\|";
//private static final String STRING_WILDCARD = "*";
public RuleBuilder(RuleType type, int element, int zoom, int selector,
String[] keys, String[] values) {
this.type = type;
this.element = element;
this.zoom = zoom;
this.selector = selector;
this.keys = keys;
this.values = values;
}
public RuleBuilder(RuleType type, String[] keys, String[] values) {
this.element = Element.ANY;
this.zoom = ~0;
this.type = type;
this.keys = keys;
this.values = values;
}
public RuleBuilder() {
this.type = RuleType.POSITIVE;
this.element = Element.ANY;
this.zoom = ~0;
this.keys = EMPTY_KV;
this.values = EMPTY_KV;
}
public static RuleBuilder create(String keys, String values) {
String[] keyList = EMPTY_KV;
String[] valueList = EMPTY_KV;
RuleType type = RuleType.POSITIVE;
if (values != null) {
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 {
valueList = values.split(SEPARATOR);
}
}
if (keys != null) {
keyList = keys.split(SEPARATOR);
}
if (type != RuleType.POSITIVE) {
if (keyList == null || keyList.length == 0)
throw new ThemeException("negative rule requires key");
}
return new RuleBuilder(type, keyList, valueList);
}
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(int[] level) {
RenderStyle[] styles = 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) {
styles = new RenderStyle[renderStyles.size()];
renderStyles.toArray(styles);
}
if (subRules.size() > 0) {
rules = new Rule[subRules.size()];
for (int i = 0; i < rules.length; i++)
rules[i] = subRules.get(i).onComplete(level);
}
int numKeys = keys.length;
int numVals = values.length;
if (numKeys == 0 && numVals == 0)
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);
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 RuleBuilder addStyle(RenderStyle style) {
renderStyles.add(style);
return this;
}
public RuleBuilder addSubRule(RuleBuilder rule) {
subRules.add(rule);
return this;
}
public RuleBuilder style(StyleBuilder<?>... styles) {
styleBuilder = styles;
return this;
}
public RuleBuilder rules(RuleBuilder... rules) {
for (RuleBuilder rule : rules)
subRules.add(rule);
return this;
}
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;
}
}