package com.kreative.paint.material.gradient; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; public class GradientParser { public static GradientList parse(String name, InputStream in) throws IOException { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(true); // make sure the XML is valid factory.setExpandEntityReferences(false); // don't allow custom entities DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(new GRDXEntityResolver()); builder.setErrorHandler(new GRDXErrorHandler(name)); Document document = builder.parse(new InputSource(in)); return parseDocument(document); } catch (ParserConfigurationException pce) { throw new IOException(pce); } catch (SAXException saxe) { throw new IOException(saxe); } } private static GradientList parseDocument(Node node) throws IOException { String type = node.getNodeName(); if (type.equalsIgnoreCase("#document")) { for (Node child : getChildren(node)) { String ctype = child.getNodeName(); if (ctype.equalsIgnoreCase("gradients")) { if (child.hasAttributes() || child.hasChildNodes()) { return parseGradients(child); } } else { throw new IOException("Unknown element: " + ctype); } } throw new IOException("Empty document."); } else { throw new IOException("Unknown element: " + type); } } private static GradientList parseGradients(Node node) throws IOException { String type = node.getNodeName(); if (type.equalsIgnoreCase("gradients")) { NamedNodeMap attr = node.getAttributes(); String name = parseString(attr, "name"); GradientList list = new GradientList(name); for (Node child : getChildren(node)) { String ctype = child.getNodeName(); if (ctype.equalsIgnoreCase("gradient")) { GradientPreset gp = parseGradient(child); if (gp != null) list.presets.add(gp); } else if (ctype.equalsIgnoreCase("shape")) { GradientShape gs = parseShape(child); if (gs != null) list.shapes.add(gs); } else if (ctype.equalsIgnoreCase("map")) { GradientColorMap gm = parseColorMap(child); if (gm != null) list.colorMaps.add(gm); } else { throw new IOException("Unknown element: " + ctype); } } return list; } else { throw new IOException("Unknown element: " + type); } } private static GradientPreset parseGradient(Node node) throws IOException { String type = node.getNodeName(); if (type.equalsIgnoreCase("gradient")) { NamedNodeMap attr = node.getAttributes(); String name = parseString(attr, "name"); GradientShape shape = null; GradientColorMap map = null; for (Node child : getChildren(node)) { String ctype = child.getNodeName(); if (ctype.equalsIgnoreCase("shape")) { shape = parseShape(child); } else if (ctype.equalsIgnoreCase("map")) { map = parseColorMap(child); } else { throw new IOException("Unknown element: " + ctype); } } return new GradientPreset(shape, map, name); } else { throw new IOException("Unknown element: " + type); } } private static GradientShape parseShape(Node node) throws IOException { String type = node.getNodeName(); if (type.equalsIgnoreCase("shape")) { NamedNodeMap attr = node.getAttributes(); String name = parseString(attr, "name"); boolean repeat = false; boolean reflect = false; boolean reverse = false; GradientShape shape = null; for (Node child : getChildren(node)) { String ctype = child.getNodeName(); NamedNodeMap cattr = child.getAttributes(); repeat = parseBoolean(cattr, "repeat", repeat); reflect = parseBoolean(cattr, "reflect", reflect); reverse = parseBoolean(cattr, "reverse", reverse); if (ctype.equalsIgnoreCase("linear")) { shape = new GradientShape.Linear( parseDouble(cattr, "x0", 0.0), parseDouble(cattr, "y0", 0.0), parseDouble(cattr, "x1", 0.0), parseDouble(cattr, "y1", 0.0), repeat, reflect, reverse, name ); } else if (ctype.equalsIgnoreCase("angular")) { shape = new GradientShape.Angular( parseDouble(cattr, "cx", 0.0), parseDouble(cattr, "cy", 0.0), parseDouble(cattr, "px", 0.0), parseDouble(cattr, "py", 0.0), repeat, reflect, reverse, name ); } else if (ctype.equalsIgnoreCase("radial")) { shape = new GradientShape.Radial( parseDouble(cattr, "cx", 0.0), parseDouble(cattr, "cy", 0.0), parseDouble(cattr, "x0", 0.0), parseDouble(cattr, "y0", 0.0), parseDouble(cattr, "x1", 0.0), parseDouble(cattr, "y1", 0.0), repeat, reflect, reverse, name ); } else if (ctype.equalsIgnoreCase("rectangular")) { shape = new GradientShape.Rectangular( parseDouble(cattr, "l0", 0.0), parseDouble(cattr, "t0", 0.0), parseDouble(cattr, "r0", 0.0), parseDouble(cattr, "b0", 0.0), parseDouble(cattr, "l1", 0.0), parseDouble(cattr, "t1", 0.0), parseDouble(cattr, "r1", 0.0), parseDouble(cattr, "b1", 0.0), repeat, reflect, reverse, name ); } else if (ctype.equalsIgnoreCase("diamond")) { System.err.println( "Warning: Unimplemented gradient shape <" + ctype + ">. " + "Ignoring gradient \"" + name + "\"." ); } else { throw new IOException("Unknown element: " + ctype); } } return shape; } else { throw new IOException("Unknown element: " + type); } } private static GradientColorMap parseColorMap(Node node) throws IOException { String type = node.getNodeName(); if (type.equalsIgnoreCase("map")) { NamedNodeMap attr = node.getAttributes(); String name = parseString(attr, "name"); GradientColorMap map = new GradientColorMap(name); for (Node child : getChildren(node)) { map.add(parseColorStop(child)); } return map; } else { throw new IOException("Unknown element: " + type); } } private static GradientColorStop parseColorStop(Node node) throws IOException { String type = node.getNodeName(); if (type.equalsIgnoreCase("stop")) { NamedNodeMap attr = node.getAttributes(); double position = parseDouble(attr, "at", 0.0); GradientColor color = null; for (Node child : getChildren(node)) { color = parseColor(child); } return new GradientColorStop(position, color); } else { throw new IOException("Unknown element: " + type); } } private static GradientColor parseColor(Node node) throws IOException { String type = node.getNodeName(); NamedNodeMap attr = node.getAttributes(); if (type.equalsIgnoreCase("rgb")) { return new GradientColor.RGB( parseInt(attr, "r", 0), parseInt(attr, "g", 0), parseInt(attr, "b", 0) ); } else if (type.equalsIgnoreCase("rgb16")) { return new GradientColor.RGB16( parseInt(attr, "r", 0), parseInt(attr, "g", 0), parseInt(attr, "b", 0) ); } else if (type.equalsIgnoreCase("rgbd")) { return new GradientColor.RGBD( parseRGBD(attr, "r", 0.0f), parseRGBD(attr, "g", 0.0f), parseRGBD(attr, "b", 0.0f) ); } else if (type.equalsIgnoreCase("rgba")) { return new GradientColor.RGBA( parseInt(attr, "r", 0), parseInt(attr, "g", 0), parseInt(attr, "b", 0), parseInt(attr, "a", 0) ); } else if (type.equalsIgnoreCase("rgba16")) { return new GradientColor.RGBA16( parseInt(attr, "r", 0), parseInt(attr, "g", 0), parseInt(attr, "b", 0), parseInt(attr, "a", 0) ); } else if (type.equalsIgnoreCase("rgbad")) { return new GradientColor.RGBAD( parseRGBD(attr, "r", 0.0f), parseRGBD(attr, "g", 0.0f), parseRGBD(attr, "b", 0.0f), parseRGBD(attr, "a", 0.0f) ); } else if (type.equalsIgnoreCase("hsv")) { return new GradientColor.HSV( parseFloat(attr, "h", 0.0f), parseFloat(attr, "s", 0.0f), parseFloat(attr, "v", 0.0f) ); } else if (type.equalsIgnoreCase("hsva")) { return new GradientColor.HSVA( parseFloat(attr, "h", 0.0f), parseFloat(attr, "s", 0.0f), parseFloat(attr, "v", 0.0f), parseFloat(attr, "a", 0.0f) ); } else { throw new IOException("Unknown element: " + type); } } private static float parseRGBD(NamedNodeMap attr, String key, float def) { if (attr == null) return def; Node node = attr.getNamedItem(key); if (node == null) return def; String text = node.getTextContent(); if (text == null) return def; try { int o = text.indexOf("/"); if (o >= 0) { float n = Float.parseFloat(text.substring(0, o).trim()); float d = Float.parseFloat(text.substring(o + 1).trim()); return n / d; } text = text.trim(); if (text.endsWith("%")) { float p = Float.parseFloat(text.substring(0, text.length() - 1).trim()); return p / 100.0f; } return Float.parseFloat(text); } catch (NumberFormatException nfe) { return def; } } private static boolean parseBoolean(NamedNodeMap attr, String key, boolean def) { if (attr == null) return def; Node node = attr.getNamedItem(key); if (node == null) return def; String text = node.getTextContent(); if (text == null) return def; text = text.trim(); if (text.equalsIgnoreCase("yes")) return true; if (text.equalsIgnoreCase("no")) return false; return def; } private static double parseDouble(NamedNodeMap attr, String key, double def) { if (attr == null) return def; Node node = attr.getNamedItem(key); if (node == null) return def; String text = node.getTextContent(); if (text == null) return def; try { return Double.parseDouble(text.trim()); } catch (NumberFormatException nfe) { return def; } } private static float parseFloat(NamedNodeMap attr, String key, float def) { if (attr == null) return def; Node node = attr.getNamedItem(key); if (node == null) return def; String text = node.getTextContent(); if (text == null) return def; try { return Float.parseFloat(text.trim()); } catch (NumberFormatException nfe) { return def; } } private static int parseInt(NamedNodeMap attr, String key, int def) { if (attr == null) return def; Node node = attr.getNamedItem(key); if (node == null) return def; String text = node.getTextContent(); if (text == null) return def; try { return Integer.parseInt(text.trim()); } catch (NumberFormatException nfe) { return def; } } private static String parseString(NamedNodeMap attr, String key) { if (attr == null) return null; Node node = attr.getNamedItem(key); if (node == null) return null; String text = node.getTextContent(); if (text == null) return null; return text.trim(); } private static List<Node> getChildren(Node node) { List<Node> list = new ArrayList<Node>(); if (node != null) { NodeList children = node.getChildNodes(); if (children != null) { int count = children.getLength(); for (int i = 0; i < count; i++) { Node child = children.item(i); if (child != null) { String type = child.getNodeName(); if (type.equalsIgnoreCase("#text") || type.equalsIgnoreCase("#comment")) { continue; } else { list.add(child); } } } } } return list; } private static class GRDXEntityResolver implements EntityResolver { @Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { if (publicId.contains("PowerGradient") || systemId.contains("grdx.dtd")) { return new InputSource(GradientParser.class.getResourceAsStream("grdx.dtd")); } else { return null; } } } private static class GRDXErrorHandler implements ErrorHandler { private final String name; public GRDXErrorHandler(String name) { this.name = name; } @Override public void error(SAXParseException e) throws SAXException { System.err.print("Warning: Failed to compile gradient set " + name + ": "); System.err.println("ERROR on "+e.getLineNumber()+":"+e.getColumnNumber()+": "+e.getMessage()); } @Override public void fatalError(SAXParseException e) throws SAXException { System.err.print("Warning: Failed to compile gradient set " + name + ": "); System.err.println("FATAL ERROR on "+e.getLineNumber()+":"+e.getColumnNumber()+": "+e.getMessage()); } @Override public void warning(SAXParseException e) throws SAXException { System.err.print("Warning: Failed to compile gradient set " + name + ": "); System.err.println("WARNING on "+e.getLineNumber()+":"+e.getColumnNumber()+": "+e.getMessage()); } } }