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

Last change on this file since 4739 was 4256, checked in by bastiK, 14 years ago

see #6560 - basic svg support, includes kitfox svgsalamander, r 98, patched

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