Package nux.xom.xquery

Source Code of nux.xom.xquery.DefaultResultSequence$NodeInfoIdentityKey

/*
* Copyright (c) 2005, The Regents of the University of California, through
* Lawrence Berkeley National Laboratory (subject to receipt of any required
* approvals from the U.S. Dept. of Energy). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* (1) Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* (2) Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* (3) Neither the name of the University of California, Lawrence Berkeley
* National Laboratory, U.S. Dept. of Energy nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* You are under no obligation whatsoever to provide any bug fixes, patches, or
* upgrades to the features, functionality or performance of the source code
* ("Enhancements") to anyone; however, if you choose to make your Enhancements
* available either publicly, or directly to Lawrence Berkeley National
* Laboratory, without imposing a separate written license agreement for such
* Enhancements, then you hereby grant the following license: a non-exclusive,
* royalty-free perpetual license to install, use, modify, prepare derivative
* works, incorporate into other computer software, distribute, and sublicense
* such enhancements or derivative works thereof, in binary and source code
* form.
*/
package nux.xom.xquery;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;

import javax.xml.transform.TransformerException;

import net.sf.saxon.Configuration;
import net.sf.saxon.om.Axis;
import net.sf.saxon.om.AxisIterator;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.NamePool;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.om.VirtualNode;
import net.sf.saxon.type.Type;
import net.sf.saxon.type.TypeHierarchy;
import net.sf.saxon.value.AnyURIValue;
import net.sf.saxon.value.AtomicValue;
import nu.xom.Attribute;
import nu.xom.Comment;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.IllegalAddException;
import nu.xom.Node;
import nu.xom.Nodes;
import nu.xom.ProcessingInstruction;
import nu.xom.Text;

/**
* A forward-only iterator representing a Saxon XQuery result sequence of zero or more
* ordered results; allows to stream (pipeline) execution output, or to conveniently
* collect it in a batched manner.
* <p>
* Warning: Only to be used by XQuery.java
*
* @author whoschek.AT.lbl.DOT.gov
* @author $Author: hoschek $
* @version $Revision: 1.23 $, $Date: 2006/06/18 21:19:18 $
*/
final class DefaultResultSequence implements ResultSequence {
 
  /** the underlying saxon iterator producing result items */
  private SequenceIterator results;
 
  /** needed for getItemType() */
  private Configuration config;
 
  /**
   * The identities of non-XOM NodeInfos that have already been converted to
   * XOM as part of this result sequence. This helps to maintain correct node
   * identities for mixed sequences of non-XOM NodeInfos. Note that for XOM
   * NodeWrappers this map is actually not needed and hence not used.
   *
   * For example: seq := ($x, $x, root($x), $x/parent::*)
   *
   * Here the identity of "$x" and its ancestors and descendants will be the
   * same in all output (sub)trees.
   */
  private HashMap alreadyConverted = null; // lazy instantiation on demand
 
  /** enable Nux debug output on System.err? */
  private static final boolean DEBUG =
    XQueryUtil.getSystemProperty("nux.xom.xquery.XQuery.debug", false);
 
  /** strictly preserve Node identities even with non-XOM object models? */
  private static final boolean PRESERVE_IDENTITIES =
    XQueryUtil.getSystemProperty("nux.xom.xquery.XQuery.preserveIdentities", true);
 
  private static final Element ATOMIC_VALUE;
  static { // avoids reverification; also let's share strings via a template element
    ATOMIC_VALUE = new Element("item:atomic-value", "http://dsd.lbl.gov/nux");
    ATOMIC_VALUE.addAttribute(
      new Attribute("xsi:type", "http://www.w3.org/2001/XMLSchema-instance", ""));
    ATOMIC_VALUE.appendChild("");
  }
 
  DefaultResultSequence(SequenceIterator results, Configuration config) {
    this.results = results;
    this.config = config;
  }
 
  public Nodes toNodes() throws XQueryException {
    Nodes nodes = new Nodes();
    Node next;
    while ((next = next()) != null) {
      nodes.append(next);
    }
    return nodes;
  }

