source: josm/trunk/src/com/kitfox/svg/xml/XMLParseUtil.java@ 6002

Last change on this file since 6002 was 6002, checked in by Don-vip, 11 years ago

fix #8742 - update svgsalamander to release 0.1.18+patch (fix bug SVGSALAMANDER-26) -> allow to open more SVG files

File size: 24.0 KB
Line 
1/*
2 * SVG Salamander
3 * Copyright (c) 2004, Mark McKay
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
9 *
10 * - Redistributions of source code must retain the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials
16 * provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
32 * projects can be found at http://www.kitfox.com
33 *
34 * Created on February 18, 2004, 1:49 PM
35 */
36
37package com.kitfox.svg.xml;
38
39import com.kitfox.svg.SVGConst;
40import org.w3c.dom.*;
41import java.awt.*;
42import java.net.*;
43import java.util.*;
44import java.util.regex.*;
45import java.lang.reflect.*;
46import java.util.logging.Level;
47import java.util.logging.Logger;
48
49/**
50 * @author Mark McKay
51 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
52 */
53public class XMLParseUtil
54{
55 static final Matcher fpMatch = Pattern.compile("([-+]?((\\d*\\.\\d+)|(\\d+))([eE][+-]?\\d+)?)(\\%|in|cm|mm|pt|pc|px|em|ex)?").matcher("");
56 static final Matcher intMatch = Pattern.compile("[-+]?\\d+").matcher("");
57
58 /** Creates a new instance of XMLParseUtil */
59 private XMLParseUtil()
60 {
61 }
62
63 /**
64 * Scans the tag's children and returns the first text element found
65 */
66 public static String getTagText(Element ele)
67 {
68 NodeList nl = ele.getChildNodes();
69 int size = nl.getLength();
70
71 Node node = null;
72 int i = 0;
73 for (; i < size; i++)
74 {
75 node = nl.item(i);
76 if (node instanceof Text) break;
77 }
78 if (i == size || node == null) return null;
79
80 return ((Text)node).getData();
81 }
82
83 /**
84 * Returns the first node that is a direct child of root with the coresponding
85 * name. Does not search children of children.
86 */
87 public static Element getFirstChild(Element root, String name)
88 {
89 NodeList nl = root.getChildNodes();
90 int size = nl.getLength();
91 for (int i = 0; i < size; i++)
92 {
93 Node node = nl.item(i);
94 if (!(node instanceof Element)) continue;
95 Element ele = (Element)node;
96 if (ele.getTagName().equals(name)) return ele;
97 }
98
99 return null;
100 }
101
102 public static String[] parseStringList(String list)
103 {
104// final Pattern patWs = Pattern.compile("\\s+");
105 final Matcher matchWs = Pattern.compile("[^\\s]+").matcher("");
106 matchWs.reset(list);
107
108 LinkedList matchList = new LinkedList();
109 while (matchWs.find())
110 {
111 matchList.add(matchWs.group());
112 }
113
114 String[] retArr = new String[matchList.size()];
115 return (String[])matchList.toArray(retArr);
116 }
117
118 public static boolean isDouble(String val)
119 {
120 fpMatch.reset(val);
121 return fpMatch.matches();
122 }
123
124 public static double parseDouble(String val)
125 {
126 /*
127 if (val == null) return 0.0;
128
129 double retVal = 0.0;
130 try
131 { retVal = Double.parseDouble(val); }
132 catch (Exception e)
133 {}
134 return retVal;
135 */
136 return findDouble(val);
137 }
138
139 /**
140 * Searches the given string for the first floating point number it contains,
141 * parses and returns it.
142 */
143 public synchronized static double findDouble(String val)
144 {
145 if (val == null) return 0;
146
147 fpMatch.reset(val);
148 try
149 {
150 if (!fpMatch.find()) return 0;
151 }
152 catch (StringIndexOutOfBoundsException e)
153 {
154 Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
155 "XMLParseUtil: regex parse problem: '" + val + "'", e);
156 }
157
158 val = fpMatch.group(1);
159 //System.err.println("Parsing " + val);
160
161 double retVal = 0;
162 try
163 {
164 retVal = Double.parseDouble(val);
165
166 float pixPerInch;
167 try {
168 pixPerInch = (float)Toolkit.getDefaultToolkit().getScreenResolution();
169 }
170 catch (NoClassDefFoundError err)
171 {
172 //Default value for headless X servers
173 pixPerInch = 72;
174 }
175 final float inchesPerCm = .3936f;
176 final String units = fpMatch.group(6);
177
178 if ("%".equals(units)) retVal /= 100;
179 else if ("in".equals(units))
180 {
181 retVal *= pixPerInch;
182 }
183 else if ("cm".equals(units))
184 {
185 retVal *= inchesPerCm * pixPerInch;
186 }
187 else if ("mm".equals(units))
188 {
189 retVal *= inchesPerCm * pixPerInch * .1f;
190 }
191 else if ("pt".equals(units))
192 {
193 retVal *= (1f / 72f) * pixPerInch;
194 }
195 else if ("pc".equals(units))
196 {
197 retVal *= (1f / 6f) * pixPerInch;
198 }
199 }
200 catch (Exception e)
201 {}
202 return retVal;
203 }
204
205 /**
206 * Scans an input string for double values. For each value found, places
207 * in a list. This method regards any characters not part of a floating
208 * point value to be seperators. Thus this will parse whitespace seperated,
209 * comma seperated, and many other separation schemes correctly.
210 */
211 public synchronized static double[] parseDoubleList(String list)
212 {
213 if (list == null) return null;
214
215 fpMatch.reset(list);
216
217 LinkedList doubList = new LinkedList();
218 while (fpMatch.find())
219 {
220 String val = fpMatch.group(1);
221 doubList.add(Double.valueOf(val));
222 }
223
224 double[] retArr = new double[doubList.size()];
225 Iterator it = doubList.iterator();
226 int idx = 0;
227 while (it.hasNext())
228 {
229 retArr[idx++] = ((Double)it.next()).doubleValue();
230 }
231
232 return retArr;
233 }
234
235 public static float parseFloat(String val)
236 {
237 /*
238 if (val == null) return 0f;
239
240 float retVal = 0f;
241 try
242 { retVal = Float.parseFloat(val); }
243 catch (Exception e)
244 {}
245 return retVal;
246 */
247 return findFloat(val);
248 }
249
250 /**
251 * Searches the given string for the first floating point number it contains,
252 * parses and returns it.
253 */
254 public synchronized static float findFloat(String val)
255 {
256 if (val == null) return 0f;
257
258 fpMatch.reset(val);
259 if (!fpMatch.find()) return 0f;
260
261 val = fpMatch.group(1);
262 //System.err.println("Parsing " + val);
263
264 float retVal = 0f;
265 try
266 {
267 retVal = Float.parseFloat(val);
268 String units = fpMatch.group(6);
269 if ("%".equals(units)) retVal /= 100;
270 }
271 catch (Exception e)
272 {}
273 return retVal;
274 }
275
276 public synchronized static float[] parseFloatList(String list)
277 {
278 if (list == null) return null;
279
280 fpMatch.reset(list);
281
282 LinkedList floatList = new LinkedList();
283 while (fpMatch.find())
284 {
285 String val = fpMatch.group(1);
286 floatList.add(Float.valueOf(val));
287 }
288
289 float[] retArr = new float[floatList.size()];
290 Iterator it = floatList.iterator();
291 int idx = 0;
292 while (it.hasNext())
293 {
294 retArr[idx++] = ((Float)it.next()).floatValue();
295 }
296
297 return retArr;
298 }
299
300 public static int parseInt(String val)
301 {
302 if (val == null) return 0;
303
304 int retVal = 0;
305 try
306 { retVal = Integer.parseInt(val); }
307 catch (Exception e)
308 {}
309 return retVal;
310 }
311
312 /**
313 * Searches the given string for the first integer point number it contains,
314 * parses and returns it.
315 */
316 public static int findInt(String val)
317 {
318 if (val == null) return 0;
319
320 intMatch.reset(val);
321 if (!intMatch.find()) return 0;
322
323 val = intMatch.group();
324 //System.err.println("Parsing " + val);
325
326 int retVal = 0;
327 try
328 { retVal = Integer.parseInt(val); }
329 catch (Exception e)
330 {}
331 return retVal;
332 }
333
334 public static int[] parseIntList(String list)
335 {
336 if (list == null) return null;
337
338 intMatch.reset(list);
339
340 LinkedList intList = new LinkedList();
341 while (intMatch.find())
342 {
343 String val = intMatch.group();
344 intList.add(Integer.valueOf(val));
345 }
346
347 int[] retArr = new int[intList.size()];
348 Iterator it = intList.iterator();
349 int idx = 0;
350 while (it.hasNext())
351 {
352 retArr[idx++] = ((Integer)it.next()).intValue();
353 }
354
355 return retArr;
356 }
357/*
358 public static int parseHex(String val)
359 {
360 int retVal = 0;
361
362 for (int i = 0; i < val.length(); i++)
363 {
364 retVal <<= 4;
365
366 char ch = val.charAt(i);
367 if (ch >= '0' && ch <= '9')
368 {
369 retVal |= ch - '0';
370 }
371 else if (ch >= 'a' && ch <= 'z')
372 {
373 retVal |= ch - 'a' + 10;
374 }
375 else if (ch >= 'A' && ch <= 'Z')
376 {
377 retVal |= ch - 'A' + 10;
378 }
379 else throw new RuntimeException();
380 }
381
382 return retVal;
383 }
384*/
385 /**
386 * The input string represents a ratio. Can either be specified as a
387 * double number on the range of [0.0 1.0] or as a percentage [0% 100%]
388 */
389 public static double parseRatio(String val)
390 {
391 if (val == null || val.equals("")) return 0.0;
392
393 if (val.charAt(val.length() - 1) == '%')
394 {
395 parseDouble(val.substring(0, val.length() - 1));
396 }
397 return parseDouble(val);
398 }
399
400 public static NumberWithUnits parseNumberWithUnits(String val)
401 {
402 if (val == null) return null;
403
404 return new NumberWithUnits(val);
405 }
406/*
407 public static Color parseColor(String val)
408 {
409 Color retVal = null;
410
411 if (val.charAt(0) == '#')
412 {
413 String hexStrn = val.substring(1);
414
415 if (hexStrn.length() == 3)
416 {
417 hexStrn = "" + hexStrn.charAt(0) + hexStrn.charAt(0) + hexStrn.charAt(1) + hexStrn.charAt(1) + hexStrn.charAt(2) + hexStrn.charAt(2);
418 }
419 int hexVal = parseHex(hexStrn);
420
421 retVal = new Color(hexVal);
422 }
423 else
424 {
425 final Matcher rgbMatch = Pattern.compile("rgb\\((\\d+),(\\d+),(\\d+)\\)", Pattern.CASE_INSENSITIVE).matcher("");
426
427 rgbMatch.reset(val);
428 if (rgbMatch.matches())
429 {
430 int r = Integer.parseInt(rgbMatch.group(1));
431 int g = Integer.parseInt(rgbMatch.group(2));
432 int b = Integer.parseInt(rgbMatch.group(3));
433 retVal = new Color(r, g, b);
434 }
435 else
436 {
437 Color lookupCol = ColorTable.instance().lookupColor(val);
438 if (lookupCol != null) retVal = lookupCol;
439 }
440 }
441
442 return retVal;
443 }
444*/
445 /**
446 * Parses the given attribute of this tag and returns it as a String.
447 */
448 public static String getAttribString(Element ele, String name)
449 {
450 return ele.getAttribute(name);
451 }
452
453 /**
454 * Parses the given attribute of this tag and returns it as an int.
455 */
456 public static int getAttribInt(Element ele, String name)
457 {
458 String sval = ele.getAttribute(name);
459 int val = 0;
460 try { val = Integer.parseInt(sval); } catch (Exception e) {}
461
462 return val;
463 }
464
465 /**
466 * Parses the given attribute of this tag as a hexadecimal encoded string and
467 * returns it as an int
468 */
469 public static int getAttribIntHex(Element ele, String name)
470 {
471 String sval = ele.getAttribute(name);
472 int val = 0;
473 try { val = Integer.parseInt(sval, 16); } catch (Exception e) {}
474
475 return val;
476 }
477
478 /**
479 * Parses the given attribute of this tag and returns it as a float
480 */
481 public static float getAttribFloat(Element ele, String name)
482 {
483 String sval = ele.getAttribute(name);
484 float val = 0.0f;
485 try { val = Float.parseFloat(sval); } catch (Exception e) {}
486
487 return val;
488 }
489
490 /**
491 * Parses the given attribute of this tag and returns it as a double.
492 */
493 public static double getAttribDouble(Element ele, String name)
494 {
495 String sval = ele.getAttribute(name);
496 double val = 0.0;
497 try { val = Double.parseDouble(sval); } catch (Exception e) {}
498
499 return val;
500 }
501
502 /**
503 * Parses the given attribute of this tag and returns it as a boolean.
504 * Essentially compares the lower case textual value to the string "true"
505 */
506 public static boolean getAttribBoolean(Element ele, String name)
507 {
508 String sval = ele.getAttribute(name);
509
510 return sval.toLowerCase().equals("true");
511 }
512
513 public static URL getAttribURL(Element ele, String name, URL docRoot)
514 {
515 String sval = ele.getAttribute(name);
516
517 URL url;
518 try
519 {
520 return new URL(docRoot, sval);
521 }
522 catch (Exception e)
523 {
524 return null;
525 }
526 }
527
528 /**
529 * Returns the first ReadableXMLElement with the given name
530 */
531 public static ReadableXMLElement getElement(Class classType, Element root, String name, URL docRoot)
532 {
533 if (root == null) return null;
534
535 //Do not process if not a LoadableObject
536 if (!ReadableXMLElement.class.isAssignableFrom(classType))
537 {
538 return null;
539 }
540
541 NodeList nl = root.getChildNodes();
542 int size = nl.getLength();
543 for (int i = 0; i < size; i++)
544 {
545 Node node = nl.item(i);
546 if (!(node instanceof Element)) continue;
547 Element ele = (Element)node;
548 if (!ele.getTagName().equals(name)) continue;
549
550 ReadableXMLElement newObj = null;
551 try
552 {
553 newObj = (ReadableXMLElement)classType.newInstance();
554 }
555 catch (Exception e)
556 {
557 Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
558 continue;
559 }
560 newObj.read(ele, docRoot);
561
562 if (newObj == null) continue;
563
564 return newObj;
565 }
566
567 return null;
568 }
569
570 /**
571 * Returns a HashMap of nodes that are children of root. All nodes will
572 * be of class classType and have a tag name of 'name'. 'key' is
573 * an attribute of tag 'name' who's string value will be used as the key
574 * in the HashMap
575 */
576 public static HashMap getElementHashMap(Class classType, Element root, String name, String key, URL docRoot)
577 {
578 if (root == null) return null;
579
580 //Do not process if not a LoadableObject
581 if (!ReadableXMLElement.class.isAssignableFrom(classType))
582 {
583 return null;
584 }
585
586 HashMap retMap = new HashMap();
587
588 NodeList nl = root.getChildNodes();
589 int size = nl.getLength();
590 for (int i = 0; i < size; i++)
591 {
592 Node node = nl.item(i);
593 if (!(node instanceof Element)) continue;
594 Element ele = (Element)node;
595 if (!ele.getTagName().equals(name)) continue;
596
597 ReadableXMLElement newObj = null;
598 try
599 {
600 newObj = (ReadableXMLElement)classType.newInstance();
601 }
602 catch (Exception e)
603 {
604 Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
605 continue;
606 }
607 newObj.read(ele, docRoot);
608
609 if (newObj == null) continue;
610
611 String keyVal = getAttribString(ele, key);
612 retMap.put(keyVal, newObj);
613 }
614
615 return retMap;
616 }
617
618 public static HashSet getElementHashSet(Class classType, Element root, String name, URL docRoot)
619 {
620 if (root == null) return null;
621
622 //Do not process if not a LoadableObject
623 if (!ReadableXMLElement.class.isAssignableFrom(classType))
624 {
625 return null;
626 }
627
628 HashSet retSet = new HashSet();
629
630 NodeList nl = root.getChildNodes();
631 int size = nl.getLength();
632 for (int i = 0; i < size; i++)
633 {
634 Node node = nl.item(i);
635 if (!(node instanceof Element)) continue;
636 Element ele = (Element)node;
637 if (!ele.getTagName().equals(name)) continue;
638
639 ReadableXMLElement newObj = null;
640 try
641 {
642 newObj = (ReadableXMLElement)classType.newInstance();
643 }
644 catch (Exception e)
645 {
646 Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
647 continue;
648 }
649 newObj.read(ele, docRoot);
650
651 if (newObj == null)
652 {
653 continue;
654 }
655
656 retSet.add(newObj);
657 }
658
659 return retSet;
660 }
661
662
663 public static LinkedList getElementLinkedList(Class classType, Element root, String name, URL docRoot)
664 {
665 if (root == null) return null;
666
667 //Do not process if not a LoadableObject
668 if (!ReadableXMLElement.class.isAssignableFrom(classType))
669 {
670 return null;
671 }
672
673 NodeList nl = root.getChildNodes();
674 LinkedList elementCache = new LinkedList();
675 int size = nl.getLength();
676 for (int i = 0; i < size; i++)
677 {
678 Node node = nl.item(i);
679 if (!(node instanceof Element)) continue;
680 Element ele = (Element)node;
681 if (!ele.getTagName().equals(name)) continue;
682
683 ReadableXMLElement newObj = null;
684 try
685 {
686 newObj = (ReadableXMLElement)classType.newInstance();
687 }
688 catch (Exception e)
689 {
690 Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
691 continue;
692 }
693 newObj.read(ele, docRoot);
694
695 elementCache.addLast(newObj);
696 }
697
698 return elementCache;
699 }
700
701 public static Object[] getElementArray(Class classType, Element root, String name, URL docRoot)
702 {
703 if (root == null) return null;
704
705 //Do not process if not a LoadableObject
706 if (!ReadableXMLElement.class.isAssignableFrom(classType))
707 {
708 return null;
709 }
710
711 LinkedList elementCache = getElementLinkedList(classType, root, name, docRoot);
712
713 Object[] retArr = (Object[])Array.newInstance(classType, elementCache.size());
714 return elementCache.toArray(retArr);
715 }
716
717 /**
718 * Takes a number of tags of name 'name' that are children of 'root', and
719 * looks for attributes of 'attrib' on them. Converts attributes to an
720 * int and returns in an array.
721 */
722 public static int[] getElementArrayInt(Element root, String name, String attrib)
723 {
724 if (root == null) return null;
725
726 NodeList nl = root.getChildNodes();
727 LinkedList elementCache = new LinkedList();
728 int size = nl.getLength();
729
730 for (int i = 0; i < size; i++)
731 {
732 Node node = nl.item(i);
733 if (!(node instanceof Element)) continue;
734 Element ele = (Element)node;
735 if (!ele.getTagName().equals(name)) continue;
736
737 String valS = ele.getAttribute(attrib);
738 int eleVal = 0;
739 try { eleVal = Integer.parseInt(valS); }
740 catch (Exception e) {}
741
742 elementCache.addLast(new Integer(eleVal));
743 }
744
745 int[] retArr = new int[elementCache.size()];
746 Iterator it = elementCache.iterator();
747 int idx = 0;
748 while (it.hasNext())
749 {
750 retArr[idx++] = ((Integer)it.next()).intValue();
751 }
752
753 return retArr;
754 }
755
756 /**
757 * Takes a number of tags of name 'name' that are children of 'root', and
758 * looks for attributes of 'attrib' on them. Converts attributes to an
759 * int and returns in an array.
760 */
761 public static String[] getElementArrayString(Element root, String name, String attrib)
762 {
763 if (root == null) return null;
764
765 NodeList nl = root.getChildNodes();
766 LinkedList elementCache = new LinkedList();
767 int size = nl.getLength();
768
769 for (int i = 0; i < size; i++)
770 {
771 Node node = nl.item(i);
772 if (!(node instanceof Element)) continue;
773 Element ele = (Element)node;
774 if (!ele.getTagName().equals(name)) continue;
775
776 String valS = ele.getAttribute(attrib);
777
778 elementCache.addLast(valS);
779 }
780
781 String[] retArr = new String[elementCache.size()];
782 Iterator it = elementCache.iterator();
783 int idx = 0;
784 while (it.hasNext())
785 {
786 retArr[idx++] = (String)it.next();
787 }
788
789 return retArr;
790 }
791
792 /**
793 * Takes a CSS style string and retursn a hash of them.
794 * @param styleString - A CSS formatted string of styles. Eg,
795 * "font-size:12;fill:#d32c27;fill-rule:evenodd;stroke-width:1pt;"
796 */
797 public static HashMap parseStyle(String styleString) {
798 return parseStyle(styleString, new HashMap());
799 }
800
801 /**
802 * Takes a CSS style string and returns a hash of them.
803 * @param styleString - A CSS formatted string of styles. Eg,
804 * "font-size:12;fill:#d32c27;fill-rule:evenodd;stroke-width:1pt;"
805 * @param map - A map to which these styles will be added
806 */
807 public static HashMap parseStyle(String styleString, HashMap map) {
808 final Pattern patSemi = Pattern.compile(";");
809
810 String[] styles = patSemi.split(styleString);
811
812 for (int i = 0; i < styles.length; i++)
813 {
814 if (styles[i].length() == 0)
815 {
816 continue;
817 }
818
819 int colon = styles[i].indexOf(':');
820 if (colon == -1)
821 {
822 continue;
823 }
824
825 String key = styles[i].substring(0, colon).trim();
826 String value = styles[i].substring(colon + 1).trim();
827
828 map.put(key, new StyleAttribute(key, value));
829 }
830
831 return map;
832 }
833}
Note: See TracBrowser for help on using the repository browser.