diff --git a/svg-core/src/main/java/com/kitfox/svg/SVGAnimatedCache.java b/svg-core/src/main/java/com/kitfox/svg/SVGAnimatedCache.java new file mode 100644 index 0000000..c571e63 --- /dev/null +++ b/svg-core/src/main/java/com/kitfox/svg/SVGAnimatedCache.java @@ -0,0 +1,49 @@ +package com.kitfox.svg; + +public class SVGAnimatedCache { + private static SVGAnimatedCache INSTANCE; + private static final SVGUniverse svgUniverse = new SVGUniverse(); + private static double svgTimer = 0d, svgStep = 0.05d; + private static final Thread svgThreadTimer = new Thread(new Runnable() { + @Override + public void run() { + svgTimer += svgStep; + try { + svgUniverse.setCurTime(svgTimer); + svgUniverse.updateTime(); + } catch (SVGException e1) { + e1.printStackTrace(); + } + + try { + Thread.sleep((long) (svgStep * 1000)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + + private SVGAnimatedCache() { + } + + public static SVGUniverse getSVGUniverse() { + return svgUniverse; + } + + public static void startAnimation(){ + if(!svgThreadTimer.isAlive()) + svgThreadTimer.start(); + } + + public static void stopAnimation(){ + svgTimer = 0d; + svgThreadTimer.interrupt(); + } + + public static SVGAnimatedCache getInstance() { + if(INSTANCE == null){ + INSTANCE = new SVGAnimatedCache(); + } + return INSTANCE; + } +} diff --git a/svg-core/src/main/java/com/kitfox/svg/SVGDiagram.java b/svg-core/src/main/java/com/kitfox/svg/SVGDiagram.java index 873efd1..b5901ff 100644 --- a/svg-core/src/main/java/com/kitfox/svg/SVGDiagram.java +++ b/svg-core/src/main/java/com/kitfox/svg/SVGDiagram.java @@ -1,272 +1,272 @@ -/* - * SVG Salamander - * Copyright (c) 2004, Mark McKay - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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, 5:04 PM - */ - -package com.kitfox.svg; - -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.io.Serializable; -import java.net.URI; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - - -/** - * Top level structure in an SVG tree. - * - * @author Mark McKay - * @author Mark McKay - */ -public class SVGDiagram implements Serializable -{ - public static final long serialVersionUID = 0; - - //Indexes elements within this SVG diagram - final HashMap idMap = new HashMap(); - - SVGRoot root; - final SVGUniverse universe; - - /** - * This is used by the SVGRoot to determine the width of the - */ - private Rectangle deviceViewport = new Rectangle(100, 100); - - /** - * If true, no attempt will be made to discard geometry based on it being - * out of bounds. This trades potentially drawing many out of bounds - * shapes with having to recalculate bounding boxes every animation iteration. - */ - protected boolean ignoreClipHeuristic = false; - - /** - * URL which uniquely identifies this document - */ -// final URI docRoot; - - /** - * URI that uniquely identifies this document. Also used to resolve - * relative urls. Default base for document. - */ - final URI xmlBase; - - /** - * Creates a new instance of SVGDiagram - * @param xmlBase - * @param universe - */ - public SVGDiagram(URI xmlBase, SVGUniverse universe) - { - this.universe = universe; -// this.docRoot = docRoot; - this.xmlBase = xmlBase; - } - - /** - * Draws this diagram to the passed graphics context - * @param g - * @throws com.kitfox.svg.SVGException - */ - public void render(Graphics2D g) throws SVGException - { - root.renderToViewport(g); - } - - /** - * Searches thorough the scene graph for all RenderableElements that have - * shapes that contain the passed point. - * - * For every shape which contains the pick point, a List containing the - * path to the node is added to the return list. That is, the result of - * SVGElement.getPath() is added for each entry. - * - * @param point - * @param retVec - * @return the passed in list - * @throws com.kitfox.svg.SVGException - */ - public List> pick(Point2D point, List> retVec) throws SVGException - { - return pick(point, false, retVec); - } - - public List> pick(Point2D point, boolean boundingBox, List> retVec) throws SVGException - { - if (retVec == null) - { - retVec = new ArrayList>(); - } - - root.pick(point, boundingBox, retVec); - - return retVec; - } - - public List> pick(Rectangle2D pickArea, List> retVec) throws SVGException - { - return pick(pickArea, false, retVec); - } - - public List> pick(Rectangle2D pickArea, boolean boundingBox, List> retVec) throws SVGException - { - if (retVec == null) - { - retVec = new ArrayList>(); - } - - root.pick(pickArea, new AffineTransform(), boundingBox, retVec); - - return retVec; - } - - public SVGUniverse getUniverse() - { - return universe; - } - - public URI getXMLBase() - { - return xmlBase; - } - -// public URL getDocRoot() -// { -// return docRoot; -// } - - public float getWidth() - { - if (root == null) return 0; - return root.getDeviceWidth(); - } - - public float getHeight() - { - if (root == null) return 0; - return root.getDeviceHeight(); - } - - /** - * Returns the viewing rectangle of this diagram in device coordinates. - * @param rect - * @return - */ - public Rectangle2D getViewRect(Rectangle2D rect) - { - if (root != null) return root.getDeviceRect(rect); - return rect; - } - - public Rectangle2D getViewRect() - { - return getViewRect(new Rectangle2D.Double()); - } - - public SVGElement getElement(String name) - { - return (SVGElement)idMap.get(name); - } - - public void setElement(String name, SVGElement node) - { - idMap.put(name, node); - } - - public void removeElement(String name) - { - idMap.remove(name); - } - - public SVGRoot getRoot() - { - return root; - } - - public void setRoot(SVGRoot root) - { - this.root = root; - root.setDiagram(this); - } - - public boolean ignoringClipHeuristic() { return ignoreClipHeuristic; } - - public void setIgnoringClipHeuristic(boolean ignoreClipHeuristic) { this.ignoreClipHeuristic = ignoreClipHeuristic; } - - /** - * Updates all attributes in this diagram associated with a time event. - * Ie, all attributes with track information. - * @param curTime - * @throws com.kitfox.svg.SVGException - */ - public void updateTime(double curTime) throws SVGException - { - if (root == null) return; - root.updateTime(curTime); - } - - public Rectangle getDeviceViewport() - { - return deviceViewport; - } - - /** - * Sets the dimensions of the device being rendered into. This is used by - * SVGRoot when its x, y, width or height parameters are specified as - * percentages. - * @param deviceViewport - */ - public void setDeviceViewport(Rectangle deviceViewport) - { - this.deviceViewport.setBounds(deviceViewport); - if (root != null) - { - try - { - root.build(); - } catch (SVGException ex) - { - Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, - "Could not build document", ex); - } - } - } -} +/* + * SVG Salamander + * Copyright (c) 2004, Mark McKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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, 5:04 PM + */ + +package com.kitfox.svg; + +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.Serializable; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + + +/** + * Top level structure in an SVG tree. + * + * @author Mark McKay + * @author Mark McKay + */ +public class SVGDiagram implements Serializable +{ + public static final long serialVersionUID = 0; + + //Indexes elements within this SVG diagram + final HashMap idMap = new HashMap(); + + SVGRoot root; + final SVGUniverse universe; + + /** + * This is used by the SVGRoot to determine the width of the + */ + private Rectangle deviceViewport = new Rectangle(100, 100); + + /** + * If true, no attempt will be made to discard geometry based on it being + * out of bounds. This trades potentially drawing many out of bounds + * shapes with having to recalculate bounding boxes every animation iteration. + */ + protected boolean ignoreClipHeuristic = false; + + /** + * URL which uniquely identifies this document + */ +// final URI docRoot; + + /** + * URI that uniquely identifies this document. Also used to resolve + * relative urls. Default base for document. + */ + final URI xmlBase; + + /** + * Creates a new instance of SVGDiagram + * @param xmlBase + * @param universe + */ + public SVGDiagram(URI xmlBase, SVGUniverse universe) + { + this.universe = universe; +// this.docRoot = docRoot; + this.xmlBase = xmlBase; + } + + /** + * Draws this diagram to the passed graphics context + * @param g + * @throws com.kitfox.svg.SVGException + */ + public void render(Graphics2D g) throws SVGException + { + root.renderToViewport(g); + } + + /** + * Searches thorough the scene graph for all RenderableElements that have + * shapes that contain the passed point. + * + * For every shape which contains the pick point, a List containing the + * path to the node is added to the return list. That is, the result of + * SVGElement.getPath() is added for each entry. + * + * @param point + * @param retVec + * @return the passed in list + * @throws com.kitfox.svg.SVGException + */ + public List> pick(Point2D point, List> retVec) throws SVGException + { + return pick(point, false, retVec); + } + + public List> pick(Point2D point, boolean boundingBox, List> retVec) throws SVGException + { + if (retVec == null) + { + retVec = new ArrayList>(); + } + + root.pick(point, boundingBox, retVec); + + return retVec; + } + + public List> pick(Rectangle2D pickArea, List> retVec) throws SVGException + { + return pick(pickArea, false, retVec); + } + + public List> pick(Rectangle2D pickArea, boolean boundingBox, List> retVec) throws SVGException + { + if (retVec == null) + { + retVec = new ArrayList>(); + } + + root.pick(pickArea, new AffineTransform(), boundingBox, retVec); + + return retVec; + } + + public SVGUniverse getUniverse() + { + return universe; + } + + public URI getXMLBase() + { + return xmlBase; + } + +// public URL getDocRoot() +// { +// return docRoot; +// } + + public float getWidth() + { + if (root == null) return 0; + return root.getDeviceWidth(); + } + + public float getHeight() + { + if (root == null) return 0; + return root.getDeviceHeight(); + } + + /** + * Returns the viewing rectangle of this diagram in device coordinates. + * @param rect + * @return + */ + public Rectangle2D getViewRect(Rectangle2D rect) + { + if (root != null) return root.getDeviceRect(rect); + return rect; + } + + public Rectangle2D getViewRect() + { + return getViewRect(new Rectangle2D.Double()); + } + + public SVGElement getElement(String name) + { + return (SVGElement)idMap.get(name); + } + + public void setElement(String name, SVGElement node) + { + idMap.put(name, node); + } + + public void removeElement(String name) + { + idMap.remove(name); + } + + public SVGRoot getRoot() + { + return root; + } + + public void setRoot(SVGRoot root) + { + this.root = root; + root.setDiagram(this); + } + + public boolean ignoringClipHeuristic() { return ignoreClipHeuristic; } + + public void setIgnoringClipHeuristic(boolean ignoreClipHeuristic) { this.ignoreClipHeuristic = ignoreClipHeuristic; } + + /** + * Updates all attributes in this diagram associated with a time event. + * Ie, all attributes with track information. + * @param curTime + * @throws com.kitfox.svg.SVGException + */ + public void updateTime(double curTime) throws SVGException + { + if (root == null) return; + root.updateTime(curTime); + } + + public Rectangle getDeviceViewport() + { + return deviceViewport; + } + + /** + * Sets the dimensions of the device being rendered into. This is used by + * SVGRoot when its x, y, width or height parameters are specified as + * percentages. + * @param deviceViewport + */ + public void setDeviceViewport(Rectangle deviceViewport) + { + this.deviceViewport.setBounds(deviceViewport); + if (root != null) + { + try + { + root.build(); + } catch (SVGException ex) + { + Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, + "Could not build document", ex); + } + } + } +} diff --git a/svg-core/src/main/java/com/kitfox/svg/app/SVGAnimated.java b/svg-core/src/main/java/com/kitfox/svg/app/SVGAnimated.java new file mode 100644 index 0000000..88b75e1 --- /dev/null +++ b/svg-core/src/main/java/com/kitfox/svg/app/SVGAnimated.java @@ -0,0 +1,246 @@ +package com.kitfox.svg.app; + +import com.kitfox.svg.*; +import com.kitfox.svg.animation.AnimationElement; +import com.kitfox.svg.app.beans.SVGIcon; + +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.net.URI; + +public class SVGAnimated { + public static final int AUTOSIZE_NONE = 0; + public static final int AUTOSIZE_HORIZ = 1; + public static final int AUTOSIZE_VERT = 2; + public static final int AUTOSIZE_BESTFIT = 3; + public static final int AUTOSIZE_STRETCH = 4; + + public static final int INTERP_NEAREST_NEIGHBOR = 0; + public static final int INTERP_BILINEAR = 1; + public static final int INTERP_BICUBIC = 2; + + private boolean antiAlias = true; + private int interpolation = INTERP_BILINEAR; + private boolean clipToViewbox = false; + private int autosize = AUTOSIZE_BESTFIT; + + private SVGAnimatedCache svgCache = SVGAnimatedCache.getInstance(); //new SVGAnimatedCache(); //SVGCache.getSVGUniverse(); + private SVGDiagram diagram; + + private Dimension preferredSize; + private AffineTransform scaleSVG = new AffineTransform(); + + private Component parent; + private boolean isAnimationRunning = false; + + public SVGAnimated(String svg, Component parent) { + this.parent = parent; + try { + URI svgURI = svgCache.getSVGUniverse().loadSVG(getClass().getResourceAsStream(svg), svg); + diagram = svgCache.getSVGUniverse().getDiagram(svgURI); + diagram.setParentComponent(parent); + + if (diagram != null) { + Dimension size = getPreferredSize(); + if (size == null) { + size = new Dimension((int) diagram.getRoot().getDeviceWidth(), (int) diagram.getRoot().getDeviceHeight()); + } + diagram.setDeviceViewport(new Rectangle(0, 0, size.width, size.height)); + } + + } catch (IOException e) { + e.printStackTrace(); + } + } + + public Dimension getPreferredSize() { + if (preferredSize == null) { + setPreferredSize(new Dimension((int) diagram.getWidth(), (int) diagram.getHeight())); + } + + return new Dimension(preferredSize); + } + + public void setPreferredSize(Dimension preferredSize) { + Dimension old = this.preferredSize; + this.preferredSize = preferredSize; + + diagram.setDeviceViewport(new Rectangle(0, 0, preferredSize.width, preferredSize.height)); + calculateScale(); +// changes.firePropertyChange("preferredSize", old, preferredSize); + } + + public int getIconWidthIgnoreAutosize() { + if (preferredSize != null && + (autosize == AUTOSIZE_HORIZ || autosize == AUTOSIZE_STRETCH + || autosize == AUTOSIZE_BESTFIT)) { + return preferredSize.width; + } + + if (diagram == null) { + return 0; + } + return (int) diagram.getWidth(); + } + + public int getIconHeightIgnoreAutosize() { + if (preferredSize != null && + (autosize == AUTOSIZE_VERT || autosize == AUTOSIZE_STRETCH + || autosize == AUTOSIZE_BESTFIT)) { + return preferredSize.height; + } + + if (diagram == null) { + return 0; + } + return (int) diagram.getHeight(); + } + + private void calculateScale() { + final int width = getIconWidthIgnoreAutosize(); + final int height = getIconHeightIgnoreAutosize(); + + if (width == 0 || height == 0) { + scaleSVG.setToScale(1, 1); + return; + } + + double diaWidth = diagram.getWidth(); + double diaHeight = diagram.getHeight(); + + double scaleW = 1; + double scaleH = 1; + switch (autosize){ + case AUTOSIZE_BESTFIT: + scaleW = scaleH = (height / diaHeight < width / diaWidth) + ? height / diaHeight : width / diaWidth; + break; + case AUTOSIZE_HORIZ: + scaleW = scaleH = width / diaWidth; + break; + case AUTOSIZE_VERT: + scaleW = scaleH = height / diaHeight; + break; + case AUTOSIZE_STRETCH: + scaleW = width / diaWidth; + scaleH = height / diaHeight; + break; + default: break; + } + scaleSVG.setToScale(scaleW, scaleH); + } + + public void render(Graphics2D g, int x, int y) { + if(!isAnimationRunning) return; + diagram.setRepaintParentComponent(parent, x, y, preferredSize.width, preferredSize.height); + + Object oldAliasHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF); + + Object oldInterpolationHint = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION); + switch (interpolation) { + case SVGIcon.INTERP_NEAREST_NEIGHBOR: + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + break; + case SVGIcon.INTERP_BILINEAR: + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + break; + case SVGIcon.INTERP_BICUBIC: + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + break; + } + + if (diagram == null) { + System.err.println("No diagram"); + return; + } + + g.translate(x, y); + diagram.setIgnoringClipHeuristic(!clipToViewbox); + if (clipToViewbox) { + g.setClip(new Rectangle2D.Float(0, 0, diagram.getWidth(), diagram.getHeight())); + } + + AffineTransform oldXform = g.getTransform(); + g.transform(scaleSVG); + + try { + diagram.render(g); + } catch (SVGException e) { + throw new RuntimeException(e); + } + + g.setTransform(oldXform); + g.translate(-x, -y); + + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAliasHint); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, oldInterpolationHint); + } + + public void startAnimation() { + isAnimationRunning = true; + svgCache.startAnimation(); + } + + public void stopAnimation(){ + isAnimationRunning = false; + svgCache.stopAnimation(); + } + + public void setColor(Color color) { + String hexColor = String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue()); + if(diagram != null){ + for(SVGElement elem : diagram.getRoot().getChildren(null)){ + try { + setColor(hexColor, elem); + } catch (SVGElementException e) { + e.printStackTrace(); + } + } + } + } + + private void setColor(String color, SVGElement element) throws SVGElementException { + if(element.hasAttribute("fill", AnimationElement.AT_AUTO)){ + element.setAttribute("fill", AnimationElement.AT_AUTO, color); + } + + for(SVGElement elem : element.getChildren(null)){ + setColor(color, elem); + } + } + + public boolean isAntiAlias() { + return antiAlias; + } + + public void setAntiAlias(boolean antiAlias) { + this.antiAlias = antiAlias; + } + + public int getInterpolation() { + return interpolation; + } + + public void setInterpolation(int interpolation) { + this.interpolation = interpolation; + } + + public boolean isClipToViewbox() { + return clipToViewbox; + } + + public void setClipToViewbox(boolean clipToViewbox) { + this.clipToViewbox = clipToViewbox; + } + + public int getAutosize() { + return autosize; + } + + public void setAutosize(int autosize) { + this.autosize = autosize; + } +} diff --git a/svg-core/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java b/svg-core/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java index 4f1a1df..1d3367d 100644 --- a/svg-core/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java +++ b/svg-core/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java @@ -1,833 +1,833 @@ -/* - * SVG Salamander - * Copyright (c) 2004, Mark McKay - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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 com.kitfox.svg.SVGConst; -import org.w3c.dom.*; -import java.awt.*; -import java.net.*; -import java.util.*; -import java.util.regex.*; -import java.lang.reflect.*; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author Mark McKay - * @author Mark McKay - */ -public class XMLParseUtil -{ - static final Matcher fpMatch = Pattern.compile("([-+]?((\\d*\\.\\d+)|(\\d+))([eE][+-]?\\d+)?)(\\%|in|cm|mm|pt|pc|px|em|ex)?").matcher(""); - static final Matcher intMatch = Pattern.compile("[-+]?\\d+").matcher(""); - static final Matcher quoteMatch = Pattern.compile("^'|'$").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) - { - Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, - "XMLParseUtil: regex parse problem: '" + val + "'", e); - } - - val = fpMatch.group(1); - //System.err.println("Parsing " + val); - - double retVal = 0; - try - { - retVal = Double.parseDouble(val); - - float pixPerInch; - try { - pixPerInch = (float)Toolkit.getDefaultToolkit().getScreenResolution(); - } - catch (NoClassDefFoundError err) - { - //Default value for headless X servers - pixPerInch = 72; - } - 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); - String units = fpMatch.group(6); - if ("%".equals(units)) 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); - - 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) - { - Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e); - 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(); - - 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) - { - Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e); - continue; - } - newObj.read(ele, docRoot); - - 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(); - - 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) - { - Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e); - continue; - } - newObj.read(ele, docRoot); - - 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) - { - Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e); - 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++] = 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++] = 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 returns 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(";"); - - String[] styles = patSemi.split(styleString); - - for (int i = 0; i < styles.length; i++) - { - if (styles[i].length() == 0) - { - continue; - } - - int colon = styles[i].indexOf(':'); - if (colon == -1) - { - continue; - } - - String key = styles[i].substring(0, colon).trim(); - String value = quoteMatch.reset(styles[i].substring(colon + 1).trim()).replaceAll(""); - - map.put(key, new StyleAttribute(key, value)); - } - - return map; - } -} +/* + * SVG Salamander + * Copyright (c) 2004, Mark McKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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 com.kitfox.svg.SVGConst; +import org.w3c.dom.*; +import java.awt.*; +import java.net.*; +import java.util.*; +import java.util.regex.*; +import java.lang.reflect.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class XMLParseUtil +{ + static final Matcher fpMatch = Pattern.compile("([-+]?((\\d*\\.\\d+)|(\\d+))([eE][+-]?\\d+)?)(\\%|in|cm|mm|pt|pc|px|em|ex)?").matcher(""); + static final Matcher intMatch = Pattern.compile("[-+]?\\d+").matcher(""); + static final Matcher quoteMatch = Pattern.compile("^'|'$").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) + { + Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, + "XMLParseUtil: regex parse problem: '" + val + "'", e); + } + + val = fpMatch.group(1); + //System.err.println("Parsing " + val); + + double retVal = 0; + try + { + retVal = Double.parseDouble(val); + + float pixPerInch; + try { + pixPerInch = (float)Toolkit.getDefaultToolkit().getScreenResolution(); + } + catch (NoClassDefFoundError err) + { + //Default value for headless X servers + pixPerInch = 72; + } + 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); + String units = fpMatch.group(6); + if ("%".equals(units)) 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); + + 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) + { + Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e); + 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(); + + 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) + { + Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e); + continue; + } + newObj.read(ele, docRoot); + + 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(); + + 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) + { + Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e); + continue; + } + newObj.read(ele, docRoot); + + 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) + { + Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e); + 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++] = 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++] = 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 returns 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(";"); + + String[] styles = patSemi.split(styleString); + + for (int i = 0; i < styles.length; i++) + { + if (styles[i].length() == 0) + { + continue; + } + + int colon = styles[i].indexOf(':'); + if (colon == -1) + { + continue; + } + + String key = styles[i].substring(0, colon).trim().intern(); + String value = quoteMatch.reset(styles[i].substring(colon + 1).trim()).replaceAll("").intern(); + + map.put(key, new StyleAttribute(key, value)); + } + + return map; + } +}