  public Node next() throws XQueryException {
    if (this.results != null) {
      Item item;
      try { // pull from underlying saxon iterator
        item = this.results.next();
      } catch (TransformerException e) {
        throw new XQueryException(e);
      }
      if (item != null) return convertItem(item);
      this.results = null; // iterator exhausted; help gc
      this.config = null; // help gc
      this.alreadyConverted = null; // help gc
    }
    return null;
  }
 
  /** Converts a saxon execution result node to XOM. */
  private Node convertItem(Item item) {
    Object base = item;
    while (base instanceof VirtualNode) { // unwrap XOM NodeWrapper, if any
      base = ((VirtualNode) base).getUnderlyingNode();
      if (base instanceof Node) {
        return (Node)base; // the most common case; in particular XPath
      }
    }
    if (DEBUG) System.err.println(
      "item.getClass=" + (item == null ? "null" : item.getClass().getName()));
   
    if (item instanceof NodeInfo) { // coming from XQuery node constructors or similar
      return convertTree((NodeInfo)item);
    }
    else if (item instanceof AtomicValue) {
      return convertAtomicValue((AtomicValue)item);
    }
    else { // should never happen
      throw new IllegalArgumentException(
        "Oops! Expected atomic value but found non-atomic type: " +
        (item == null ? "null" : item.getClass().getName()))
    }
  }
 
  /** Converts a saxon result node coming from XQuery node constructors to XOM. */
  private Node convertTree(NodeInfo node) {
    if (PRESERVE_IDENTITIES && this.alreadyConverted == null) {
      this.alreadyConverted = new HashMap();
    }
    Node value = convertNodeInfo(node);
    if (PRESERVE_IDENTITIES && node.getNodeKind() != Type.NAMESPACE) {
      NodeInfo root = node.getRoot();
      if (!root.isSameNodeInfo(node)) {
        if (DEBUG) System.err.println(
            "ROOT converting nodeinfo: " + root.getStringValue() + " : " + root);
        convertNodeInfo(root); // link value together with it's ancestor pointers
      }
    }
    return value;
  }
 
  /** Converts a saxon result node coming from XQuery node constructors to XOM. */
  private Node convertNodeInfo(NodeInfo node) {
    Node value;
    Object key = null;
    if (PRESERVE_IDENTITIES) {
      // if the node has already been converted return the previous conversion result.
      if (saxon86GenerateIdMethod == null) {
        key = node; // saxon >= 8.7
      } else {
        key = new NodeInfoIdentityKey(node); // saxon < 8.7   
      }
      value = (Node) this.alreadyConverted.get(key);
      if (value != null) {
        if (DEBUG) System.err.println("FOUND already converted nodeinfo: "
            + node.getStringValue() + " : " + node);
        return value;
      }
    }
    if (DEBUG) System.err.println("NEW  converting nodeinfo: " +
        node.getStringValue() + " : " + node);
   
    switch (node.getNodeKind()) {
      case Type.ATTRIBUTE:
        value = convertAttributeNodeInfo(node);
        break;
      case Type.ELEMENT:
        value = convertElementNodeInfo(node);
        break;
      case Type.DOCUMENT:
        value = convertDocumentNodeInfo(node);
        break;
      case Type.TEXT:
        value = new Text(node.getStringValue());
        break;
      case Type.COMMENT:
        value = new Comment(node.getStringValue());
        break;
      case Type.PROCESSING_INSTRUCTION:
        value = new ProcessingInstruction(node.getLocalPart(), node.getStringValue());
        break;
      case Type.NAMESPACE:
        value = convertAtomicValue(new AnyURIValue(node.getStringValue()));
//        value = new nu.xom.Namespace(node.getLocalPart(), node.getStringValue(), (Element) convertNodeInfo(node.getParent()));
        break;
      default:
        throw new IllegalArgumentException(
          "Illegal NodeInfo kind: " + node.getClass().getName());
    }
   
    if (PRESERVE_IDENTITIES) {
      this.alreadyConverted.put(key, value); // remember conversion result
    }
    return value;
  }

