package net.osmand.render; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Stack; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import net.osmand.LogUtil; import org.apache.commons.logging.Log; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public class OsmandRenderingRules { private final static Log log = LogUtil.getLog(OsmandRenderingRules.class); public static class EffectAttributes { public int color = 0; public float strokeWidth = 0; public String pathEffect = null; public int shadowColor = 0; public float shadowRadius = 0; public String cap = null; } public static class TextAttributes { public int textColor = 0; public float textSize = 0; public boolean textBold = false; public String textShield = null; public int textMinDistance = 0; public boolean textOnPath = false; public int textWrapWidth = 0; public float textHaloRadius = 0; public int textDy = 0; public String ref = null; } protected static class SwitchState { List filters = new ArrayList(); } public static class FilterState { public int minzoom = -1; public int maxzoom = -1; public String tag = null; public String val = null; public int layer = 0; public int textLength = 0; public String shader = null; // point public String icon = null; public EffectAttributes main = new EffectAttributes(); public TextAttributes text = null; public List effectAttributes = new ArrayList(3); protected EffectAttributes getEffectAttributes(int i){ while(i >= effectAttributes.size()){ effectAttributes.add(new EffectAttributes()); } return effectAttributes.get(i); } } private final static int POINT_STATE = 1; private final static int POLYGON_STATE = 2; private final static int LINE_STATE = 3; private final static int TEXT_STATE = 4; public void parseRenderingRules(InputStream is) throws IOException, SAXException { try { final SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); saxParser.parse(is, new RenderingRulesHandler(saxParser)); } catch (ParserConfigurationException e) { throw new SAXException(e); } } private class RenderingRulesHandler extends DefaultHandler { private final SAXParser parser; private int state; Stack stack = new Stack(); public RenderingRulesHandler(SAXParser parser){ this.parser = parser; } @Override public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { name = parser.isNamespaceAware() ? localName : name; if("filter".equals(name)){ //$NON-NLS-1$ FilterState st = parseFilterAttributes(attributes); stack.push(st); } else if("text".equals(name)){ //$NON-NLS-1$ state = TEXT_STATE; } else if("point".equals(name)){ //$NON-NLS-1$ state = POINT_STATE; } else if("line".equals(name)){ //$NON-NLS-1$ state = LINE_STATE; } else if("polygon".equals(name)){ //$NON-NLS-1$ state = POLYGON_STATE; } else if("switch".equals(name)){ //$NON-NLS-1$ SwitchState st = new SwitchState(); stack.push(st); } else if("case".equals(name)){ //$NON-NLS-1$ FilterState st = parseFilterAttributes(attributes); ((SwitchState)stack.peek()).filters.add(st); } else { // System.err.println("Unknown tag " + name); } } @Override public void endElement(String uri, String localName, String name) throws SAXException { name = parser.isNamespaceAware() ? localName : name; if ("filter".equals(name)) { //$NON-NLS-1$ List list = popAndAggregateState(); for (FilterState pop : list) { if (pop.tag != null && pop.minzoom != -1) { String gen = generateAttributes(pop); if (gen != null) { String res = ""; if (pop.maxzoom != -1) { res += " zoom : " +pop.minzoom + "-" + pop.maxzoom; } else { res += " zoom : " +pop.minzoom; } res += " tag="+pop.tag; res += " val="+pop.val; if(pop.layer != 0){ res += " layer="+pop.layer; } res += gen; System.out.println(res); } } } } else if("switch".equals(name)){ stack.pop(); } } private String generateAttributes(FilterState s){ String res = ""; if(s.shader != null){ res+=" shader=" + s.shader; } if(s.main.color != 0){ res +=" color="+colorToString(s.main.color); } if(s.icon != null){ res+= " icon="+s.icon; } if(s.main.strokeWidth != 0){ res+= " strokeWidth="+s.main.strokeWidth; } if(s.main.pathEffect != null){ res+= " pathEffect="+s.main.pathEffect; } if(state == POLYGON_STATE){ return null; // if(s.shader != null){ // return " shader=" + s.shader; // } // return " color=" + colorToString(s.main.color); // } else if(state == POINT_STATE){ // return " icon=" + s.icon; } else if(state == LINE_STATE){ return res; } else { return null; } } public List popAndAggregateState() { FilterState pop = (FilterState) stack.pop(); List res = null; for (int i = stack.size() - 1; i >= 0; i--) { Object o = stack.get(i); if(o instanceof FilterState){ if(res == null){ mergeStateInto((FilterState) o, pop); } else { for(FilterState f : res){ mergeStateInto((FilterState) o, f); } } } else { List filters = ((SwitchState)o).filters; if(res == null){ res =new ArrayList(); res.add(pop); } int l = res.size(); for (int t = 0; t < filters.size() - 1; t++) { for (int j = 0; j < l; j++) { FilterState n = new FilterState(); mergeStateInto(res.get(j), n); res.add(n); } } for (int j = 0; j < res.size(); j++) { mergeStateInto(filters.get(j % filters.size()), res.get(j)); } } } if(res == null){ return Collections.singletonList(pop); } else { return res; } } public void mergeStateInto(FilterState toMerge, FilterState mergeInto){ if(toMerge.maxzoom != -1 && mergeInto.maxzoom == -1){ mergeInto.maxzoom = toMerge.maxzoom; } if(toMerge.minzoom != -1 && mergeInto.minzoom == -1){ mergeInto.minzoom = toMerge.minzoom; } if(toMerge.icon != null && mergeInto.icon == null){ mergeInto.icon = toMerge.icon; } if(toMerge.tag != null && mergeInto.tag == null){ mergeInto.tag = toMerge.tag; } if(toMerge.layer != 0 && mergeInto.layer == 0){ mergeInto.layer = toMerge.layer; } if(toMerge.textLength != 0 && mergeInto.textLength == 0){ mergeInto.textLength = toMerge.textLength; } if(toMerge.val != null && mergeInto.val == null){ mergeInto.val = toMerge.val; } if(toMerge.text != null){ if(mergeInto.text == null){ mergeInto.text = new TextAttributes(); } if(toMerge.text.textColor != 0 && mergeInto.text.textColor == 0){ mergeInto.text.textColor = toMerge.text.textColor; } if(toMerge.text.textSize != 0 && mergeInto.text.textSize == 0){ mergeInto.text.textSize = toMerge.text.textSize; } if(toMerge.text.textBold && !mergeInto.text.textBold){ mergeInto.text.textBold = toMerge.text.textBold; } if(toMerge.text.textShield != null && mergeInto.text.textShield == null){ mergeInto.text.textShield = toMerge.text.textShield; } if(toMerge.text.textMinDistance != 0 && mergeInto.text.textMinDistance == 0){ mergeInto.text.textMinDistance = toMerge.text.textMinDistance; } if(toMerge.text.textDy != 0 && mergeInto.text.textDy == 0){ mergeInto.text.textDy = toMerge.text.textDy; } if(toMerge.text.textHaloRadius != 0 && mergeInto.text.textHaloRadius == 0){ mergeInto.text.textHaloRadius = toMerge.text.textHaloRadius; } if(toMerge.text.textWrapWidth != 0 && mergeInto.text.textWrapWidth == 0){ mergeInto.text.textWrapWidth = toMerge.text.textWrapWidth; } if(toMerge.text.textOnPath && !mergeInto.text.textOnPath){ mergeInto.text.textOnPath = toMerge.text.textOnPath; } } mergeStateInto(toMerge.main, mergeInto.main); while(mergeInto.effectAttributes.size() < toMerge.effectAttributes.size()){ mergeInto.effectAttributes.add(new EffectAttributes()); } for(int i=0; i