001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.mappaint.xml; 003 004import java.awt.Color; 005import java.util.Arrays; 006import java.util.Collection; 007import java.util.LinkedList; 008 009import org.openstreetmap.josm.Main; 010import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference; 011import org.openstreetmap.josm.gui.mappaint.Range; 012import org.openstreetmap.josm.tools.ColorHelper; 013import org.xml.sax.Attributes; 014import org.xml.sax.helpers.DefaultHandler; 015 016public class XmlStyleSourceHandler extends DefaultHandler { 017 private boolean inDoc, inRule, inCondition, inLine, inLineMod, inIcon, inArea, inScaleMax, inScaleMin; 018 private boolean hadLine, hadLineMod, hadIcon, hadArea; 019 private RuleElem rule = new RuleElem(); 020 021 private XmlStyleSource style; 022 023 static class RuleElem { 024 private XmlCondition cond = new XmlCondition(); 025 private Collection<XmlCondition> conditions; 026 private double scaleMax; 027 private double scaleMin; 028 private LinePrototype line = new LinePrototype(); 029 private LinemodPrototype linemod = new LinemodPrototype(); 030 private AreaPrototype area = new AreaPrototype(); 031 private IconPrototype icon = new IconPrototype(); 032 public void init() { 033 conditions = null; 034 scaleMax = Double.POSITIVE_INFINITY; 035 scaleMin = 0; 036 line.init(); 037 cond.init(); 038 linemod.init(); 039 area.init(); 040 icon.init(); 041 } 042 } 043 044 public XmlStyleSourceHandler(XmlStyleSource style) { 045 this.style = style; 046 inDoc = inRule = inCondition = inLine = inIcon = inArea = false; 047 rule.init(); 048 } 049 050 private Color convertColor(String colString) { 051 int i = colString.indexOf('#'); 052 Color ret; 053 if (i < 0) { 054 ret = Main.pref.getColor("mappaint."+style.getPrefName()+'.'+colString, Color.red); 055 } else if (i == 0) { 056 ret = ColorHelper.html2color(colString); 057 } else { 058 ret = Main.pref.getColor("mappaint."+style.getPrefName()+'.'+colString.substring(0, i), 059 ColorHelper.html2color(colString.substring(i))); 060 } 061 return ret; 062 } 063 064 @Override 065 public void startDocument() { 066 inDoc = true; 067 } 068 069 @Override 070 public void endDocument() { 071 inDoc = false; 072 } 073 074 private void error(String message) { 075 String warning = style.getDisplayString() + " (" + rule.cond.key + '=' + rule.cond.value + "): " + message; 076 Main.warn(warning); 077 style.logError(new Exception(warning)); 078 } 079 080 private void startElementLine(String qName, Attributes atts, LinePrototype line) { 081 for (int count = 0; count < atts.getLength(); count++) { 082 switch (atts.getQName(count)) { 083 case "width": 084 String val = atts.getValue(count); 085 if (!(val.startsWith("+") || val.startsWith("-") || val.endsWith("%"))) { 086 line.setWidth(Integer.parseInt(val)); 087 } 088 break; 089 case "colour": 090 line.color = convertColor(atts.getValue(count)); 091 break; 092 case "realwidth": 093 line.realWidth = Integer.valueOf(atts.getValue(count)); 094 break; 095 case "dashed": 096 Float[] dashed; 097 try { 098 String[] parts = atts.getValue(count).split(","); 099 dashed = new Float[parts.length]; 100 for (int i = 0; i < parts.length; i++) { 101 dashed[i] = (float) Integer.parseInt(parts[i]); 102 } 103 } catch (NumberFormatException nfe) { 104 boolean isDashed = Boolean.parseBoolean(atts.getValue(count)); 105 if (isDashed) { 106 dashed = new Float[]{9f}; 107 } else { 108 dashed = null; 109 } 110 } 111 line.setDashed(dashed == null ? null : Arrays.asList(dashed)); 112 break; 113 case "dashedcolour": 114 line.dashedColor = convertColor(atts.getValue(count)); 115 break; 116 case "priority": 117 line.priority = Integer.parseInt(atts.getValue(count)); 118 break; 119 case "mode": 120 if (line instanceof LinemodPrototype) 121 break; 122 default: 123 error("The element \"" + qName + "\" has unknown attribute \"" + atts.getQName(count) + "\"!"); 124 } 125 } 126 } 127 128 private void startElementLinemod(String qName, Attributes atts, LinemodPrototype line) { 129 startElementLine(qName, atts, line); 130 for (int count = 0; count < atts.getLength(); count++) { 131 switch (atts.getQName(count)) { 132 case "width": 133 String val = atts.getValue(count); 134 if (val.startsWith("+")) { 135 line.setWidth(Integer.parseInt(val.substring(1))); 136 line.widthMode = LinemodPrototype.WidthMode.OFFSET; 137 } else if (val.startsWith("-")) { 138 line.setWidth(Integer.parseInt(val)); 139 line.widthMode = LinemodPrototype.WidthMode.OFFSET; 140 } else if (val.endsWith("%")) { 141 line.setWidth(Integer.parseInt(val.substring(0, val.length()-1))); 142 line.widthMode = LinemodPrototype.WidthMode.PERCENT; 143 } else { 144 line.setWidth(Integer.parseInt(val)); 145 } 146 break; 147 case "mode": 148 line.over = !"under".equals(atts.getValue(count)); 149 break; 150 } 151 } 152 } 153 154 @Override 155 public void startElement(String uri, String name, String qName, Attributes atts) { 156 if (inDoc) { 157 switch(qName) { 158 case "rule": 159 inRule = true; 160 break; 161 case "rules": 162 if (style.name == null) { 163 style.name = atts.getValue("name"); 164 } 165 if (style.title == null) { 166 style.title = atts.getValue("shortdescription"); 167 } 168 if (style.icon == null) { 169 style.icon = atts.getValue("icon"); 170 } 171 break; 172 case "scale_max": 173 inScaleMax = true; 174 break; 175 case "scale_min": 176 inScaleMin = true; 177 break; 178 case "condition": 179 if (inRule) { 180 inCondition = true; 181 XmlCondition c = rule.cond; 182 if (c.key != null) { 183 if (rule.conditions == null) { 184 rule.conditions = new LinkedList<>(); 185 } 186 rule.conditions.add(new XmlCondition(rule.cond)); 187 c = new XmlCondition(); 188 rule.conditions.add(c); 189 } 190 for (int count = 0; count < atts.getLength(); count++) { 191 switch (atts.getQName(count)) { 192 case "k": 193 c.key = atts.getValue(count); 194 break; 195 case "v": 196 c.value = atts.getValue(count); 197 break; 198 case "b": 199 c.boolValue = atts.getValue(count); 200 break; 201 default: 202 error("The element \"" + qName + "\" has unknown attribute \"" + atts.getQName(count) + "\"!"); 203 } 204 } 205 if (c.key == null) { 206 error("The condition has no key!"); 207 } 208 } 209 break; 210 case "line": 211 hadLine = inLine = true; 212 startElementLine(qName, atts, rule.line); 213 break; 214 case "linemod": 215 hadLineMod = inLineMod = true; 216 startElementLinemod(qName, atts, rule.linemod); 217 break; 218 case "icon": 219 inIcon = true; 220 for (int count = 0; count < atts.getLength(); count++) { 221 switch (atts.getQName(count)) { 222 case "src": 223 IconReference icon = new IconReference(atts.getValue(count), style); 224 hadIcon = (icon != null); 225 rule.icon.icon = icon; 226 break; 227 case "annotate": 228 rule.icon.annotate = Boolean.valueOf(atts.getValue(count)); 229 break; 230 case "priority": 231 rule.icon.priority = Integer.parseInt(atts.getValue(count)); 232 break; 233 default: 234 error("The element \"" + qName + "\" has unknown attribute \"" + atts.getQName(count) + "\"!"); 235 } 236 } 237 break; 238 case "area": 239 hadArea = inArea = true; 240 for (int count = 0; count < atts.getLength(); count++) { 241 switch (atts.getQName(count)) { 242 case "colour": 243 rule.area.color = convertColor(atts.getValue(count)); 244 break; 245 case "closed": 246 rule.area.closed = Boolean.parseBoolean(atts.getValue(count)); 247 break; 248 case "priority": 249 rule.area.priority = Integer.parseInt(atts.getValue(count)); 250 break; 251 default: 252 error("The element \"" + qName + "\" has unknown attribute \"" + atts.getQName(count) + "\"!"); 253 } 254 } 255 break; 256 default: 257 error("The element \"" + qName + "\" is unknown!"); 258 } 259 } 260 } 261 262 @Override 263 public void endElement(String uri, String name, String qName) { 264 if (inRule && "rule".equals(qName)) { 265 if (hadLine) { 266 style.add(rule.cond, rule.conditions, 267 new LinePrototype(rule.line, new Range(rule.scaleMin, rule.scaleMax))); 268 } 269 if (hadLineMod) { 270 style.add(rule.cond, rule.conditions, 271 new LinemodPrototype(rule.linemod, new Range(rule.scaleMin, rule.scaleMax))); 272 } 273 if (hadIcon) { 274 style.add(rule.cond, rule.conditions, 275 new IconPrototype(rule.icon, new Range(rule.scaleMin, rule.scaleMax))); 276 } 277 if (hadArea) { 278 style.add(rule.cond, rule.conditions, 279 new AreaPrototype(rule.area, new Range(rule.scaleMin, rule.scaleMax))); 280 } 281 inRule = false; 282 hadLine = hadLineMod = hadIcon = hadArea = false; 283 rule.init(); 284 } else if (inCondition && "condition".equals(qName)) { 285 inCondition = false; 286 } else if (inLine && "line".equals(qName)) { 287 inLine = false; 288 } else if (inLineMod && "linemod".equals(qName)) { 289 inLineMod = false; 290 } else if (inIcon && "icon".equals(qName)) { 291 inIcon = false; 292 } else if (inArea && "area".equals(qName)) { 293 inArea = false; 294 } else if ("scale_max".equals(qName)) { 295 inScaleMax = false; 296 } else if ("scale_min".equals(qName)) { 297 inScaleMin = false; 298 } 299 } 300 301 @Override 302 public void characters(char[] ch, int start, int length) { 303 if (inScaleMax) { 304 rule.scaleMax = Long.parseLong(new String(ch, start, length)); 305 } else if (inScaleMin) { 306 rule.scaleMin = Long.parseLong(new String(ch, start, length)); 307 } 308 } 309}