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