  private Attribute convertAttributeNodeInfo(NodeInfo node) {
    return new Attribute(
      node.getDisplayName(), node.getURI(), node.getStringValue());     
  }
 
  private Document convertDocumentNodeInfo(NodeInfo node) {
    Document doc = new Document(new Element("fakeRoot"));
    doc.setBaseURI(node.getBaseURI());
   
    boolean hasRootElement = false;
    int i = 0;
    NodeInfo next;
    AxisIterator iter = node.iterateAxis(Axis.CHILD);
    while ((next = (NodeInfo) iter.next()) != null) {
      Node child = convertNodeInfo(next);
      if (child instanceof Element) { // replace fake root with real root
        if (hasRootElement) throw new IllegalAddException(
          "A XOM document must not have more than one root element.");
        doc.setRootElement((Element) child);
        hasRootElement = true;
      } else {
        doc.insertChild(child, i);
      }
      i++;
    }
    if (!hasRootElement) throw new IllegalAddException(
      "Missing document root element; A XOM document must have a root element.");
    return doc;
  }
 
  private Element convertElementNodeInfo(NodeInfo node) {
    if (DEBUG) System.err.println("converting element=" + node.getDisplayName());
    Element elem = new Element(node.getDisplayName(), node.getURI());
    NodeInfo next;
   
    // Append attributes
    AxisIterator iter = node.iterateAxis(Axis.ATTRIBUTE);
    ArrayList prefixes = null;
    while ((next = (NodeInfo) iter.next()) != null) {
      elem.addAttribute((Attribute) convertNodeInfo(next));
     
      // keep track of attributes with prefixes, so we can avoid adding costly
      // additional namespaces declarations below. This safes potentially vast
      // amounts of memory due too XOM's expensive NS declaration storage scheme.
      String prefix = next.getPrefix();
      if (prefix.length() != 0) { // e.g. SOAP
         if (prefixes == null) prefixes = new ArrayList(1);
         prefixes.add(prefix);
      }
    }
   
    // Append namespace declarations (avoids unnecessary redeclarations).
    // For background see net.sf.saxon.om.NamespaceIterator and
    // net.sf.saxon.om.NamespaceDeclarationsImpl
    int[] namespaces = node.getDeclaredNamespaces(NodeInfo.EMPTY_NAMESPACE_LIST);
    int i = 0;
    int nsCode;
    NamePool pool = null;
    while (i < namespaces.length && (nsCode = namespaces[i]) != -1) {
      short uriCode = (short) (nsCode & 0xffff);
      if (uriCode != 0) { // it is not an undeclaration
        if (pool == null) pool = node.getNamePool();
        String uri = pool.getURIFromURICode(uriCode);
        String prefix = pool.getPrefixFromNamespaceCode(nsCode);
       
        if (prefixes != null && prefixes.contains(prefix)) {
          if (DEBUG) System.err.println(
            "Safely ignoring additional namespace declaration for prefix="
            + prefix + ", uri=" + uri);
        }
        else {
          if (DEBUG) System.err.println(
            "Adding additional namespace declaration for prefix="
            + prefix + ", uri=" + uri);
          elem.addNamespaceDeclaration(prefix, uri);
        }
      }
      i++;
    }
    prefixes = null; // help gc
    namespaces = null; // help gc
   
//    // Append namespace declarations (would introduce lots of redundant redeclarations)
//    iter = node.iterateAxis(Axis.NAMESPACE);
//    while ((next = (NodeInfo) iter.next()) != null) {
//      String prefix = next.getLocalPart();
//      String uri = next.getStringValue();
//      if (DEBUG) System.err.println("converted prefix=" + prefix + ", uri=" + uri);
//      elem.addNamespaceDeclaration(prefix, uri);
//    }
   
    // Append children (recursively)
    iter = node.iterateAxis(Axis.CHILD);
    while ((next = (NodeInfo) iter.next()) != null) {
      elem.appendChild(convertNodeInfo(next));
    }
   
    return elem;
  }
 
