887 lines
25 KiB
Java
887 lines
25 KiB
Java
package net.osmand.router;
|
|
|
|
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
|
|
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule;
|
|
import net.osmand.binary.RouteDataObject;
|
|
import net.osmand.router.BinaryRoutePlanner.RouteSegment;
|
|
import net.osmand.util.Algorithms;
|
|
import net.osmand.util.MapUtils;
|
|
|
|
import java.io.PrintStream;
|
|
import java.util.ArrayList;
|
|
import java.util.BitSet;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.LinkedHashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.Set;
|
|
|
|
import gnu.trove.set.hash.TLongHashSet;
|
|
|
|
public class GeneralRouter implements VehicleRouter {
|
|
|
|
private static final float CAR_SHORTEST_DEFAULT_SPEED = 55/3.6f;
|
|
public static final String USE_SHORTEST_WAY = "short_way";
|
|
public static final String AVOID_FERRIES = "avoid_ferries";
|
|
public static final String AVOID_TOLL = "avoid_toll";
|
|
public static final String AVOID_MOTORWAY = "avoid_motorway";
|
|
public static final String AVOID_UNPAVED = "avoid_unpaved";
|
|
public static final String PREFER_MOTORWAYS = "prefer_motorway";
|
|
|
|
private final RouteAttributeContext[] objectAttributes;
|
|
public final Map<String, String> attributes;
|
|
private final Map<String, RoutingParameter> parameters;
|
|
private final Map<String, Integer> universalRules;
|
|
private final List<String> universalRulesById;
|
|
private final Map<String, BitSet> tagRuleMask;
|
|
private final ArrayList<Object> ruleToValue;
|
|
private boolean shortestRoute;
|
|
|
|
private Map<RouteRegion, Map<Integer, Integer>> regionConvert = new LinkedHashMap<RouteRegion, Map<Integer,Integer>>();
|
|
|
|
// cached values
|
|
private boolean restrictionsAware = true;
|
|
private float leftTurn;
|
|
private float roundaboutTurn;
|
|
private float rightTurn;
|
|
// speed in m/s
|
|
private float minDefaultSpeed = 10;
|
|
// speed in m/s
|
|
private float maxDefaultSpeed = 10;
|
|
|
|
private TLongHashSet impassableRoads;
|
|
|
|
|
|
public enum RouteDataObjectAttribute {
|
|
ROAD_SPEED("speed"),
|
|
ROAD_PRIORITIES("priority"),
|
|
ACCESS("access"),
|
|
OBSTACLES("obstacle_time"),
|
|
ROUTING_OBSTACLES("obstacle"),
|
|
ONEWAY("oneway"),
|
|
PENALTY_TRANSITION("penalty_transition");
|
|
public final String nm;
|
|
RouteDataObjectAttribute(String name) {
|
|
nm = name;
|
|
}
|
|
|
|
public static RouteDataObjectAttribute getValueOf(String s){
|
|
for(RouteDataObjectAttribute a : RouteDataObjectAttribute.values()){
|
|
if(a.nm.equals(s)){
|
|
return a;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public enum GeneralRouterProfile {
|
|
CAR,
|
|
PEDESTRIAN,
|
|
BICYCLE
|
|
}
|
|
|
|
|
|
public enum RoutingParameterType {
|
|
NUMERIC,
|
|
BOOLEAN,
|
|
SYMBOLIC
|
|
}
|
|
|
|
public GeneralRouter(GeneralRouterProfile profile, Map<String, String> attributes) {
|
|
this.attributes = new LinkedHashMap<String, String>();
|
|
Iterator<Entry<String, String>> e = attributes.entrySet().iterator();
|
|
while(e.hasNext()){
|
|
Entry<String, String> next = e.next();
|
|
addAttribute(next.getKey(), next.getValue());
|
|
}
|
|
objectAttributes = new RouteAttributeContext[RouteDataObjectAttribute.values().length];
|
|
for (int i = 0; i < objectAttributes.length; i++) {
|
|
objectAttributes[i] = new RouteAttributeContext();
|
|
}
|
|
universalRules = new LinkedHashMap<String, Integer>();
|
|
universalRulesById = new ArrayList<String>();
|
|
tagRuleMask = new LinkedHashMap<String, BitSet>();
|
|
ruleToValue = new ArrayList<Object>();
|
|
parameters = new LinkedHashMap<String, GeneralRouter.RoutingParameter>();
|
|
}
|
|
|
|
public GeneralRouter(GeneralRouter parent, Map<String, String> params) {
|
|
this.attributes = new LinkedHashMap<String, String>();
|
|
Iterator<Entry<String, String>> e = parent.attributes.entrySet().iterator();
|
|
while (e.hasNext()) {
|
|
Entry<String, String> next = e.next();
|
|
addAttribute(next.getKey(), next.getValue());
|
|
}
|
|
// do not copy, keep linked
|
|
universalRules = parent.universalRules;
|
|
universalRulesById = parent.universalRulesById;
|
|
tagRuleMask = parent.tagRuleMask;
|
|
ruleToValue = parent.ruleToValue;
|
|
parameters = parent.parameters;
|
|
|
|
objectAttributes = new RouteAttributeContext[RouteDataObjectAttribute.values().length];
|
|
for (int i = 0; i < objectAttributes.length; i++) {
|
|
objectAttributes[i] = new RouteAttributeContext(parent.objectAttributes[i], params);
|
|
}
|
|
shortestRoute = params.containsKey(USE_SHORTEST_WAY) && parseSilentBoolean(params.get(USE_SHORTEST_WAY), false);
|
|
if(shortestRoute) {
|
|
maxDefaultSpeed = Math.min(CAR_SHORTEST_DEFAULT_SPEED, maxDefaultSpeed);
|
|
}
|
|
|
|
}
|
|
|
|
public Map<String, RoutingParameter> getParameters() {
|
|
return parameters;
|
|
}
|
|
|
|
public void addAttribute(String k, String v) {
|
|
attributes.put(k, v);
|
|
if(k.equals("restrictionsAware")) {
|
|
restrictionsAware = parseSilentBoolean(v, restrictionsAware);
|
|
} else if(k.equals("leftTurn")) {
|
|
leftTurn = parseSilentFloat(v, leftTurn);
|
|
} else if(k.equals("rightTurn")) {
|
|
rightTurn = parseSilentFloat(v, rightTurn);
|
|
} else if(k.equals("roundaboutTurn")) {
|
|
roundaboutTurn = parseSilentFloat(v, roundaboutTurn);
|
|
} else if(k.equals("minDefaultSpeed")) {
|
|
minDefaultSpeed = parseSilentFloat(v, minDefaultSpeed * 3.6f) / 3.6f;
|
|
} else if(k.equals("maxDefaultSpeed")) {
|
|
maxDefaultSpeed = parseSilentFloat(v, maxDefaultSpeed * 3.6f) / 3.6f;
|
|
}
|
|
}
|
|
|
|
public RouteAttributeContext getObjContext(RouteDataObjectAttribute a) {
|
|
return objectAttributes[a.ordinal()];
|
|
}
|
|
|
|
|
|
public void registerBooleanParameter(String id, String name, String description) {
|
|
RoutingParameter rp = new RoutingParameter();
|
|
rp.name = name;
|
|
rp.description = description;
|
|
rp.id = id;
|
|
rp.type = RoutingParameterType.BOOLEAN;
|
|
parameters.put(rp.id, rp);
|
|
|
|
}
|
|
|
|
public void registerNumericParameter(String id, String name, String description, Double[] vls, String[] vlsDescriptions) {
|
|
RoutingParameter rp = new RoutingParameter();
|
|
rp.name = name;
|
|
rp.description = description;
|
|
rp.id = id;
|
|
rp.possibleValues = vls;
|
|
rp.possibleValueDescriptions = vlsDescriptions;
|
|
rp.type = RoutingParameterType.NUMERIC;
|
|
parameters.put(rp.id, rp);
|
|
}
|
|
|
|
@Override
|
|
public boolean acceptLine(RouteDataObject way) {
|
|
int res = getObjContext(RouteDataObjectAttribute.ACCESS).evaluateInt(way, 0);
|
|
if(impassableRoads != null && impassableRoads.contains(way.id)) {
|
|
return false;
|
|
}
|
|
return res >= 0;
|
|
}
|
|
|
|
public long[] getImpassableRoadIds() {
|
|
if(impassableRoads == null) {
|
|
return new long[0];
|
|
}
|
|
return impassableRoads.toArray();
|
|
}
|
|
|
|
private int registerTagValueAttribute(String tag, String value) {
|
|
String key = tag +"$"+value;
|
|
if(universalRules.containsKey(key)) {
|
|
return universalRules.get(key);
|
|
}
|
|
int id = universalRules.size();
|
|
universalRulesById.add(key);
|
|
universalRules.put(key, id);
|
|
if(!tagRuleMask.containsKey(tag)) {
|
|
tagRuleMask.put(tag, new BitSet());
|
|
}
|
|
tagRuleMask.get(tag).set(id);
|
|
return id;
|
|
}
|
|
|
|
|
|
private Object parseValue(String value, String type) {
|
|
float vl = -1;
|
|
value = value.trim();
|
|
if("speed".equals(type)) {
|
|
vl = RouteDataObject.parseSpeed(value, vl);
|
|
} else if("weight".equals(type)) {
|
|
vl = RouteDataObject.parseWeightInTon(value, vl);
|
|
} else if("length".equals(type)) {
|
|
vl = RouteDataObject.parseLength(value, vl);
|
|
} else {
|
|
int i = Algorithms.findFirstNumberEndIndex(value);
|
|
if (i > 0) {
|
|
// could be negative
|
|
return Float.parseFloat(value.substring(0, i));
|
|
}
|
|
}
|
|
if(vl == -1) {
|
|
return null;
|
|
}
|
|
return vl;
|
|
}
|
|
|
|
private Object parseValueFromTag(int id, String type) {
|
|
while (ruleToValue.size() <= id) {
|
|
ruleToValue.add(null);
|
|
}
|
|
Object res = ruleToValue.get(id);
|
|
if (res == null) {
|
|
String v = universalRulesById.get(id);
|
|
String value = v.substring(v.indexOf('$') + 1);
|
|
res = parseValue(value, type);
|
|
if (res == null) {
|
|
res = "";
|
|
}
|
|
ruleToValue.set(id, res);
|
|
}
|
|
if ("".equals(res)) {
|
|
return null;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
@Override
|
|
public GeneralRouter build(Map<String, String> params) {
|
|
return new GeneralRouter(this, params);
|
|
}
|
|
|
|
@Override
|
|
public boolean restrictionsAware() {
|
|
return restrictionsAware;
|
|
}
|
|
|
|
@Override
|
|
public float defineObstacle(RouteDataObject road, int point) {
|
|
int[] pointTypes = road.getPointTypes(point);
|
|
if(pointTypes != null) {
|
|
return getObjContext(RouteDataObjectAttribute.OBSTACLES).evaluateFloat(road.region, pointTypes, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public float defineRoutingObstacle(RouteDataObject road, int point) {
|
|
int[] pointTypes = road.getPointTypes(point);
|
|
if(pointTypes != null){
|
|
return getObjContext(RouteDataObjectAttribute.ROUTING_OBSTACLES).evaluateFloat(road.region, pointTypes, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public int isOneWay(RouteDataObject road) {
|
|
return getObjContext(RouteDataObjectAttribute.ONEWAY).evaluateInt(road, 0);
|
|
}
|
|
|
|
@Override
|
|
public float getPenaltyTransition(RouteDataObject road) {
|
|
return getObjContext(RouteDataObjectAttribute.PENALTY_TRANSITION).evaluateInt(road, 0);
|
|
}
|
|
|
|
@Override
|
|
public float defineRoutingSpeed(RouteDataObject road) {
|
|
return Math.min(defineVehicleSpeed(road), maxDefaultSpeed);
|
|
}
|
|
|
|
@Override
|
|
public float defineVehicleSpeed(RouteDataObject road) {
|
|
return getObjContext(RouteDataObjectAttribute.ROAD_SPEED) .evaluateFloat(road, getMinDefaultSpeed());
|
|
}
|
|
|
|
@Override
|
|
public float defineSpeedPriority(RouteDataObject road) {
|
|
return getObjContext(RouteDataObjectAttribute.ROAD_PRIORITIES).evaluateFloat(road, 1f);
|
|
}
|
|
|
|
@Override
|
|
public float getMinDefaultSpeed() {
|
|
return minDefaultSpeed;
|
|
}
|
|
|
|
@Override
|
|
public float getMaxDefaultSpeed() {
|
|
return maxDefaultSpeed;
|
|
}
|
|
|
|
|
|
public double getLeftTurn() {
|
|
return leftTurn;
|
|
}
|
|
|
|
public double getRightTurn() {
|
|
return rightTurn;
|
|
}
|
|
public double getRoundaboutTurn() {
|
|
return roundaboutTurn;
|
|
}
|
|
@Override
|
|
public double calculateTurnTime(RouteSegment segment, int segmentEnd, RouteSegment prev, int prevSegmentEnd) {
|
|
float ts = getPenaltyTransition(segment.getRoad());
|
|
float prevTs = getPenaltyTransition(prev.getRoad());
|
|
if(prevTs != ts) {
|
|
return Math.abs(ts - prevTs) / 2;
|
|
}
|
|
// int[] pt = prev.getRoad().getPointTypes(prevSegmentEnd);
|
|
// if(pt != null) {
|
|
// RouteRegion reg = prev.getRoad().region;
|
|
// for (int i = 0; i < pt.length; i++) {
|
|
// RouteTypeRule r = reg.quickGetEncodingRule(pt[i]);
|
|
// if ("highway".equals(r.getTag()) && "traffic_signals".equals(r.getValue())) {
|
|
// // traffic signals don't add turn info
|
|
// return 0;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
|
|
if(segment.getRoad().roundabout() && !prev.getRoad().roundabout()) {
|
|
double rt = getRoundaboutTurn();
|
|
if(rt > 0) {
|
|
return rt;
|
|
}
|
|
}
|
|
if (getLeftTurn() > 0 || getRightTurn() > 0) {
|
|
double a1 = segment.getRoad().directionRoute(segment.getSegmentStart(), segment.getSegmentStart() < segmentEnd);
|
|
double a2 = prev.getRoad().directionRoute(prevSegmentEnd, prevSegmentEnd < prev.getSegmentStart());
|
|
double diff = Math.abs(MapUtils.alignAngleDifference(a1 - a2 - Math.PI));
|
|
// more like UT
|
|
if (diff > 2 * Math.PI / 3) {
|
|
return getLeftTurn();
|
|
} else if (diff > Math.PI / 2) {
|
|
return getRightTurn();
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
@Override
|
|
public boolean containsAttribute(String attribute) {
|
|
return attributes.containsKey(attribute);
|
|
}
|
|
|
|
@Override
|
|
public String getAttribute(String attribute) {
|
|
return attributes.get(attribute);
|
|
}
|
|
|
|
private static boolean parseSilentBoolean(String t, boolean v) {
|
|
if (t == null || t.length() == 0) {
|
|
return v;
|
|
}
|
|
return Boolean.parseBoolean(t);
|
|
}
|
|
|
|
private static float parseSilentFloat(String t, float v) {
|
|
if (t == null || t.length() == 0) {
|
|
return v;
|
|
}
|
|
return Float.parseFloat(t);
|
|
}
|
|
|
|
|
|
|
|
public static class RoutingParameter {
|
|
private String id;
|
|
private String name;
|
|
private String description;
|
|
private RoutingParameterType type;
|
|
private Object[] possibleValues;
|
|
private String[] possibleValueDescriptions;
|
|
|
|
public String getId() {
|
|
return id;
|
|
}
|
|
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
public String getDescription() {
|
|
return description;
|
|
}
|
|
public RoutingParameterType getType() {
|
|
return type;
|
|
}
|
|
public String[] getPossibleValueDescriptions() {
|
|
return possibleValueDescriptions;
|
|
}
|
|
|
|
public Object[] getPossibleValues() {
|
|
return possibleValues;
|
|
}
|
|
}
|
|
|
|
|
|
private class ParameterContext {
|
|
private Map<String, String> vars;
|
|
}
|
|
|
|
public class RouteAttributeContext {
|
|
List<RouteAttributeEvalRule> rules = new ArrayList<RouteAttributeEvalRule>();
|
|
ParameterContext paramContext = null;
|
|
|
|
public RouteAttributeContext(){
|
|
}
|
|
public RouteAttributeContext(RouteAttributeContext original, Map<String, String> params){
|
|
if (params != null) {
|
|
paramContext = new ParameterContext();
|
|
paramContext.vars = params;
|
|
}
|
|
for(RouteAttributeEvalRule rt : original.rules){
|
|
if(checkParameter(rt)){
|
|
rules.add(rt);
|
|
}
|
|
}
|
|
}
|
|
|
|
public RouteAttributeEvalRule[] getRules() {
|
|
return rules.toArray(new RouteAttributeEvalRule[rules.size()]);
|
|
}
|
|
|
|
public String[] getParamKeys() {
|
|
if(paramContext == null) {
|
|
return new String[0];
|
|
}
|
|
return paramContext.vars.keySet().toArray(new String[paramContext.vars.size()]);
|
|
}
|
|
|
|
public String[] getParamValues() {
|
|
if(paramContext == null) {
|
|
return new String[0];
|
|
}
|
|
return paramContext.vars.values().toArray(new String[paramContext.vars.size()]);
|
|
}
|
|
|
|
private Object evaluate(RouteDataObject ro) {
|
|
return evaluate(convert(ro.region, ro.types));
|
|
}
|
|
|
|
public void printRules(PrintStream out) {
|
|
for(RouteAttributeEvalRule r : rules) {
|
|
r.printRule(out);
|
|
}
|
|
}
|
|
|
|
public RouteAttributeEvalRule registerNewRule(String selectValue, String selectType) {
|
|
RouteAttributeEvalRule ev = new RouteAttributeEvalRule();
|
|
ev.registerSelectValue(selectValue, selectType);
|
|
rules.add(ev);
|
|
return ev;
|
|
}
|
|
|
|
public RouteAttributeEvalRule getLastRule() {
|
|
return rules.get(rules.size() - 1);
|
|
}
|
|
|
|
private Object evaluate(BitSet types) {
|
|
for (int k = 0; k < rules.size(); k++) {
|
|
RouteAttributeEvalRule r = rules.get(k);
|
|
Object o = r.eval(types, paramContext);
|
|
if (o != null) {
|
|
return o;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private boolean checkParameter(RouteAttributeEvalRule r) {
|
|
if (paramContext != null && r.parameters.size() > 0) {
|
|
for (String p : r.parameters) {
|
|
boolean not = false;
|
|
if (p.startsWith("-")) {
|
|
not = true;
|
|
p = p.substring(1);
|
|
}
|
|
boolean val = paramContext.vars.containsKey(p);
|
|
if (not && val) {
|
|
return false;
|
|
} else if (!not && !val) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public int evaluateInt(RouteDataObject ro, int defValue) {
|
|
Object o = evaluate(ro);
|
|
if(!(o instanceof Number)) {
|
|
return defValue;
|
|
}
|
|
return ((Number)o).intValue();
|
|
}
|
|
|
|
public int evaluateInt(RouteRegion region, int[] types, int defValue) {
|
|
Object o = evaluate(convert(region, types));
|
|
if(!(o instanceof Number)){
|
|
return defValue;
|
|
}
|
|
return ((Number)o).intValue();
|
|
}
|
|
|
|
public float evaluateFloat(RouteDataObject ro, float defValue) {
|
|
Object o = evaluate(ro);
|
|
if(!(o instanceof Number)) {
|
|
return defValue;
|
|
}
|
|
return ((Number)o).floatValue();
|
|
}
|
|
|
|
public float evaluateFloat(RouteRegion region, int[] types, float defValue) {
|
|
Object o = evaluate(convert(region, types));
|
|
if(!(o instanceof Number)) {
|
|
return defValue;
|
|
}
|
|
return ((Number)o).floatValue();
|
|
}
|
|
|
|
private BitSet convert(RouteRegion reg, int[] types) {
|
|
BitSet b = new BitSet(universalRules.size());
|
|
Map<Integer, Integer> map = regionConvert.get(reg);
|
|
if(map == null){
|
|
map = new HashMap<Integer, Integer>();
|
|
regionConvert.put(reg, map);
|
|
}
|
|
for(int k = 0; k < types.length; k++) {
|
|
Integer nid = map.get(types[k]);
|
|
if(nid == null){
|
|
RouteTypeRule r = reg.quickGetEncodingRule(types[k]);
|
|
nid = registerTagValueAttribute(r.getTag(), r.getValue());
|
|
map.put(types[k], nid);
|
|
}
|
|
b.set(nid);
|
|
}
|
|
return b;
|
|
}
|
|
}
|
|
|
|
public class RouteAttributeExpression {
|
|
public static final int LESS_EXPRESSION = 1;
|
|
public static final int GREAT_EXPRESSION = 2;
|
|
|
|
public RouteAttributeExpression(String[] vs, String valueType, int expressionId) {
|
|
this.expressionType = expressionId;
|
|
this.values = vs;
|
|
if (vs.length < 2) {
|
|
throw new IllegalStateException("Expression should have at least 2 arguments");
|
|
}
|
|
this.cacheValues = new Number[vs.length];
|
|
this.valueType = valueType;
|
|
for (int i = 0; i < vs.length; i++) {
|
|
if(!vs[i].startsWith("$") && !vs[i].startsWith(":")) {
|
|
Object o = parseValue(vs[i], valueType);
|
|
if (o instanceof Number) {
|
|
cacheValues[i] = (Number) o;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// definition
|
|
private String[] values;
|
|
private int expressionType;
|
|
private String valueType;
|
|
// numbers
|
|
private Number[] cacheValues;
|
|
|
|
public boolean matches(BitSet types, ParameterContext paramContext) {
|
|
double f1 = calculateExprValue(0, types, paramContext);
|
|
double f2 = calculateExprValue(1, types, paramContext);
|
|
if (Double.isNaN(f1) || Double.isNaN(f2)) {
|
|
return false;
|
|
}
|
|
if (expressionType == LESS_EXPRESSION) {
|
|
return f1 <= f2;
|
|
} else if (expressionType == GREAT_EXPRESSION) {
|
|
return f1 >= f2;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private double calculateExprValue(int id, BitSet types, ParameterContext paramContext) {
|
|
String value = values[id];
|
|
Number cacheValue = cacheValues[id];
|
|
if(cacheValue != null) {
|
|
return cacheValue.doubleValue();
|
|
}
|
|
Object o = null;
|
|
if (value instanceof String && value.toString().startsWith("$")) {
|
|
BitSet mask = tagRuleMask.get(value.toString().substring(1));
|
|
if (mask != null && mask.intersects(types)) {
|
|
BitSet findBit = new BitSet(mask.size());
|
|
findBit.or(mask);
|
|
findBit.and(types);
|
|
int v = findBit.nextSetBit(0);
|
|
o = parseValueFromTag(v, valueType);
|
|
}
|
|
} else if (value instanceof String && value.toString().startsWith(":")) {
|
|
String p = ((String) value).substring(1);
|
|
if (paramContext != null && paramContext.vars.containsKey(p)) {
|
|
o = parseValue(paramContext.vars.get(p), valueType);
|
|
}
|
|
}
|
|
|
|
if(o instanceof Number) {
|
|
return ((Number)o).doubleValue();
|
|
}
|
|
return Double.NaN;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
public class RouteAttributeEvalRule {
|
|
protected List<String> parameters = new ArrayList<String>() ;
|
|
protected List<String> tagValueCondDefTag = new ArrayList<String>();
|
|
protected List<String> tagValueCondDefValue = new ArrayList<String>();
|
|
protected List<Boolean> tagValueCondDefNot = new ArrayList<Boolean>();
|
|
|
|
protected String selectValueDef = null;
|
|
protected Object selectValue = null;
|
|
protected String selectType = null;
|
|
protected BitSet filterTypes = new BitSet();
|
|
protected BitSet filterNotTypes = new BitSet();
|
|
protected BitSet evalFilterTypes = new BitSet();
|
|
|
|
protected Set<String> onlyTags = new LinkedHashSet<String>();
|
|
protected Set<String> onlyNotTags = new LinkedHashSet<String>();
|
|
protected List<RouteAttributeExpression> expressions = new ArrayList<RouteAttributeExpression>();
|
|
|
|
|
|
public RouteAttributeExpression[] getExpressions() {
|
|
return expressions.toArray(new RouteAttributeExpression[expressions.size()]);
|
|
}
|
|
|
|
public String[] getParameters() {
|
|
return parameters.toArray(new String[parameters.size()]);
|
|
}
|
|
|
|
public String[] getTagValueCondDefTag() {
|
|
return tagValueCondDefTag.toArray(new String[tagValueCondDefTag.size()]);
|
|
}
|
|
|
|
public String[] getTagValueCondDefValue() {
|
|
return tagValueCondDefValue.toArray(new String[tagValueCondDefValue.size()]);
|
|
}
|
|
|
|
public boolean[] getTagValueCondDefNot() {
|
|
boolean[] r = new boolean[tagValueCondDefNot.size()];
|
|
for (int i = 0; i < r.length; i++) {
|
|
r[i] = tagValueCondDefNot.get(i).booleanValue();
|
|
}
|
|
return r;
|
|
}
|
|
|
|
public void registerSelectValue(String value, String type) {
|
|
selectType = type;
|
|
selectValueDef = value;
|
|
if(value.startsWith(":") || value.startsWith("$")) {
|
|
selectValue = value;
|
|
} else {
|
|
selectValue = parseValue(value, type);
|
|
if(selectValue == null) {
|
|
System.err.println("Routing.xml select value '" + value+"' was not registered");
|
|
}
|
|
}
|
|
}
|
|
|
|
public void printRule(PrintStream out) {
|
|
out.print(" Select " + selectValue + " if ");
|
|
for(int k = 0; k < filterTypes.size(); k++) {
|
|
if(filterTypes.get(k)) {
|
|
String key = universalRulesById.get(k);
|
|
out.print(key + " ");
|
|
}
|
|
}
|
|
if(filterNotTypes.size() > 0) {
|
|
out.print(" ifnot ");
|
|
}
|
|
for(int k = 0; k < filterNotTypes.size(); k++) {
|
|
if(filterNotTypes.get(k)) {
|
|
String key = universalRulesById.get(k);
|
|
out.print(key + " ");
|
|
}
|
|
}
|
|
for(int k = 0; k < parameters.size(); k++) {
|
|
out.print(" param="+parameters.get(k));
|
|
}
|
|
if(onlyTags.size() > 0) {
|
|
out.print(" match tag = " + onlyTags);
|
|
}
|
|
if(onlyNotTags.size() > 0) {
|
|
out.print(" not match tag = " + onlyNotTags);
|
|
}
|
|
if(expressions.size() > 0) {
|
|
out.println(" subexpressions " + expressions.size());
|
|
}
|
|
out.println();
|
|
}
|
|
|
|
public void registerAndTagValueCondition(String tag, String value, boolean not) {
|
|
tagValueCondDefTag.add(tag);
|
|
tagValueCondDefValue.add(value);
|
|
tagValueCondDefNot.add(not);
|
|
if(value == null) {
|
|
if (not) {
|
|
onlyNotTags.add(tag);
|
|
} else {
|
|
onlyTags.add(tag);
|
|
}
|
|
} else {
|
|
int vtype = registerTagValueAttribute(tag, value);
|
|
if(not) {
|
|
filterNotTypes.set(vtype);
|
|
} else {
|
|
filterTypes.set(vtype);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void registerLessCondition(String value1, String value2, String valueType) {
|
|
expressions.add(new RouteAttributeExpression(new String[] { value1, value2 }, valueType,
|
|
RouteAttributeExpression.LESS_EXPRESSION));
|
|
}
|
|
|
|
public void registerGreatCondition(String value1, String value2, String valueType) {
|
|
expressions.add(new RouteAttributeExpression(new String[] { value1, value2 }, valueType,
|
|
RouteAttributeExpression.GREAT_EXPRESSION));
|
|
}
|
|
|
|
public void registerAndParamCondition(String param, boolean not) {
|
|
param = not ? "-" + param : param;
|
|
parameters.add(param);
|
|
}
|
|
|
|
public synchronized Object eval(BitSet types, ParameterContext paramContext) {
|
|
if (matches(types, paramContext)) {
|
|
return calcSelectValue(types, paramContext);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
protected Object calcSelectValue(BitSet types, ParameterContext paramContext) {
|
|
if (selectValue instanceof String && selectValue.toString().startsWith("$")) {
|
|
BitSet mask = tagRuleMask.get(selectValue.toString().substring(1));
|
|
if (mask != null && mask.intersects(types)) {
|
|
BitSet findBit = new BitSet(mask.size());
|
|
findBit.or(mask);
|
|
findBit.and(types);
|
|
int value = findBit.nextSetBit(0);
|
|
return parseValueFromTag(value, selectType);
|
|
}
|
|
} else if (selectValue instanceof String && selectValue.toString().startsWith(":")) {
|
|
String p = ((String) selectValue).substring(1);
|
|
if (paramContext != null && paramContext.vars.containsKey(p)) {
|
|
selectValue = parseValue(paramContext.vars.get(p), selectType);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
return selectValue;
|
|
}
|
|
|
|
public boolean matches(BitSet types, ParameterContext paramContext) {
|
|
if(!checkAllTypesShouldBePresent(types)) {
|
|
return false;
|
|
}
|
|
if(!checkAllTypesShouldNotBePresent(types)) {
|
|
return false;
|
|
}
|
|
if(!checkFreeTags(types)) {
|
|
return false;
|
|
}
|
|
if(!checkNotFreeTags(types)) {
|
|
return false;
|
|
}
|
|
if(!checkExpressions(types, paramContext)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private boolean checkExpressions(BitSet types, ParameterContext paramContext) {
|
|
for(RouteAttributeExpression e : expressions){
|
|
if(!e.matches(types, paramContext)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private boolean checkFreeTags(BitSet types) {
|
|
for (String ts : onlyTags) {
|
|
BitSet b = tagRuleMask.get(ts);
|
|
if (b == null || !b.intersects(types)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private boolean checkNotFreeTags(BitSet types) {
|
|
for (String ts : onlyNotTags) {
|
|
BitSet b = tagRuleMask.get(ts);
|
|
if (b != null && b.intersects(types)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private boolean checkAllTypesShouldNotBePresent(BitSet types) {
|
|
if(filterNotTypes.intersects(types)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private boolean checkAllTypesShouldBePresent(BitSet types) {
|
|
// Bitset method subset is missing "filterTypes.isSubset(types)"
|
|
// reset previous evaluation
|
|
// evalFilterTypes.clear(); // not needed same as or()
|
|
evalFilterTypes.or(filterTypes);
|
|
// evaluate bit intersection and check if filterTypes contained as set in types
|
|
evalFilterTypes.and(types);
|
|
if(!evalFilterTypes.equals(filterTypes)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
public void printRules(PrintStream out) {
|
|
for(int i = 0; i < RouteDataObjectAttribute.values().length ; i++) {
|
|
out.println(RouteDataObjectAttribute.values()[i]);
|
|
objectAttributes[i].printRules(out);
|
|
}
|
|
|
|
}
|
|
|
|
public void addImpassableRoads(Set<Long> impassableRoads) {
|
|
if (impassableRoads != null && !impassableRoads.isEmpty()) {
|
|
if (this.impassableRoads == null) {
|
|
this.impassableRoads = new TLongHashSet();
|
|
}
|
|
this.impassableRoads.addAll(impassableRoads);
|
|
}
|
|
}
|
|
}
|
|
|