Package org.openrdf.rio.rdfa

Source Code of org.openrdf.rio.rdfa.PrettyRDFWriter$Node

/*
* Copyright James Leigh (c) 2008.
*
* Licensed under the BSD license.
*/
package org.openrdf.rio.rdfa;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import info.aduna.xml.XMLUtil;

import org.openrdf.model.BNode;
import org.openrdf.model.Literal;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFWriter;

/**
* @author James Leigh
*/
public class PrettyRDFWriter implements RDFWriter {

  private static class Node {

    private boolean isWritten = false;

    private Value value;

    private Set<URI> types = new HashSet<URI>();

    /**
     * Creates a new Node for the supplied Value.
     */
    public Node(Value value) {
      this.value = value;
    }

    public Value getValue() {
      return value;
    }

    public void addType(URI type) {
      types.add(type);
    }

    public Set<URI> getTypes() {
      return types;
    }

    public boolean isWritten() {
      return isWritten;
    }

    public void setIsWritten(boolean isWritten) {
      this.isWritten = isWritten;
    }
  }

  /*-----------*
   * Variables *
   *-----------*/

  private String baseURI;

  private java.net.URI relativeURI;

  private boolean headerWritten;

  private boolean inHeader;

  private Map<String, String> namespaceTable;

  /**
   * Stack for remembering the nodes (subjects/objects) of statements at each
   * level.
   */
  private Stack<Node> nodeStack = new Stack<Node>();

  /*
   * We implement striped syntax by using two stacks, one for predicates and
   * one for subjects/objects.
   */

  /**
   * Stack for remembering the predicate of statements at each level.
   */
  private Stack<URI> predicateStack = new Stack<URI>();

  private RDFaMetaWriter writer;

  /*--------------*
   * Constructors *
   *--------------*/

  private boolean writingStarted;

  public PrettyRDFWriter(RDFaMetaWriter writer) {
    this.writer = writer;
    namespaceTable = new LinkedHashMap<String, String>();
    writingStarted = false;
    headerWritten = false;
  }

  /*---------*
   * Methods *
   *---------*/

  public RDFFormat getRDFFormat() {
    return RDFFormat.RDFA;
  }

  public void setBaseURI(String baseURI) {
    this.baseURI = baseURI;
    try {
      if (baseURI == null) {
        relativeURI = null;
      }
      else if (baseURI.charAt(baseURI.length() - 1) == '/') {
        relativeURI = new java.net.URI(baseURI);
      }
      else if (baseURI.lastIndexOf('/') > 0) {
        String parent = baseURI.substring(0, baseURI.lastIndexOf('/'));
        relativeURI = new java.net.URI(parent);
      }
    }
    catch (URISyntaxException e) {
      // don't use relative URIs
      relativeURI = null;
    }
  }

  public void close()
    throws IOException
  {
    try {
      if (writingStarted) {
        endRDF();
      }
    }
    catch (RDFHandlerException e) {
      if (e.getCause() instanceof IOException) {
        throw (IOException)e.getCause();
      }
      else {
        IOException ioe = new IOException(e.getMessage());
        ioe.initCause(e);
        throw ioe;
      }
    }
    finally {
      writer.close();
    }
  }

  public void startRDF() {
    if (writingStarted) {
      throw new RuntimeException("Document writing has already started");
    }
    writingStarted = true;
  }

  public void endRDF()
    throws RDFHandlerException
  {
    if (!writingStarted) {
      throw new RuntimeException("Document writing has not yet started");
    }

    try {
      if (!headerWritten) {
        header();
      }
      if (inHeader) {
        flush();
        body();
      }

      flush();

      writer.endRDF();
    }
    catch (IOException e) {
      throw new RDFHandlerException(e);
    }
    finally {
      writingStarted = false;
      headerWritten = false;
    }
  }

  public void handleComment(String comment)
    throws RDFHandlerException
  {
    try {
      if (!headerWritten) {
        header();
      }

      flush();

      writer.handleComment(comment);
    }
    catch (IOException e) {
      throw new RDFHandlerException(e);
    }
  }

  public void handleNamespace(String prefix, String name) {
    if (headerWritten) {
      // Header containing namespace declarations has already been written
      return;
    }

    if (!namespaceTable.containsKey(name)) {
      // Namespace not yet mapped to a prefix, try to give it the specified
      // prefix

      boolean isLegalPrefix = prefix.length() == 0 || XMLUtil.isNCName(prefix);

      if (!isLegalPrefix || namespaceTable.containsValue(prefix)) {
        // Specified prefix is not legal or the prefix is already in use,
        // generate a legal unique prefix

        if (prefix.length() == 0 || !isLegalPrefix) {
          prefix = "ns";
        }

        int number = 1;

        while (namespaceTable.containsValue(prefix + number)) {
          number++;
        }

        prefix += number;
      }

      namespaceTable.put(name, prefix);
    }
  }

  public void handleStatement(Statement st)
    throws RDFHandlerException
  {
    if (!writingStarted) {
      throw new RuntimeException("Document writing has not yet been started");
    }

    Resource subj = st.getSubject();
    URI pred = st.getPredicate();
    Value obj = st.getObject();

    try {
      if (!headerWritten) {
        header();
      }
      // can only be about baseURI subject
      // can't have nested BNodes in header
      if (inHeader && isAbout(subj)) {
        if (obj instanceof URI) {
          String relativize = relativize(obj.stringValue());
          writer.handleMetaLink(pred, relativize);
          return;
        }
        else if (obj instanceof Literal) {
          writer.handleMetaAttribute(pred, (Literal)obj);
          return;
        }
      }
      else if (inHeader) {
        flush();
        body();
      }

      if (!nodeStack.isEmpty() && !subj.equals(nodeStack.peek().getValue())) {
        // New subject, empty the stack
        popStacks(subj);
      }
      else if (nodeStack.size() > 1 && subj instanceof URI) {
        // New subject, empty the stack
        popStacks(subj);
      }

      // Stack is either empty or contains the same subject at top

      if (nodeStack.isEmpty()) {
        // Push subject
        nodeStack.push(new Node(subj));
      }

      // Stack now contains at least one element

      // Check if current statement is a type statement and use a typed node
      // element is possible
      if ((obj instanceof URI && RDF.TYPE.equals(pred) && !nodeStack.peek().isWritten())) {
        nodeStack.peek().addType((URI)obj);
      }
      else {
        // Push predicate and object
        predicateStack.push(pred);
        nodeStack.push(new Node(obj));
      }
    }
    catch (IOException e) {
      throw new RDFHandlerException(e);
    }
  }