  /** Converts saxon's atomic value to XOM. */
  private Node convertAtomicValue(AtomicValue value) {
    if (DEBUG) System.err.println("atomicValue.getClass="+value.getClass().getName());
    Element elem = new Element(ATOMIC_VALUE)// copy to avoid reverification
    elem.getAttribute(0).setValue(getItemType(value)); // e.g. "xs:integer"
    Text text = ((Text)elem.getChild(0));
    text.setValue(value.getStringValue()); // e.g. "123"
    return elem;
  }
 
  // work-around for incompatibility introduced in saxon-8.6 and again in 8.7.1
  private static final Method saxon85Method = findGetItemTypeMethod85();
  private static final Method saxon86Method = findGetItemTypeMethod86();
 
  private String getItemType(AtomicValue value) {
    if (saxon85Method == null && saxon86Method == null) {
      try { // saxon >= 8.7.1
        return getItemType871(value);
      } catch (Throwable t) {
        throw new RuntimeException(t);
      }
    }
   
    if (saxon86Method != null) { // saxon < 8.7.1 && >= 8.6
      try {
        // value.getItemType(config.getNamePool().getTypeHierarchy()).toString();
        TypeHierarchy h = (TypeHierarchy) saxon86Method.invoke(config.getNamePool(), null);
        return value.getItemType(h).toString();
      } catch (Throwable t) {
        throw new RuntimeException(t);
      }
    }

    if (saxon85Method != null) { // saxon < 8.6
      try {
        // return value.getItemType().toString();
        return saxon85Method.invoke(value, null).toString();
      } catch (Throwable t) {
        throw new RuntimeException(t);
      }
    }
   
    throw new RuntimeException("Unsupported saxon version?");
  }
 
  // saxon >= 8.7.1
  private String getItemType871(AtomicValue value) {
    return value.getItemType(config.getTypeHierarchy()).toString();
  }
 
  // saxon >= 8.6 && < 8.7.1
  private static Method findGetItemTypeMethod86() {
    try {
      return NamePool.class.getMethod("getTypeHierarchy", null);
    } catch (Throwable t) {
      return null;
    }
  }
 
  // saxon < 8.6
  private static Method findGetItemTypeMethod85() {
    try {
      return AtomicValue.class.getMethod("getItemType", null);
    } catch (Throwable t) {
      return null;
    }
  }
 

 
  // work-around for incompatibility introduced in saxon-8.7
  private static final Method saxon86GenerateIdMethod = findGenerateIdMethod86();
 
  private static Method findGenerateIdMethod86() {
    try {
      return NodeInfo.class.getMethod("generateId", null);
    } catch (Throwable t) {
      return null;
    }
  }
 
  private static String generateId86(NodeInfo node) {
    // saxon < 8.7
//    return node.generateId();
    try {
      return saxon86GenerateIdMethod.invoke(node, null).toString();
    } catch (Throwable t) {
      throw new RuntimeException(t);
    }
  }
 
  static boolean isAtomicValue(Node node) {
    if (node instanceof Element) {
      Element elem = (Element) node;
      return elem.getLocalName().equals("atomic-value") &&
        elem.getNamespaceURI().equals("http://dsd.lbl.gov/nux");
    }
    return false;
  }
 
 
  ///////////////////////////////////////////////////////////////////////////////
  // Nested classes:
  ///////////////////////////////////////////////////////////////////////////////
 
  /** A key in the "alreadyConverted" map; saxon < 8.7 */
  private static final class NodeInfoIdentityKey {
    private final NodeInfo key;
   
    public NodeInfoIdentityKey(NodeInfo key) {
      this.key = key;
    }
   
    public boolean equals(Object other) {
      if (other instanceof NodeInfoIdentityKey) {
        return this.key.isSameNodeInfo(((NodeInfoIdentityKey)other).key);
      }
      return false;
    }
   
    public int hashCode() {
      return generateId86(this.key).hashCode();
//      return this.key.generateId().hashCode();
////      key.getDocumentNumber(), key.getNodeKind(), key.tree.nodeNr;
    }
  }
       
}
TOP

Related Classes of nux.xom.xquery.DefaultResultSequence$NodeInfoIdentityKey

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.