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