// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.gui.mappaint.xml;
import java.awt.Color;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
import org.openstreetmap.josm.gui.mappaint.Range;
import org.openstreetmap.josm.tools.ColorHelper;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
public class XmlStyleSourceHandler extends DefaultHandler {
private boolean inDoc, inRule, inCondition, inLine, inLineMod, inIcon, inArea, inScaleMax, inScaleMin;
private boolean hadLine, hadLineMod, hadIcon, hadArea;
private RuleElem rule = new RuleElem();
XmlStyleSource style;
static class RuleElem {
XmlCondition cond = new XmlCondition();
Collection<XmlCondition> conditions;
double scaleMax;
double scaleMin;
LinePrototype line = new LinePrototype();
LinemodPrototype linemod = new LinemodPrototype();
AreaPrototype area = new AreaPrototype();
IconPrototype icon = new IconPrototype();
public void init() {
conditions = null;
scaleMax = Double.POSITIVE_INFINITY;
scaleMin = 0;
line.init();
cond.init();
linemod.init();
area.init();
icon.init();
}
}
public XmlStyleSourceHandler(XmlStyleSource style) {
this.style = style;
inDoc=inRule=inCondition=inLine=inIcon=inArea=false;
rule.init();
}
Color convertColor(String colString) {
int i = colString.indexOf('#');
Color ret;
if (i < 0) {
ret = Main.pref.getColor("mappaint."+style.getPrefName()+"."+colString, Color.red);
} else if(i == 0) {
ret = ColorHelper.html2color(colString);
} else {
ret = Main.pref.getColor("mappaint."+style.getPrefName()+"."+colString.substring(0,i),
ColorHelper.html2color(colString.substring(i)));
}
return ret;
}
@Override public void startDocument() {
inDoc = true;
}
@Override public void endDocument() {
inDoc = false;
}
private void error(String message) {
String warning = style.getDisplayString() + " (" + rule.cond.key + "=" + rule.cond.value + "): " + message;
Main.warn(warning);
style.logError(new Exception(warning));
}
private void startElementLine(String qName, Attributes atts, LinePrototype line) {
for (int count=0; count<atts.getLength(); count++) {
switch (atts.getQName(count)) {
case "width":
String val = atts.getValue(count);
if (! (val.startsWith("+") || val.startsWith("-") || val.endsWith("%"))) {
line.setWidth(Integer.parseInt(val));
}
break;
case "colour":
line.color = convertColor(atts.getValue(count));
break;
case "realwidth":
line.realWidth = Integer.parseInt(atts.getValue(count));
break;
case "dashed":
Float[] dashed;
try {
String[] parts = atts.getValue(count).split(",");
dashed = new Float[parts.length];
for (int i = 0; i < parts.length; i++) {
dashed[i] = (float) Integer.parseInt(parts[i]);
}
} catch (NumberFormatException nfe) {
boolean isDashed = Boolean.parseBoolean(atts.getValue(count));
if(isDashed) {
dashed = new Float[]{9f};
} else {
dashed = null;
}
}
line.setDashed(dashed == null ? null : Arrays.asList(dashed));
break;
case "dashedcolour":
line.dashedColor = convertColor(atts.getValue(count));
break;
case "priority":
line.priority = Integer.parseInt(atts.getValue(count));
break;
case "mode":
if (line instanceof LinemodPrototype)
break;
default:
error("The element \"" + qName + "\" has unknown attribute \"" + atts.getQName(count) + "\"!");
}
}
}
private void startElementLinemod(String qName, Attributes atts, LinemodPrototype line) {
startElementLine(qName, atts, line);
for (int count=0; count<atts.getLength(); count++) {
switch (atts.getQName(count)) {
case "width":
String val = atts.getValue(count);
if (val.startsWith("+")) {
line.setWidth(Integer.parseInt(val.substring(1)));
line.widthMode = LinemodPrototype.WidthMode.OFFSET;
} else if(val.startsWith("-")) {
line.setWidth(Integer.parseInt(val));
line.widthMode = LinemodPrototype.WidthMode.OFFSET;
} else if(val.endsWith("%")) {
line.setWidth(Integer.parseInt(val.substring(0, val.length()-1)));
line.widthMode = LinemodPrototype.WidthMode.PERCENT;
} else {
line.setWidth(Integer.parseInt(val));
}
break;
case "mode":
line.over = !"under".equals(atts.getValue(count));
break;
}
}
}
@Override
public void startElement(String uri,String name, String qName, Attributes atts) {
if (inDoc) {
switch(qName) {
case "rule":
inRule = true;
break;
case "rules":
if (style.name == null) {
style.name = atts.getValue("name");
}
if (style.title == null) {
style.title = atts.getValue("shortdescription");
}
if (style.icon == null) {
style.icon = atts.getValue("icon");
}
break;
case "scale_max":
inScaleMax = true;
break;
case "scale_min":
inScaleMin = true;
break;
case "condition":
if (inRule) {
inCondition = true;
XmlCondition c = rule.cond;
if (c.key != null) {
if(rule.conditions == null) {
rule.conditions = new LinkedList<>();
}
rule.conditions.add(new XmlCondition(rule.cond));
c = new XmlCondition();
rule.conditions.add(c);
}
for (int count=0; count<atts.getLength(); count++) {
switch (atts.getQName(count)) {
case "k":
c.key = atts.getValue(count);
break;
case "v":
c.value = atts.getValue(count);
break;
case "b":
c.boolValue = atts.getValue(count);
break;
default:
error("The element \"" + qName + "\" has unknown attribute \"" + atts.getQName(count) + "\"!");
}
}
if(c.key == null) {
error("The condition has no key!");
}
}
break;
case "line":
hadLine = inLine = true;
startElementLine(qName, atts, rule.line);
break;
case "linemod":
hadLineMod = inLineMod = true;
startElementLinemod(qName, atts, rule.linemod);
break;
case "icon":
inIcon = true;
for (int count=0; count<atts.getLength(); count++) {
switch (atts.getQName(count)) {
case "src":
IconReference icon = new IconReference(atts.getValue(count), style);
hadIcon = (icon != null);
rule.icon.icon = icon;
break;
case "annotate":
rule.icon.annotate = Boolean.parseBoolean (atts.getValue(count));
break;
case "priority":
rule.icon.priority = Integer.parseInt(atts.getValue(count));
break;
default:
error("The element \"" + qName + "\" has unknown attribute \"" + atts.getQName(count) + "\"!");
}
}
break;
case "area":
hadArea = inArea = true;
for (int count=0; count<atts.getLength(); count++) {
switch (atts.getQName(count)) {
case "colour":
rule.area.color=convertColor(atts.getValue(count));
break;
case "closed":
rule.area.closed=Boolean.parseBoolean(atts.getValue(count));
break;
case "priority":
rule.area.priority = Integer.parseInt(atts.getValue(count));
break;
default:
error("The element \"" + qName + "\" has unknown attribute \"" + atts.getQName(count) + "\"!");
}
}
break;
default:
error("The element \"" + qName + "\" is unknown!");
}
}
}
@Override
public void endElement(String uri,String name, String qName) {
if (inRule && "rule".equals(qName)) {
if (hadLine) {
style.add(rule.cond, rule.conditions,
new LinePrototype(rule.line, new Range(rule.scaleMin, rule.scaleMax)));
}
if (hadLineMod) {
style.add(rule.cond, rule.conditions,
new LinemodPrototype(rule.linemod, new Range(rule.scaleMin, rule.scaleMax)));
}
if (hadIcon) {
style.add(rule.cond, rule.conditions,
new IconPrototype(rule.icon, new Range(rule.scaleMin, rule.scaleMax)));
}
if (hadArea) {
style.add(rule.cond, rule.conditions,
new AreaPrototype(rule.area, new Range(rule.scaleMin, rule.scaleMax)));
}
inRule = false;
hadLine = hadLineMod = hadIcon = hadArea = false;
rule.init();
} else if (inCondition && "condition".equals(qName)) {
inCondition = false;
} else if (inLine && "line".equals(qName)) {
inLine = false;
} else if (inLineMod && "linemod".equals(qName)) {
inLineMod = false;
} else if (inIcon && "icon".equals(qName)) {
inIcon = false;
} else if (inArea && "area".equals(qName)) {
inArea = false;
} else if ("scale_max".equals(qName)) {
inScaleMax = false;
} else if ("scale_min".equals(qName)) {
inScaleMin = false;
}
}
@Override
public void characters(char[] ch, int start, int length) {
if (inScaleMax) {
rule.scaleMax = Long.parseLong(new String(ch, start, length));
} else if (inScaleMin) {
rule.scaleMin = Long.parseLong(new String(ch, start, length));
}
}
}