/* * 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.io.xml; import static ca.gedge.opgraph.io.xml.XMLSerializerFactory.*; import java.io.IOException; import java.net.URI; 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.OutputField; import ca.gedge.opgraph.extensions.Extendable; /** * A default serializer for reading/writing {@link OpNode} to/from XML. */ public class DefaultNodeXMLSerializer implements XMLSerializer { // qualified names static final QName NODE_QNAME = new QName(DEFAULT_NAMESPACE, "node", XMLConstants.DEFAULT_NS_PREFIX); static final QName DESCRIPTION_QNAME = new QName(DEFAULT_NAMESPACE, "description", XMLConstants.DEFAULT_NS_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 OpNode)) throw new IOException(DefaultNodeXMLSerializer.class.getName() + " cannot write objects of type " + obj.getClass().getName()); // Create node element final OpNode node = (OpNode)obj; final Element nodeElem = doc.createElementNS(NODE_QNAME.getNamespaceURI(), NODE_QNAME.getLocalPart()); nodeElem.setAttribute("id", node.getId()); nodeElem.setAttribute("type", "class:" + node.getClass().getName()); if(!node.getName().equals(node.getDefaultName())) nodeElem.setAttribute("name", node.getName()); if(!node.getCategory().equals(node.getDefaultCategory())) nodeElem.setAttribute("category", node.getCategory()); if(!node.getDescription().equals(node.getDefaultDescription())) { final Element descriptionElem = doc.createElementNS(DESCRIPTION_QNAME.getNamespaceURI(), DESCRIPTION_QNAME.getLocalPart()); descriptionElem.setTextContent(node.getDescription()); nodeElem.appendChild(descriptionElem); } // Input fields for(InputField field : node.getInputFields()) { final XMLSerializer serializer = serializerFactory.getHandler(field.getClass()); if(serializer == null) throw new IOException("Cannot get handler for input field: " + field.getClass().getName()); serializer.write(serializerFactory, doc, nodeElem, field); } // Output fields for(OutputField field : node.getOutputFields()) { final XMLSerializer serializer = serializerFactory.getHandler(field.getClass()); if(serializer == null) throw new IOException("Cannot get handler for output field: " + field.getClass().getName()); serializer.write(serializerFactory, doc, nodeElem, field); } // Extensions last if(node.getExtensionClasses().size() > 0) { final XMLSerializer serializer = serializerFactory.getHandler(Extendable.class); if(serializer == null) throw new IOException("No XML serializer for extensions"); serializer.write(serializerFactory, doc, nodeElem, node); } // parentElem.appendChild(nodeElem); } @Override public Object read(XMLSerializerFactory serializerFactory, OpGraph graph, Object parent, Document doc, Element elem) throws IOException { OpNode node = null; if(NODE_QNAME.equals(XMLSerializerFactory.getQName(elem))) { // Attempt to instantiate final String type = elem.getAttribute("type"); final URI uri = URI.create(type); if(uri == null || !"class".equals(uri.getScheme())) throw new IOException("Node has unknown type: " + type); try { node = (OpNode)Class.forName(uri.getSchemeSpecificPart()).newInstance(); } catch(InstantiationException exc) { throw new IOException("Could not instantiate node of type " + type, exc); } catch(IllegalAccessException exc) { throw new IOException("Could not instantiate node of type " + type, exc); } catch(ClassNotFoundException exc) { throw new IOException("Node has unknown type " + type, exc); } catch(ClassCastException exc) { throw new IOException("Node type is not OpNode: " + type, exc); } // Set attributes if(elem.hasAttribute("id")) node.setId(elem.getAttribute("id")); if(elem.hasAttribute("name")) node.setName(elem.getAttribute("name")); if(elem.hasAttribute("category")) node.setName(elem.getAttribute("category")); // Read children final NodeList children = elem.getChildNodes(); for(int childIndex = 0; childIndex < children.getLength(); ++childIndex) { final Node childNode = children.item(childIndex); if(childNode instanceof Element) { final Element childElem = (Element)childNode; final QName name = XMLSerializerFactory.getQName(childElem); if(DESCRIPTION_QNAME.equals(name)) { node.setDescription(childElem.getTextContent()); } else { // Get a handler for the element final XMLSerializer serializer = serializerFactory.getHandler(name); if(serializer == null) throw new IOException("Could not get handler for element: " + name); // Determine what kind of element was read. If the element represented a // input/output field, add it to the node. Otherwise, the only element should // be the <extensions> element, and its serializer handles adding extensions. // final Object objRead = serializer.read(serializerFactory, graph, node, doc, childElem); if(objRead != null) { if(objRead instanceof InputField) { node.putField((InputField)objRead); } else if(objRead instanceof OutputField) { node.putField((OutputField)objRead); } } } } } } return node; } @Override public boolean handles(Class<?> cls) { return (cls == OpNode.class); } @Override public boolean handles(QName name) { return NODE_QNAME.equals(name); } }