package com.kreative.paint.material.sprite;
import java.awt.image.BufferedImage;
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 SPNXParser {
public static SpriteSheet parse(String name, InputStream in, BufferedImage image) 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 SPNXEntityResolver());
builder.setErrorHandler(new SPNXErrorHandler(name));
Document document = builder.parse(new InputSource(in));
return parseDocument(document, image);
} catch (ParserConfigurationException pce) {
throw new IOException(pce);
} catch (SAXException saxe) {
throw new IOException(saxe);
}
}
private static SpriteSheet parseDocument(Node node, BufferedImage image) throws IOException {
String type = node.getNodeName();
if (type.equalsIgnoreCase("#document")) {
for (Node child : getChildren(node)) {
String ctype = child.getNodeName();
if (ctype.equalsIgnoreCase("sprite-sheet")) {
if (child.hasAttributes() || child.hasChildNodes()) {
return parseSpriteSheet(child, image);
}
} else {
throw new IOException("Unknown element: " + ctype);
}
}
throw new IOException("Empty document.");
} else {
throw new IOException("Unknown element: " + type);
}
}
private static SpriteSheet parseSpriteSheet(Node node, BufferedImage image) throws IOException {
String type = node.getNodeName();
if (type.equalsIgnoreCase("sprite-sheet")) {
NamedNodeMap attr = node.getAttributes();
String name = parseString(attr, "name");
int intent = parseIntent(attr, "intent");
int cols = parseInt(attr, "cols", 0);
int rows = parseInt(attr, "rows", 0);
ArrayOrdering order = parseOrder(attr, "order");
SpriteSheet sheet = new SpriteSheet(image, name, intent, cols, rows, order);
for (Node child : getChildren(node)) {
String ctype = child.getNodeName();
if (ctype.equalsIgnoreCase("slice")) {
sheet.slices.add(parseSlice(child, image));
} else if (ctype.equalsIgnoreCase("sprite-set")) {
sheet.root.children.add(parseSpriteSet(child));
} else if (ctype.equalsIgnoreCase("sprite")) {
sheet.root.children.add(parseSprite(child));
} else {
throw new IOException("Unknown element: " + ctype);
}
}
return sheet;
} else {
throw new IOException("Unknown element: " + type);
}
}
private static SpriteSheetSlice parseSlice(Node node, BufferedImage image) throws IOException {
String type = node.getNodeName();
if (type.equalsIgnoreCase("slice")) {
NamedNodeMap attr = node.getAttributes();
int w = image.getWidth();
int h = image.getHeight();
int sx = parseInt(attr, "sx", 0);
int sy = parseInt(attr, "sy", 0);
int cw = parseInt(attr, "cw", Math.min(w - sx, h - sy));
int ch = parseInt(attr, "ch", Math.min(w - sx, h - sy));
int chx = parseInt(attr, "chx", cw / 2);
int chy = parseInt(attr, "chy", ch / 2);
int cdx = parseInt(attr, "cdx", cw);
int cdy = parseInt(attr, "cdy", ch);
int cols = parseInt(attr, "cols", (w - sx + cdx - cw) / cdx);
int rows = parseInt(attr, "rows", (h - sy + cdy - ch) / cdy);
ArrayOrdering order = parseOrder(attr, "order");
ColorTransform transform = parseTransform(attr, "color-transform");
return new SpriteSheetSlice(
sx, sy, cw, ch, chx, chy, cdx, cdy,
cols, rows, order, transform
);
} else {
throw new IOException("Unknown element: " + type);
}
}
private static SpriteTreeNode.Branch parseSpriteSet(Node node) throws IOException {
String type = node.getNodeName();
if (type.equalsIgnoreCase("sprite-set")) {
NamedNodeMap attr = node.getAttributes();
String name = parseString(attr, "name");
int index = parseInt(attr, "index", 0);
int duration = parseInt(attr, "duration", 0);
SpriteTreeNode.Branch branch = new SpriteTreeNode.Branch(name, index, duration);
for (Node child : getChildren(node)) {
String ctype = child.getNodeName();
if (ctype.equalsIgnoreCase("sprite-set")) {
branch.children.add(parseSpriteSet(child));
} else if (ctype.equalsIgnoreCase("sprite")) {
branch.children.add(parseSprite(child));
} else {
throw new IOException("Unknown element: " + ctype);
}
}
return branch;
} else {
throw new IOException("Unknown element: " + type);
}
}
private static SpriteTreeNode.Leaf parseSprite(Node node) throws IOException {
String type = node.getNodeName();
if (type.equalsIgnoreCase("sprite")) {
NamedNodeMap attr = node.getAttributes();
String name = parseString(attr, "name");
int index = parseInt(attr, "index", 0);
int duration = parseInt(attr, "duration", 0);
int count = parseInt(attr, "count", 1);
return new SpriteTreeNode.Leaf(name, index, duration, count);
} else {
throw new IOException("Unknown element: " + type);
}
}
private static int parseIntent(NamedNodeMap attr, String key) {
if (attr == null) return 0;
Node node = attr.getNamedItem(key);
if (node == null) return 0;
String text = node.getTextContent();
if (text == null) return 0;
return SpriteIntent.fromString(text.trim());
}
private static ArrayOrdering parseOrder(NamedNodeMap attr, String key) {
if (attr == null) return ArrayOrdering.LTR_TTB;
Node node = attr.getNamedItem(key);
if (node == null) return ArrayOrdering.LTR_TTB;
String text = node.getTextContent();
if (text == null) return ArrayOrdering.LTR_TTB;
return ArrayOrdering.fromString(text.trim());
}
private static ColorTransform parseTransform(NamedNodeMap attr, String key) {
if (attr == null) return ColorTransform.NONE;
Node node = attr.getNamedItem(key);
if (node == null) return ColorTransform.NONE;
String text = node.getTextContent();
if (text == null) return ColorTransform.NONE;
return ColorTransform.fromString(text.trim());
}
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 SPNXEntityResolver implements EntityResolver {
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
if (publicId.contains("SpriteInfo") || systemId.contains("spnx.dtd")) {
return new InputSource(SPNXParser.class.getResourceAsStream("spnx.dtd"));
} else {
return null;
}
}
}
private static class SPNXErrorHandler implements ErrorHandler {
private final String name;
public SPNXErrorHandler(String name) {
this.name = name;
}
@Override
public void error(SAXParseException e) throws SAXException {
System.err.print("Warning: Failed to compile sprite info " + 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 sprite info " + 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 sprite info " + name + ": ");
System.err.println("WARNING on "+e.getLineNumber()+":"+e.getColumnNumber()+": "+e.getMessage());
}
}
}