/*
* Copyright (C) 2012 Jason Gedge <http://www.gedge.ca>
*
* This file is part of the OpGraph project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
*
*/
package ca.gedge.opgraph.app.xml;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import ca.gedge.opgraph.InputField;
import ca.gedge.opgraph.OpGraph;
import ca.gedge.opgraph.OpNode;
import ca.gedge.opgraph.app.extensions.NodeMetadata;
import ca.gedge.opgraph.io.xml.XMLSerializer;
import ca.gedge.opgraph.io.xml.XMLSerializerFactory;
/**
*/
public class NodeMetadataXMLSerializer implements XMLSerializer {
static final String NAMESPACE = "http://gedge.ca/ns/opgraph-app";
static final String PREFIX = "oga";
static final QName META_QNAME = new QName(NAMESPACE, "meta", PREFIX);
@Override
public void write(XMLSerializerFactory serializerFactory, Document doc, Element parentElem, Object obj)
throws IOException
{
if(obj == null)
throw new IOException("Null object given to serializer");
if(!(obj instanceof NodeMetadata))
throw new IOException(NodeMetadataXMLSerializer.class.getName() + " cannot write objects of type " + obj.getClass().getName());
// setup namespace for document
final Element rootEle = doc.getDocumentElement();
rootEle.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI,
XMLConstants.XMLNS_ATTRIBUTE + ":" + PREFIX, NAMESPACE);
// Create metadata element
final NodeMetadata meta = (NodeMetadata)obj;
final Element metaElem = doc.createElementNS(NAMESPACE, PREFIX + ":" + META_QNAME.getLocalPart());
metaElem.setAttribute("x", Integer.toString(meta.getX()));
metaElem.setAttribute("y", Integer.toString(meta.getY()));
// Create elements for default values
for(Map.Entry<InputField, Object> entry : meta.getDefaults().entrySet()) {
final InputField field = entry.getKey();
final Object value = entry.getValue();
final Element defaultElem = doc.createElementNS(NAMESPACE, PREFIX + ":default");
defaultElem.setAttribute("for", field.getKey());
defaultElem.setAttribute("type", value.getClass().getName());
defaultElem.setTextContent(value.toString());
metaElem.appendChild(defaultElem);
}
parentElem.appendChild(metaElem);
}
@Override
public Object read(XMLSerializerFactory serializerFactory, OpGraph graph, Object parent, Document doc, Element elem)
throws IOException
{
if(META_QNAME.equals(XMLSerializerFactory.getQName(elem))) {
// Try to get the parent node
if(parent == null || !(parent instanceof OpNode))
throw new IOException("Node metadata requires parent node");
final OpNode node = (OpNode)parent;
// Read metadata
final NodeMetadata meta = new NodeMetadata();
meta.setX(Integer.parseInt(elem.getAttribute("x")));
meta.setY(Integer.parseInt(elem.getAttribute("y")));
// Read in defaults
final NodeList children = elem.getChildNodes();
for(int childIndex = 0; childIndex < children.getLength(); ++childIndex) {
final Node defaultNode = children.item(childIndex);
if(defaultNode instanceof Element) {
final Element defaultElem = (Element) defaultNode;
final String fieldKey = defaultElem.getAttribute("for");
final InputField field = node.getInputFieldWithKey(fieldKey);
if(field == null)
throw new Error("Default value references unknown input field: " + fieldKey);
final String valueTypeClassName = defaultElem.getAttribute("type");
final String valueString = defaultElem.getTextContent();
try {
boolean parsed = false;
final Class<?> valueClass = Class.forName(valueTypeClassName);
for(Method method : valueClass.getMethods()) {
if(/*valueClass.isAssignableFrom(method.getReturnType())
&&*/ Modifier.isStatic(method.getModifiers())
&& method.getParameterTypes().length == 1
&& method.getParameterTypes()[0] == String.class
&& method.getName().startsWith("parse"))
{
meta.setDefault(field, method.invoke(null, valueString));
parsed = true;
break;
}
}
if(!parsed)
throw new Error("Couldn't parse default value");
} catch(ClassNotFoundException exc) {
throw new Error("Unknown default value type: " + valueTypeClassName);
} catch(IllegalArgumentException exc) {
throw new Error("Couldn't parse default value", exc);
} catch(IllegalAccessException exc) {
throw new Error("Couldn't parse default value", exc);
} catch(InvocationTargetException exc) {
throw new Error("Couldn't parse default value", exc);
}
}
}
node.putExtension(NodeMetadata.class, meta);
}
return null;
}
@Override
public boolean handles(Class<?> cls) {
return (cls == NodeMetadata.class);
}
@Override
public boolean handles(QName name) {
return META_QNAME.equals(name);
}
}