1 /*
  2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3  *
  4  * Copyright 2014 Mike Becker. All rights reserved.
  5  *
  6  * Redistribution and use in source and binary forms, with or without
  7  * modification, are permitted provided that the following conditions are met:
  8  *
  9  *   1. Redistributions of source code must retain the above copyright
 10  *      notice, this list of conditions and the following disclaimer.
 11  *
 12  *   2. Redistributions in binary form must reproduce the above copyright
 13  *      notice, this list of conditions and the following disclaimer in the
 14  *      documentation and/or other materials provided with the distribution.
 15  *
 16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 26  * POSSIBILITY OF SUCH DAMAGE.
 27  *
 28  */
 29 
 30 package de.uapcore.sigred.doc.base;
 31 
 32 import de.uapcore.sigred.doc.Resources;
 33 import de.uapcore.sigrapi.impl.Digraph;
 34 import de.uapcore.sigrapi.impl.Graph;
 35 import de.uapcore.sigrapi.IGraph;
 36 import java.io.IOException;
 37 import java.io.InputStream;
 38 import java.io.OutputStream;
 39 import java.util.concurrent.atomic.AtomicBoolean;
 40 import java.util.concurrent.atomic.AtomicReference;
 41 import org.apache.xerces.impl.Constants;
 42 import org.dom4j.Document;
 43 import org.dom4j.DocumentException;
 44 import org.dom4j.DocumentHelper;
 45 import org.dom4j.Element;
 46 import org.dom4j.Namespace;
 47 import org.dom4j.QName;
 48 import org.dom4j.io.OutputFormat;
 49 import org.dom4j.io.SAXReader;
 50 import org.dom4j.io.XMLWriter;
 51 import org.xml.sax.ErrorHandler;
 52 import org.xml.sax.SAXException;
 53 import org.xml.sax.SAXParseException;
 54 
 55 public abstract class AbstractGraphDocument<T extends IGraph>
 56         extends FileBackedDocument {
 57     
 58     protected static final Namespace NAMESPACE = Namespace.get("sigred",
 59         "http://develop.uap-core.de/sigred/");
 60     
 61     private static final
 62         QName TAG_GRAPHDOC = QName.get("graph-document", NAMESPACE);
 63     private static final
 64         QName TAG_GRAPH = QName.get("graph", NAMESPACE);
 65     private static final
 66         QName TAG_DIGRAPH = QName.get("digraph", NAMESPACE);
 67     private static final
 68         QName TAG_METADATA = QName.get("metadata", NAMESPACE);
 69     
 70     protected final T graph;
 71     
 72     private final GraphDocumentMetadata metadata;
 73     
 74     public AbstractGraphDocument(Class<T> graphType) {
 75         T g;
 76         try {
 77             g = graphType.newInstance();
 78         } catch (ReflectiveOperationException e) {
 79             assert false;
 80             g = null; // for the compiler
 81         }
 82         graph = g;
 83         metadata = new GraphDocumentMetadata();
 84     }
 85 
 86     public T getGraph() {
 87         return graph;
 88     }
 89     
 90     public GraphDocumentMetadata getMetadata() {
 91         return metadata;
 92     }
 93 
 94     protected abstract void writeGraph(Element rootNode) throws IOException;
 95     protected abstract void readGraph(Element rootNode) throws IOException;
 96 
 97     @Override
 98     public void writeTo(OutputStream out) throws IOException {
 99         Document doc = DocumentHelper.createDocument();
100 
101         Element rootNode = doc.addElement(TAG_GRAPHDOC);
102 
103         Element metadataNode = rootNode.addElement(TAG_METADATA);
104 
105         metadata.write(metadataNode);
106 
107         if (graph instanceof Graph) {
108             writeGraph(rootNode.addElement(TAG_GRAPH));
109         } else if (graph instanceof Digraph) {
110             writeGraph(rootNode.addElement(TAG_DIGRAPH));
111         } else {
112             throw new IOException("unsupported graph type");
113         }
114 
115         XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint());
116         writer.write(doc);
117         writer.flush();
118     }
119 
120     @Override
121     public void readFrom(InputStream in) throws IOException {
122         try {
123             SAXReader reader = new SAXReader(true);
124             reader.setStripWhitespaceText(true);
125             
126             reader.setFeature(Constants.XERCES_FEATURE_PREFIX+
127                 Constants.SCHEMA_VALIDATION_FEATURE, true);
128             reader.setProperty(Constants.XERCES_PROPERTY_PREFIX +
129                 Constants.SCHEMA_LOCATION, String.format("%s %s",
130                     NAMESPACE.getURI(), Resources.class.getResource(
131                         "graph-document.xsd").toExternalForm()));
132             
133             final AtomicBoolean passed = new AtomicBoolean(true);
134             final AtomicReference<SAXParseException> xmlerror = new AtomicReference<>();
135             // TODO: we should do more detailed error handling here
136             reader.setErrorHandler(new ErrorHandler() {
137                 @Override
138                 public void warning(SAXParseException exception) throws SAXException {
139                 }
140 
141                 @Override
142                 public void error(SAXParseException exception) throws SAXException {
143                     xmlerror.set(exception);
144                     passed.set(false);
145                 }
146 
147                 @Override
148                 public void fatalError(SAXParseException exception) throws SAXException {
149                     xmlerror.set(exception);
150                     passed.set(false);
151                 }
152                 
153             });
154             Document doc = reader.read(in);
155             if (!passed.get()) {
156                 // TODO: provide details (maybe via separate error object?)
157                 throw xmlerror.get();
158             }
159             
160             doc.normalize();
161             
162             Element root = doc.getRootElement();
163             metadata.read(root.element(TAG_METADATA));
164             
165             if (graph instanceof Graph) {
166                 readGraph(root.element(TAG_GRAPH));
167             } else if (graph instanceof Digraph) {
168                 readGraph(root.element(TAG_DIGRAPH));
169             } else {
170                 throw new IOException("unsupported graph type");
171             }
172         } catch (DocumentException | SAXException ex) {
173             throw new IOException(ex);
174         }
175     }
176 }