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