universe@25: /* universe@25: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. universe@25: * universe@25: * Copyright 2014 Mike Becker. All rights reserved. universe@25: * universe@25: * Redistribution and use in source and binary forms, with or without universe@25: * modification, are permitted provided that the following conditions are met: universe@25: * universe@25: * 1. Redistributions of source code must retain the above copyright universe@25: * notice, this list of conditions and the following disclaimer. universe@25: * universe@25: * 2. Redistributions in binary form must reproduce the above copyright universe@25: * notice, this list of conditions and the following disclaimer in the universe@25: * documentation and/or other materials provided with the distribution. universe@25: * universe@25: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" universe@25: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE universe@25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE universe@25: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE universe@25: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR universe@25: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF universe@25: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS universe@25: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN universe@25: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) universe@25: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE universe@25: * POSSIBILITY OF SUCH DAMAGE. universe@25: * universe@25: */ universe@25: universe@25: package de.uapcore.sigred.doc.base; universe@25: universe@25: import de.uapcore.sigred.doc.Resources; universe@25: import de.uapcore.sigrapi.impl.Digraph; universe@25: import de.uapcore.sigrapi.impl.Graph; universe@25: import de.uapcore.sigrapi.IGraph; universe@25: import java.io.IOException; universe@25: import java.io.InputStream; universe@25: import java.io.OutputStream; universe@25: import java.util.concurrent.atomic.AtomicBoolean; universe@25: import java.util.concurrent.atomic.AtomicReference; universe@25: import org.apache.xerces.impl.Constants; universe@25: import org.dom4j.Document; universe@25: import org.dom4j.DocumentException; universe@25: import org.dom4j.DocumentHelper; universe@25: import org.dom4j.Element; universe@25: import org.dom4j.Namespace; universe@25: import org.dom4j.QName; universe@25: import org.dom4j.io.OutputFormat; universe@25: import org.dom4j.io.SAXReader; universe@25: import org.dom4j.io.XMLWriter; universe@25: import org.xml.sax.ErrorHandler; universe@25: import org.xml.sax.SAXException; universe@25: import org.xml.sax.SAXParseException; universe@25: universe@25: public abstract class AbstractGraphDocument universe@25: extends FileBackedDocument { universe@25: universe@25: protected static final Namespace NAMESPACE = Namespace.get("sigred", universe@25: "http://develop.uap-core.de/sigred/"); universe@25: universe@25: private static final universe@25: QName TAG_GRAPHDOC = QName.get("graph-document", NAMESPACE); universe@25: private static final universe@25: QName TAG_GRAPH = QName.get("graph", NAMESPACE); universe@25: private static final universe@25: QName TAG_DIGRAPH = QName.get("digraph", NAMESPACE); universe@25: private static final universe@25: QName TAG_METADATA = QName.get("metadata", NAMESPACE); universe@25: universe@25: protected final T graph; universe@25: universe@25: private final GraphDocumentMetadata metadata; universe@25: universe@25: public AbstractGraphDocument(Class graphType) { universe@25: T g; universe@25: try { universe@25: g = graphType.newInstance(); universe@25: } catch (ReflectiveOperationException e) { universe@25: assert false; universe@25: g = null; // for the compiler universe@25: } universe@25: graph = g; universe@25: metadata = new GraphDocumentMetadata(); universe@25: } universe@25: universe@25: public T getGraph() { universe@25: return graph; universe@25: } universe@25: universe@25: public GraphDocumentMetadata getMetadata() { universe@25: return metadata; universe@25: } universe@25: universe@25: protected abstract void writeGraph(Element rootNode) throws IOException; universe@25: protected abstract void readGraph(Element rootNode) throws IOException; universe@25: universe@25: @Override universe@25: public void writeTo(OutputStream out) throws IOException { universe@25: Document doc = DocumentHelper.createDocument(); universe@25: universe@25: Element rootNode = doc.addElement(TAG_GRAPHDOC); universe@25: universe@25: Element metadataNode = rootNode.addElement(TAG_METADATA); universe@25: universe@25: metadata.write(metadataNode); universe@25: universe@25: if (graph instanceof Graph) { universe@25: writeGraph(rootNode.addElement(TAG_GRAPH)); universe@25: } else if (graph instanceof Digraph) { universe@25: writeGraph(rootNode.addElement(TAG_DIGRAPH)); universe@25: } else { universe@25: throw new IOException("unsupported graph type"); universe@25: } universe@25: universe@25: XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint()); universe@25: writer.write(doc); universe@25: writer.flush(); universe@25: } universe@25: universe@25: @Override universe@25: public void readFrom(InputStream in) throws IOException { universe@25: try { universe@25: SAXReader reader = new SAXReader(true); universe@25: reader.setStripWhitespaceText(true); universe@25: universe@25: reader.setFeature(Constants.XERCES_FEATURE_PREFIX+ universe@25: Constants.SCHEMA_VALIDATION_FEATURE, true); universe@25: reader.setProperty(Constants.XERCES_PROPERTY_PREFIX + universe@25: Constants.SCHEMA_LOCATION, String.format("%s %s", universe@25: NAMESPACE.getURI(), Resources.class.getResource( universe@25: "graph-document.xsd").toExternalForm())); universe@25: universe@25: final AtomicBoolean passed = new AtomicBoolean(true); universe@25: final AtomicReference xmlerror = new AtomicReference<>(); universe@25: // TODO: we should do more detailed error handling here universe@25: reader.setErrorHandler(new ErrorHandler() { universe@25: @Override universe@25: public void warning(SAXParseException exception) throws SAXException { universe@25: } universe@25: universe@25: @Override universe@25: public void error(SAXParseException exception) throws SAXException { universe@25: xmlerror.set(exception); universe@25: passed.set(false); universe@25: } universe@25: universe@25: @Override universe@25: public void fatalError(SAXParseException exception) throws SAXException { universe@25: xmlerror.set(exception); universe@25: passed.set(false); universe@25: } universe@25: universe@25: }); universe@25: Document doc = reader.read(in); universe@25: if (!passed.get()) { universe@25: // TODO: provide details (maybe via separate error object?) universe@25: throw xmlerror.get(); universe@25: } universe@25: universe@25: doc.normalize(); universe@25: universe@25: Element root = doc.getRootElement(); universe@25: metadata.read(root.element(TAG_METADATA)); universe@25: universe@25: if (graph instanceof Graph) { universe@25: readGraph(root.element(TAG_GRAPH)); universe@25: } else if (graph instanceof Digraph) { universe@25: readGraph(root.element(TAG_DIGRAPH)); universe@25: } else { universe@25: throw new IOException("unsupported graph type"); universe@25: } universe@25: } catch (DocumentException | SAXException ex) { universe@25: throw new IOException(ex); universe@25: } universe@25: } universe@25: }