  private void header()
    throws IOException
  {
    try {
      writer.startRDF(baseURI, namespaceTable);
      inHeader = true;
      writer.startMeta();
    }
    finally {
      headerWritten = true;
    }
  }

  private void body()
    throws IOException
  {
    try {
      writer.endMeta();
    }
    finally {
      inHeader = false;
    }
  }

  private boolean isAbout(Resource subj) {
    if (subj instanceof URI) {
      return subj.stringValue().equals(baseURI);
    }
    return false;
  }

  private void flush()
    throws IOException
  {
    if (!nodeStack.isEmpty()) {
      popStacks(null);
    }
  }

  /**
   * Write out the stacks until we find subject. If subject == null, write out
   * the entire stack
   *
   * @param newSubject
   */
  private void popStacks(Resource newSubject)
    throws IOException
  {
    // Write start tags for the part of the stacks that are not yet
    // written
    for (int i = 0; i < nodeStack.size() - 1; i++) {
      Node node = nodeStack.get(i);

      if (!node.isWritten()) {
        if (i > 0) {
          assert node.getValue() instanceof BNode;
          writer.openProperty((i * 2 - 1), predicateStack.get(i - 1));
        }
        openStartTag((i * 2), node.getValue(), node.getTypes());
        node.setIsWritten(true);
      }
    }

    // Write tags for the top subject
    Node topNode = nodeStack.pop();

    if (predicateStack.isEmpty()) {
      openStartTag(0, topNode.getValue(), topNode.getTypes());
      endTag(0, topNode.getValue(), topNode.getTypes());
    }
    else {
      URI topPredicate = predicateStack.pop();
      Value obj = topNode.getValue();
      int indent = nodeStack.size() * 2 - 1;
      if (obj instanceof URI) {
        String relativize = relativize(obj.stringValue());
        assert topNode.getTypes().isEmpty();
        writer.handleURI(indent, topPredicate, relativize);
      }
      else if (obj instanceof BNode && topNode.getTypes().isEmpty()) {
        writer.handleBlankNode(indent, topPredicate, (BNode)obj);
      }
      else if (obj instanceof BNode) {
        writer.openProperty(indent, topPredicate);
        writer.startBlankNode(indent, (BNode)obj, topNode.getTypes());
        writer.endBlankNode(indent, (BNode)obj, topNode.getTypes());
        writer.closeProperty(indent, topPredicate);
      }
      else if (obj instanceof Literal) {
        writer.handleLiteral(indent, topPredicate, (Literal)obj);
      }

      // Write out the end tags until we find the subject
      while (!nodeStack.isEmpty()) {
        Node nextElement = nodeStack.peek();
        if (nextElement.getValue().equals(newSubject)) {
          break;
        }
        else {
          Node node = nodeStack.pop();
          // We have already written out the subject/object,
          // but we still need to close the tag
          indent = predicateStack.size() + nodeStack.size();
          endTag(indent, nextElement.getValue(), nextElement.getTypes());
          if (predicateStack.size() > 0) {
            URI predicate = predicateStack.pop();
            indent = predicateStack.size() + nodeStack.size();
            assert node.getValue() instanceof BNode;
            writer.closeProperty(indent, predicate);
          }
        }
      }
    }
  }

  /**
   * Write out the opening tag of the subject or object of a statement up to
   * (but not including) the end of the tag. Used both in writeStartSubject and
   * writeEmptySubject.
   */
  private void openStartTag(int indent, Value subj, Set<URI> types)
    throws IOException
  {
    if (subj instanceof URI) {
      assert indent == 0;
      String uri = subj.stringValue();
      String relativize = relativize(uri);
      writer.startNode(relativize, types);
    }
    else {
      writer.startBlankNode(indent, (BNode)subj, types);
    }
  }

  /**
   * Write out the closing tag for the subject or object of a statement.
   */
  private void endTag(int indent, Value subj, Set<URI> types)
    throws IOException
  {
    if (subj instanceof URI) {
      assert indent == 0;
      String uri = subj.stringValue();
      String relativize = relativize(uri);
      writer.endNode(relativize, types);
    }
    else {
      writer.endBlankNode(indent, (BNode)subj, types);
    }
  }

  private String relativize(String stringValue) {
    if (baseURI == null) {
      return stringValue;
    }
    if (stringValue.equals(baseURI)) {
      return "";
    }
    if (!stringValue.startsWith(baseURI)) {
      return stringValue;
    }
    if ('#' == stringValue.charAt(baseURI.length())) {
      return stringValue.substring(baseURI.length());
    }
    if (relativeURI == null) {
      return stringValue;
    }
    try {
      java.net.URI uri = new java.net.URI(stringValue);
      return relativeURI.relativize(uri).toString();
    }
    catch (URISyntaxException e) {
      return stringValue;
    }
  }

}
TOP

Related Classes of org.openrdf.rio.rdfa.PrettyRDFWriter$Node

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.