summaryrefslogtreecommitdiffstats
path: root/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/kitfox/svg/xml/XMLParseUtil.java')
-rw-r--r--src/main/java/com/kitfox/svg/xml/XMLParseUtil.java806
1 files changed, 806 insertions, 0 deletions
diff --git a/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java b/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java
new file mode 100644
index 0000000..b9cbec0
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java
@@ -0,0 +1,806 @@
+/*
+ * XMLParseUtil.java
+ *
+ *
+ * The Salamander Project - 2D and 3D graphics libraries in Java
+ * Copyright (C) 2004 Mark McKay
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
+ * projects can be found at http://www.kitfox.com
+ *
+ * Created on February 18, 2004, 1:49 PM
+ */
+
+package com.kitfox.svg.xml;
+
+import org.w3c.dom.*;
+import java.awt.*;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+import java.lang.reflect.*;
+
+/**
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class XMLParseUtil
+{
+ static final Matcher fpMatch = Pattern.compile("([-+]?((\\d*\\.\\d+)|(\\d+))([eE][+-]?\\d+)?)(\\%|in|cm|mm|pt|pc)?").matcher("");
+ static final Matcher intMatch = Pattern.compile("[-+]?\\d+").matcher("");
+
+ /** Creates a new instance of XMLParseUtil */
+ private XMLParseUtil()
+ {
+ }
+
+ /**
+ * Scans the tag's children and returns the first text element found
+ */
+ public static String getTagText(Element ele)
+ {
+ NodeList nl = ele.getChildNodes();
+ int size = nl.getLength();
+
+ Node node = null;
+ int i = 0;
+ for (; i < size; i++)
+ {
+ node = nl.item(i);
+ if (node instanceof Text) break;
+ }
+ if (i == size || node == null) return null;
+
+ return ((Text)node).getData();
+ }
+
+ /**
+ * Returns the first node that is a direct child of root with the coresponding
+ * name. Does not search children of children.
+ */
+ public static Element getFirstChild(Element root, String name)
+ {
+ NodeList nl = root.getChildNodes();
+ int size = nl.getLength();
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (ele.getTagName().equals(name)) return ele;
+ }
+
+ return null;
+ }
+
+ public static String[] parseStringList(String list)
+ {
+// final Pattern patWs = Pattern.compile("\\s+");
+ final Matcher matchWs = Pattern.compile("[^\\s]+").matcher("");
+ matchWs.reset(list);
+
+ LinkedList matchList = new LinkedList();
+ while (matchWs.find())
+ {
+ matchList.add(matchWs.group());
+ }
+
+ String[] retArr = new String[matchList.size()];
+ return (String[])matchList.toArray(retArr);
+ }
+
+ public static boolean isDouble(String val)
+ {
+ fpMatch.reset(val);
+ return fpMatch.matches();
+ }
+
+ public static double parseDouble(String val)
+ {
+ /*
+ if (val == null) return 0.0;
+
+ double retVal = 0.0;
+ try
+ { retVal = Double.parseDouble(val); }
+ catch (Exception e)
+ {}
+ return retVal;
+ */
+ return findDouble(val);
+ }
+
+ /**
+ * Searches the given string for the first floating point number it contains,
+ * parses and returns it.
+ */
+ public synchronized static double findDouble(String val)
+ {
+ if (val == null) return 0;
+
+ fpMatch.reset(val);
+ try
+ {
+ if (!fpMatch.find()) return 0;
+ }
+ catch (StringIndexOutOfBoundsException e)
+ {
+ System.err.println("XMLParseUtil: regex parse problem: '" + val + "'");
+ e.printStackTrace();
+ }
+
+ val = fpMatch.group(1);
+ //System.err.println("Parsing " + val);
+
+ double retVal = 0;
+ try
+ {
+ retVal = Double.parseDouble(val);
+
+ float pixPerInch = (float)Toolkit.getDefaultToolkit().getScreenResolution();
+ final float inchesPerCm = .3936f;
+ final String units = fpMatch.group(6);
+
+ if ("%".equals(units)) retVal /= 100;
+ else if ("in".equals(units))
+ {
+ retVal *= pixPerInch;
+ }
+ else if ("cm".equals(units))
+ {
+ retVal *= inchesPerCm * pixPerInch;
+ }
+ else if ("mm".equals(units))
+ {
+ retVal *= inchesPerCm * pixPerInch * .1f;
+ }
+ else if ("pt".equals(units))
+ {
+ retVal *= (1f / 72f) * pixPerInch;
+ }
+ else if ("pc".equals(units))
+ {
+ retVal *= (1f / 6f) * pixPerInch;
+ }
+ }
+ catch (Exception e)
+ {}
+ return retVal;
+ }
+
+ /**
+ * Scans an input string for double values. For each value found, places
+ * in a list. This method regards any characters not part of a floating
+ * point value to be seperators. Thus this will parse whitespace seperated,
+ * comma seperated, and many other separation schemes correctly.
+ */
+ public synchronized static double[] parseDoubleList(String list)
+ {
+ if (list == null) return null;
+
+ fpMatch.reset(list);
+
+ LinkedList doubList = new LinkedList();
+ while (fpMatch.find())
+ {
+ String val = fpMatch.group(1);
+ doubList.add(Double.valueOf(val));
+ }
+
+ double[] retArr = new double[doubList.size()];
+ Iterator it = doubList.iterator();
+ int idx = 0;
+ while (it.hasNext())
+ {
+ retArr[idx++] = ((Double)it.next()).doubleValue();
+ }
+
+ return retArr;
+ }
+
+ public static float parseFloat(String val)
+ {
+ /*
+ if (val == null) return 0f;
+
+ float retVal = 0f;
+ try
+ { retVal = Float.parseFloat(val); }
+ catch (Exception e)
+ {}
+ return retVal;
+ */
+ return findFloat(val);
+ }
+
+ /**
+ * Searches the given string for the first floating point number it contains,
+ * parses and returns it.
+ */
+ public synchronized static float findFloat(String val)
+ {
+ if (val == null) return 0f;
+
+ fpMatch.reset(val);
+ if (!fpMatch.find()) return 0f;
+
+ val = fpMatch.group(1);
+ //System.err.println("Parsing " + val);
+
+ float retVal = 0f;
+ try
+ {
+ retVal = Float.parseFloat(val);
+ if (fpMatch.group(6).equals("%")) retVal /= 100;
+ }
+ catch (Exception e)
+ {}
+ return retVal;
+ }
+
+ public synchronized static float[] parseFloatList(String list)
+ {
+ if (list == null) return null;
+
+ fpMatch.reset(list);
+
+ LinkedList floatList = new LinkedList();
+ while (fpMatch.find())
+ {
+ String val = fpMatch.group(1);
+ floatList.add(Float.valueOf(val));
+ }
+
+ float[] retArr = new float[floatList.size()];
+ Iterator it = floatList.iterator();
+ int idx = 0;
+ while (it.hasNext())
+ {
+ retArr[idx++] = ((Float)it.next()).floatValue();
+ }
+
+ return retArr;
+ }
+
+ public static int parseInt(String val)
+ {
+ if (val == null) return 0;
+
+ int retVal = 0;
+ try
+ { retVal = Integer.parseInt(val); }
+ catch (Exception e)
+ {}
+ return retVal;
+ }
+
+ /**
+ * Searches the given string for the first integer point number it contains,
+ * parses and returns it.
+ */
+ public static int findInt(String val)
+ {
+ if (val == null) return 0;
+
+ intMatch.reset(val);
+ if (!intMatch.find()) return 0;
+
+ val = intMatch.group();
+ //System.err.println("Parsing " + val);
+
+ int retVal = 0;
+ try
+ { retVal = Integer.parseInt(val); }
+ catch (Exception e)
+ {}
+ return retVal;
+ }
+
+ public static int[] parseIntList(String list)
+ {
+ if (list == null) return null;
+
+ intMatch.reset(list);
+
+ LinkedList intList = new LinkedList();
+ while (intMatch.find())
+ {
+ String val = intMatch.group();
+ intList.add(Integer.valueOf(val));
+ }
+
+ int[] retArr = new int[intList.size()];
+ Iterator it = intList.iterator();
+ int idx = 0;
+ while (it.hasNext())
+ {
+ retArr[idx++] = ((Integer)it.next()).intValue();
+ }
+
+ return retArr;
+ }
+/*
+ public static int parseHex(String val)
+ {
+ int retVal = 0;
+
+ for (int i = 0; i < val.length(); i++)
+ {
+ retVal <<= 4;
+
+ char ch = val.charAt(i);
+ if (ch >= '0' && ch <= '9')
+ {
+ retVal |= ch - '0';
+ }
+ else if (ch >= 'a' && ch <= 'z')
+ {
+ retVal |= ch - 'a' + 10;
+ }
+ else if (ch >= 'A' && ch <= 'Z')
+ {
+ retVal |= ch - 'A' + 10;
+ }
+ else throw new RuntimeException();
+ }
+
+ return retVal;
+ }
+*/
+ /**
+ * The input string represents a ratio. Can either be specified as a
+ * double number on the range of [0.0 1.0] or as a percentage [0% 100%]
+ */
+ public static double parseRatio(String val)
+ {
+ if (val == null || val.equals("")) return 0.0;
+
+ if (val.charAt(val.length() - 1) == '%')
+ {
+ parseDouble(val.substring(0, val.length() - 1));
+ }
+ return parseDouble(val);
+ }
+
+ public static NumberWithUnits parseNumberWithUnits(String val)
+ {
+ if (val == null) return null;
+
+ return new NumberWithUnits(val);
+ }
+/*
+ public static Color parseColor(String val)
+ {
+ Color retVal = null;
+
+ if (val.charAt(0) == '#')
+ {
+ String hexStrn = val.substring(1);
+
+ if (hexStrn.length() == 3)
+ {
+ hexStrn = "" + hexStrn.charAt(0) + hexStrn.charAt(0) + hexStrn.charAt(1) + hexStrn.charAt(1) + hexStrn.charAt(2) + hexStrn.charAt(2);
+ }
+ int hexVal = parseHex(hexStrn);
+
+ retVal = new Color(hexVal);
+ }
+ else
+ {
+ final Matcher rgbMatch = Pattern.compile("rgb\\((\\d+),(\\d+),(\\d+)\\)", Pattern.CASE_INSENSITIVE).matcher("");
+
+ rgbMatch.reset(val);
+ if (rgbMatch.matches())
+ {
+ int r = Integer.parseInt(rgbMatch.group(1));
+ int g = Integer.parseInt(rgbMatch.group(2));
+ int b = Integer.parseInt(rgbMatch.group(3));
+ retVal = new Color(r, g, b);
+ }
+ else
+ {
+ Color lookupCol = ColorTable.instance().lookupColor(val);
+ if (lookupCol != null) retVal = lookupCol;
+ }
+ }
+
+ return retVal;
+ }
+*/
+ /**
+ * Parses the given attribute of this tag and returns it as a String.
+ */
+ public static String getAttribString(Element ele, String name)
+ {
+ return ele.getAttribute(name);
+ }
+
+ /**
+ * Parses the given attribute of this tag and returns it as an int.
+ */
+ public static int getAttribInt(Element ele, String name)
+ {
+ String sval = ele.getAttribute(name);
+ int val = 0;
+ try { val = Integer.parseInt(sval); } catch (Exception e) {}
+
+ return val;
+ }
+
+ /**
+ * Parses the given attribute of this tag as a hexadecimal encoded string and
+ * returns it as an int
+ */
+ public static int getAttribIntHex(Element ele, String name)
+ {
+ String sval = ele.getAttribute(name);
+ int val = 0;
+ try { val = Integer.parseInt(sval, 16); } catch (Exception e) {}
+
+ return val;
+ }
+
+ /**
+ * Parses the given attribute of this tag and returns it as a float
+ */
+ public static float getAttribFloat(Element ele, String name)
+ {
+ String sval = ele.getAttribute(name);
+ float val = 0.0f;
+ try { val = Float.parseFloat(sval); } catch (Exception e) {}
+
+ return val;
+ }
+
+ /**
+ * Parses the given attribute of this tag and returns it as a double.
+ */
+ public static double getAttribDouble(Element ele, String name)
+ {
+ String sval = ele.getAttribute(name);
+ double val = 0.0;
+ try { val = Double.parseDouble(sval); } catch (Exception e) {}
+
+ return val;
+ }
+
+ /**
+ * Parses the given attribute of this tag and returns it as a boolean.
+ * Essentially compares the lower case textual value to the string "true"
+ */
+ public static boolean getAttribBoolean(Element ele, String name)
+ {
+ String sval = ele.getAttribute(name);
+
+ return sval.toLowerCase().equals("true");
+ }
+
+ public static URL getAttribURL(Element ele, String name, URL docRoot)
+ {
+ String sval = ele.getAttribute(name);
+
+ URL url;
+ try
+ {
+ return new URL(docRoot, sval);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the first ReadableXMLElement with the given name
+ */
+ public static ReadableXMLElement getElement(Class classType, Element root, String name, URL docRoot)
+ {
+ if (root == null) return null;
+
+ //Do not process if not a LoadableObject
+ if (!ReadableXMLElement.class.isAssignableFrom(classType))
+ {
+ return null;
+ }
+
+ NodeList nl = root.getChildNodes();
+ int size = nl.getLength();
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (!ele.getTagName().equals(name)) continue;
+
+ ReadableXMLElement newObj = null;
+ try { newObj = (ReadableXMLElement)classType.newInstance(); }
+ catch (Exception e) { e.printStackTrace(); continue; }
+ newObj.read(ele, docRoot);
+
+ if (newObj == null) continue;
+
+ return newObj;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a HashMap of nodes that are children of root. All nodes will
+ * be of class classType and have a tag name of 'name'. 'key' is
+ * an attribute of tag 'name' who's string value will be used as the key
+ * in the HashMap
+ */
+ public static HashMap getElementHashMap(Class classType, Element root, String name, String key, URL docRoot)
+ {
+ if (root == null) return null;
+
+ //Do not process if not a LoadableObject
+ if (!ReadableXMLElement.class.isAssignableFrom(classType))
+ {
+ return null;
+ }
+
+ HashMap retMap = new HashMap();
+
+/*
+ Class[] params = {Element.class, URL.class};
+ Method loadMethod = null;
+ try { loadMethod = classType.getMethod("load", params); }
+ catch (Exception e) { e.printStackTrace(); return null; }
+
+ */
+ NodeList nl = root.getChildNodes();
+ int size = nl.getLength();
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (!ele.getTagName().equals(name)) continue;
+
+ ReadableXMLElement newObj = null;
+ try { newObj = (ReadableXMLElement)classType.newInstance(); }
+ catch (Exception e) { e.printStackTrace(); continue; }
+ newObj.read(ele, docRoot);
+/*
+ Object[] args = {ele, source};
+ Object obj = null;
+ try { obj = loadMethod.invoke(null, args); }
+ catch (Exception e) { e.printStackTrace(); }
+
+ */
+ if (newObj == null) continue;
+
+ String keyVal = getAttribString(ele, key);
+ retMap.put(keyVal, newObj);
+ }
+
+ return retMap;
+ }
+
+ public static HashSet getElementHashSet(Class classType, Element root, String name, URL docRoot)
+ {
+ if (root == null) return null;
+
+ //Do not process if not a LoadableObject
+ if (!ReadableXMLElement.class.isAssignableFrom(classType))
+ {
+ return null;
+ }
+
+ HashSet retSet = new HashSet();
+
+ /*
+ Class[] params = {Element.class, URL.class};
+ Method loadMethod = null;
+ try { loadMethod = classType.getMethod("load", params); }
+ catch (Exception e) { e.printStackTrace(); return null; }
+ */
+
+ NodeList nl = root.getChildNodes();
+ int size = nl.getLength();
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (!ele.getTagName().equals(name)) continue;
+
+ ReadableXMLElement newObj = null;
+ try { newObj = (ReadableXMLElement)classType.newInstance(); }
+ catch (Exception e) { e.printStackTrace(); continue; }
+ newObj.read(ele, docRoot);
+ /*
+ Object[] args = {ele, source};
+ Object obj = null;
+ try { obj = loadMethod.invoke(null, args); }
+ catch (Exception e) { e.printStackTrace(); }
+ */
+
+ if (newObj == null) continue;
+
+ retSet.add(newObj);
+ }
+
+ return retSet;
+ }
+
+
+ public static LinkedList getElementLinkedList(Class classType, Element root, String name, URL docRoot)
+ {
+ if (root == null) return null;
+
+ //Do not process if not a LoadableObject
+ if (!ReadableXMLElement.class.isAssignableFrom(classType))
+ {
+ return null;
+ }
+
+ NodeList nl = root.getChildNodes();
+ LinkedList elementCache = new LinkedList();
+ int size = nl.getLength();
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (!ele.getTagName().equals(name)) continue;
+
+ ReadableXMLElement newObj = null;
+ try { newObj = (ReadableXMLElement)classType.newInstance(); }
+ catch (Exception e) { e.printStackTrace(); continue; }
+ newObj.read(ele, docRoot);
+
+ elementCache.addLast(newObj);
+ }
+
+ return elementCache;
+ }
+
+ public static Object[] getElementArray(Class classType, Element root, String name, URL docRoot)
+ {
+ if (root == null) return null;
+
+ //Do not process if not a LoadableObject
+ if (!ReadableXMLElement.class.isAssignableFrom(classType))
+ {
+ return null;
+ }
+
+ LinkedList elementCache = getElementLinkedList(classType, root, name, docRoot);
+
+ Object[] retArr = (Object[])Array.newInstance(classType, elementCache.size());
+ return elementCache.toArray(retArr);
+ }
+
+ /**
+ * Takes a number of tags of name 'name' that are children of 'root', and
+ * looks for attributes of 'attrib' on them. Converts attributes to an
+ * int and returns in an array.
+ */
+ public static int[] getElementArrayInt(Element root, String name, String attrib)
+ {
+ if (root == null) return null;
+
+ NodeList nl = root.getChildNodes();
+ LinkedList elementCache = new LinkedList();
+ int size = nl.getLength();
+
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (!ele.getTagName().equals(name)) continue;
+
+ String valS = ele.getAttribute(attrib);
+ int eleVal = 0;
+ try { eleVal = Integer.parseInt(valS); }
+ catch (Exception e) {}
+
+ elementCache.addLast(new Integer(eleVal));
+ }
+
+ int[] retArr = new int[elementCache.size()];
+ Iterator it = elementCache.iterator();
+ int idx = 0;
+ while (it.hasNext())
+ {
+ retArr[idx++] = ((Integer)it.next()).intValue();
+ }
+
+ return retArr;
+ }
+
+ /**
+ * Takes a number of tags of name 'name' that are children of 'root', and
+ * looks for attributes of 'attrib' on them. Converts attributes to an
+ * int and returns in an array.
+ */
+ public static String[] getElementArrayString(Element root, String name, String attrib)
+ {
+ if (root == null) return null;
+
+ NodeList nl = root.getChildNodes();
+ LinkedList elementCache = new LinkedList();
+ int size = nl.getLength();
+
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (!ele.getTagName().equals(name)) continue;
+
+ String valS = ele.getAttribute(attrib);
+
+ elementCache.addLast(valS);
+ }
+
+ String[] retArr = new String[elementCache.size()];
+ Iterator it = elementCache.iterator();
+ int idx = 0;
+ while (it.hasNext())
+ {
+ retArr[idx++] = (String)it.next();
+ }
+
+ return retArr;
+ }
+
+ /**
+ * Takes a CSS style string and retursn a hash of them.
+ * @param styleString - A CSS formatted string of styles. Eg,
+ * "font-size:12;fill:#d32c27;fill-rule:evenodd;stroke-width:1pt;"
+ */
+ public static HashMap parseStyle(String styleString) {
+ return parseStyle(styleString, new HashMap());
+ }
+
+ /**
+ * Takes a CSS style string and retursn a hash of them.
+ * @param styleString - A CSS formatted string of styles. Eg,
+ * "font-size:12;fill:#d32c27;fill-rule:evenodd;stroke-width:1pt;"
+ * @param map - A map to which these styles will be added
+ */
+ public static HashMap parseStyle(String styleString, HashMap map) {
+ final Pattern patSemi = Pattern.compile(";");
+ final Pattern patColonSpace = Pattern.compile(":");
+
+ //Strips left and right whitespace
+ final Matcher matcherContent = Pattern.compile("\\s*([^\\s](.*[^\\s])?)\\s*").matcher("");
+
+ String[] styles = patSemi.split(styleString);
+
+ for (int i = 0; i < styles.length; i++) {
+ String[] vals = patColonSpace.split(styles[i]);
+
+ matcherContent.reset(vals[0]);
+ matcherContent.matches();
+ vals[0] = matcherContent.group(1);
+
+ matcherContent.reset(vals[1]);
+ matcherContent.matches();
+ vals[1] = matcherContent.group(1);
+
+ map.put(vals[0], new StyleAttribute(vals[0], vals[1]));
+ }
+
+ return map;
+ }
+}