Package nux.xom.io

Source Code of nux.xom.io.StaxReader$DTDInfo

/*
* 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.io;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import nu.xom.Attribute;
import nu.xom.Comment;
import nu.xom.DocType;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Node;
import nu.xom.ParentNode;
import nu.xom.ProcessingInstruction;
import nu.xom.Text;

/**
* StAX {@link XMLStreamReader} pull parser implementation that reads from an
* underlying XOM Node; typically a Document or Element (subtree); Ideal for efficient
* conversion of a XOM tree to JAXB 2, for example when incrementally converting
* XQuery results via an {@link javax.xml.bind.Unmarshaller}, perhaps in
* combination with a {@link nux.xom.xquery.StreamingPathFilter}.
* <p>
* Example usage:
*
* <pre>
*  Document doc = new Builder().build("samples/data/articles.xml");
*  Nodes results = XQueryUtil.xquery(doc, "/articles/article");
*  Unmarshaller unmarshaller = JAXBContext.newInstance(...).createUnmarshaller();
*  for (int i=0; i < results.size(); i++) {
*      Object jaxbObject = unmarshaller.unmarshall(new StaxReader(results.get(i)));
*      ... do something with the JAXB object
*  }
* </pre>
*
* @author whoschek.AT.lbl.DOT.gov
* @author $Author: hoschek3 $
* @version $Revision: 1.13 $, $Date: 2006/04/29 03:00:54 $
*/
final class StaxReader implements XMLStreamReader { // not a public class
 
  // TODO: integrate with XOMUtil.jaxbUnmarshal() if JAXB2 detected
  // TODO: find a better way to pass DocType through? perhaps via getProperty("nu.xom.DocType")
  // TODO: add handling for nu.xom.Namespace
 
  /** The root of the (sub)tree to expose via StAX. */
  private ParentNode root;
 
  /** The node the cursor is currently positioned over. */
  private Node current;
 
  /** The event the cursor is currently positioned over. */
  private int eventType;
 
  /**
   * Merge adjacent Text nodes into a single virtual text node, ala XPath.
   * Empty text nodes are ignored irrespective of this flag.
   */
  private final boolean isCoalescing;
 
  /** Text value of current CHARACTERS event; potentially coalesced. */
  private String textValue;
 
  private static final boolean DEBUG = false;
 
 
  /**
   * Constructs an instance that reads nodes from the given subtree. The
   * parser is non-validating and is text coalescing.
   *
   * @param root
   *            the root node of the subtree to read from; typically a
   *            Document or Element; can be parentless.
   *            XMLStreamConstants.START_DOCUMENT and
   *            XMLStreamConstants.END_DOCUMENT events will not be emitted if
   *            the root is an Element, i.e. a fragment. If the root is not a
   *            {@link nu.xom.ParentNode}, the XMLStreamReader's method
   *            <code>hasNext()</code> will always return <code>false</code>.
   */
  public StaxReader(Node root) {
    this(root, true);
  }

  // TODO: any need to make this public?
  private StaxReader(Node root, boolean isCoalescing) {
    if (root == null) throw new IllegalArgumentException(
      "root node must not be null");
     
    // TODO requires xom >= 1.1
//    if (root instanceof nu.xom.Namespace) throw new UnsupportedOperationException(
//      "namespace node not yet supported");
   
    this.isCoalescing = isCoalescing;
    this.current = root;
    setEventType(root);
   
    this.textValue = null;
    if (root instanceof Text) this.textValue = root.getValue();
   
    this.root = null;
    if (root instanceof ParentNode) this.root = (ParentNode) root;
  }

  /** {@inheritDoc} */
  public void close() throws XMLStreamException {
    this.root = null;
    this.current = null;
    this.textValue = null;
    this.eventType = END_DOCUMENT;
  }

  /** {@inheritDoc} */
  public int getEventType() {
    return eventType;
  }

  /** {@inheritDoc} */
  public boolean hasNext() throws XMLStreamException {
    if (root == null) return false; // root was not a ParentNode on instance construction
    int ev = getEventType();
    return current != root || (ev != END_ELEMENT && ev != END_DOCUMENT);
  }

  /** {@inheritDoc} */
  public int next() throws XMLStreamException {
    /*
     * Note that the following event types are never returned: CDATA, SPACE,
     * ENTITY_REFERENCE, ENTITY_DECLARATION, NOTATION_DECLARATION,
     * NAMESPACE. These event types are not present in the XOM object model.
     */  
    do { // Move to next node on the descendants axis
      if (!hasNext()) throw new NoSuchElementException(
          "Attempted to read beyond end of XML tree");
     
      textValue = null;
      int i = 0;
      if (isEndElement() || current.getChildCount() <= 0) {
        if (isStartElement()) {
          return setEventType(END_ELEMENT); // empty elem (zero children)
        }
       
        // move to next sibling
        i = current.getParent().indexOf(current) + 1; // fast with Nux O(1) indexOf() patch
        current = current.getParent(); // recurse up
       
        if (i >= current.getChildCount()) { // last child?
          // if we've come all the way back to the start root we're done
          int ev = (current == root && current instanceof Document) ?
              END_DOCUMENT : END_ELEMENT;
          return setEventType(ev);
        }
      }
     
      current = nextChild(i); // and recurse down
      setEventType(current);
    } while (textValue != null && textValue.length() == 0); // ignore empty Text ala XPath
   
    return getEventType();
  }
 
  private Node nextChild(int i) {
    Node node = current.getChild(i);
    if (node instanceof Text) {
      textValue = node.getValue();
      if (isCoalescing) {
        // Merge adjacent Text nodes into a single virtual text node, ala XPath
        Node n;
        int count = current.getChildCount();
        while (++i < count && ((n = current.getChild(i)) instanceof Text)) {
          node = n;
          textValue += node.getValue(); // rare case
        }
      }
    }
    return node;
  }
 
  private int setEventType(Node node) {
    int ev;
    if (node instanceof Element) {
      ev = START_ELEMENT;
    } else if (node instanceof Text) {
      ev = CHARACTERS;
    } else if (node instanceof Comment) {
      ev = COMMENT;
    } else if (node instanceof Attribute) {
      ev = ATTRIBUTE;
    } else if (node instanceof ProcessingInstruction) {
      ev = PROCESSING_INSTRUCTION;
    } else if (node instanceof DocType) {
      ev = DTD;
    } else if (node instanceof Document) {
      ev = START_DOCUMENT;
    } else {
      throw new IllegalStateException("Cannot read node type: " + node);
    }
   
    return setEventType(ev);
  }

  private int setEventType(int eventType) {
    this.eventType = eventType;
    return eventType;
  }

  /** {@inheritDoc} */
  public int nextTag() throws XMLStreamException {
    while (true) {
      int ev = next();

      switch (ev) {
        case SPACE:
        case COMMENT:
        case PROCESSING_INSTRUCTION:
          break; // skip to next event
        case CDATA:
        case CHARACTERS:
          if (!isWhiteSpace()) throwXMLStreamException(
              "Required whitespace-only CHARACTERS|CDATA");
          break; // skip to next event
        case START_ELEMENT:
        case END_ELEMENT:
          return ev;
        default:
          throwXMLStreamException("type", "START_ELEMENT|END_ELEMENT",
              toString(ev));
      }
    }
  }

  /** {@inheritDoc} */
  public boolean isStartElement() {
    return getEventType() == START_ELEMENT;
  }

  /** {@inheritDoc} */
  public boolean isEndElement() {
    return getEventType() == END_ELEMENT;
  }

  /** {@inheritDoc} */
  public boolean isCharacters() {
    return getEventType() == CHARACTERS;
  }

  /** {@inheritDoc} */
  public boolean isWhiteSpace() {
    return (isCharacters() || getEventType() == CDATA)
      && isWhitespaceOnly(getText());
  }

  private Element currentElement() {
    if (current instanceof Element) {
      return (Element) current;
    }
    throwIllegalStateException("START_ELEMENT|END_ELEMENT");
    return null; // unreachable
  }
 
  private Element currentStartElement() {
    if (getEventType() != START_ELEMENT) {
      throwIllegalStateException("START_ELEMENT");
    }
    return (Element) current;
  }
 
  private ProcessingInstruction currentPI() {
    if (current instanceof ProcessingInstruction) {
      return (ProcessingInstruction) current;
    }
    throwIllegalStateException("PROCESSING_INSTRUCTION");
    return null; // unreachable
  }
 
  private DocType currentDocType() {
    if (current instanceof DocType) {
      return (DocType) current;
    }
    throwIllegalStateException("DTD");
    return null; // unreachable
  }
 
  private Attribute currentAttribute(int index) {
    if (getEventType() == ATTRIBUTE) { // @ATTR
      return (Attribute) current;
    }
    return currentStartElement().getAttribute(index);
  }

  /** {@inheritDoc} */
  public String getLocalName() {
    return currentElement().getLocalName();
  }

  /** {@inheritDoc} */
  public String getPrefix() {
    String prefix = currentElement().getNamespacePrefix();
//    prefix = toNull(prefix);
    return prefix;
  }

  /** {@inheritDoc} */
  public String getNamespaceURI() {
    String uri = currentElement().getNamespaceURI();
//    uri = toNull(uri);
    return uri;
  }

  /** {@inheritDoc} */
  public QName getName() {
    return new QName(getNamespaceURI(), getLocalName(), getPrefix());
  }
 
  /** {@inheritDoc} */
  public int getAttributeCount() {
    if (getEventType() == ATTRIBUTE) return 1; // @ATTR
    return currentStartElement().getAttributeCount();
  }

  /** {@inheritDoc} */
  public QName getAttributeName(int index) {
    return new QName(
      getAttributeNamespace(index),
      getAttributeLocalName(index),
      getAttributePrefix(index));
  }
 
  /** {@inheritDoc} */
  public String getAttributeNamespace(int index) {
    return currentAttribute(index).getNamespaceURI();
  }

  /** {@inheritDoc} */
  public String getAttributeLocalName(int index) {
    return currentAttribute(index).getLocalName();
  }

  /** {@inheritDoc} */
  public String getAttributePrefix(int index) {
    return currentAttribute(index).getNamespacePrefix();
  }

  /** {@inheritDoc} */
  public String getAttributeType(int index) {
    Attribute.Type attrType = currentAttribute(index).getType();
    if (attrType == Attribute.Type.ENUMERATION) return "ENUMERATED"; // TODO: ???
    return attrType.getName(); // StAX spec isn't clear on what's legal here
  }

  /** {@inheritDoc} */
  public String getAttributeValue(int index) {
    return currentAttribute(index).getValue();
  }

  /** {@inheritDoc} */
  public String getAttributeValue(String namespaceURI, String localName) {
    if (localName == null)
      throw new IllegalArgumentException("localName must not be null");
   
    if (getEventType() != ATTRIBUTE) { // @ATTR
      if (namespaceURI != null) {
        return currentElement().getAttributeValue(localName, namespaceURI);       
      }
    }
   
    int count = getAttributeCount();
    for (int i=0; i < count; i++) {
      if (localName.equals(getAttributeLocalName(i))) {
        if (namespaceURI == null || namespaceURI.equals(getAttributeNamespace(i))) {
          return getAttributeValue(i);
        }
      }
    }
    return null; // not found
  }

  /** {@inheritDoc} */
  public boolean isAttributeSpecified(int index) {
    return false; // info not available from XOM
  }

  /** {@inheritDoc} */
  public String getElementText() throws XMLStreamException {
    require(START_ELEMENT, null, null);
    String str = null;
   
    while (true) {
      switch (next()) {
        case END_ELEMENT:
          return str == null ? "" : str; // we're done
        case CDATA:
        case SPACE:
        case ENTITY_REFERENCE:
        case CHARACTERS: { // accumulate strings
          String text = getText();
          str = str == null ? text : str + text;
          break;
        }
        case PROCESSING_INSTRUCTION:
        case COMMENT:
          break; // skip to next event
        case END_DOCUMENT:
          throwXMLStreamException(
            "unexpected end of document when reading element text content");
        case START_ELEMENT:
          throwXMLStreamException(
            "element text content may not contain START_ELEMENT");
        default:
          throwXMLStreamException(
            "Unexpected event type: " + toString(getEventType()));
      }       
    }
  }

  /** {@inheritDoc} */
  public int getNamespaceCount() {
    // TODO: ideally should only report bindings going into scope,
    // excluding duplicate redeclarations
    return currentElement().getNamespaceDeclarationCount();
  }

  /** {@inheritDoc} */
  public String getNamespacePrefix(int index) {
    String prefix = currentElement().getNamespacePrefix(index);
//    if (prefix.length() == 0) prefix = null; // ???
    return prefix;
  }

  /** {@inheritDoc} */
  public String getNamespaceURI(int index) {
    return getNamespaceURI(getNamespacePrefix(index));
  }

  /** {@inheritDoc} */
  public String getNamespaceURI(String prefix) {
    return getNamespaceURI(prefix, currentElement());
  }

  private static String getNamespaceURI(String prefix, Element elem) {
    if (prefix == null)
      throw new IllegalArgumentException("prefix must not be null");
   
    // "xmlns" --> "http://www.w3.org/2000/xmlns/"
    // XOM would return "", see Element.getLocalNamespaceURI(String prefix)
    if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) {
      return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
    }
    return elem.getNamespaceURI(prefix);
  }

  /** {@inheritDoc} */
  public NamespaceContext getNamespaceContext() {
    return new NamespaceContextImpl(currentElement());
  }

  /** {@inheritDoc} */
  public String getPITarget() {
    return currentPI().getTarget();
  }

  /** {@inheritDoc} */
  public String getPIData() {
    return currentPI().getValue();
  }
 
  /** {@inheritDoc} */
  public String getText() {
    switch (getEventType()) {
      case CHARACTERS: return textValue;
      case CDATA: return textValue;
      case SPACE: return textValue;
      case COMMENT: return current.getValue();
      case DTD: return currentDocType().toXML(); // TODO: ???
      default: {
        throwIllegalStateException("CHARACTERS|CDATA|SPACE|COMMENT|DTD");
        return null; // unreachable
      }
    }
  }

  /** {@inheritDoc} */
  public char[] getTextCharacters() {
    return getText().toCharArray();
  }

  /** {@inheritDoc} */
  public int getTextCharacters(int sourceStart, char[] target,
      int targetStart, int length) throws XMLStreamException {
   
    String text = getText();
    int sourceEnd = Math.min(text.length(), sourceStart + length);
    text.getChars(sourceStart, sourceEnd, target, targetStart);
    return sourceEnd - sourceStart;
  }

  /** {@inheritDoc} */
  public int getTextStart() {
    return 0;
  }

  /** {@inheritDoc} */
  public int getTextLength() {
    return getText().length();
  }

  /** {@inheritDoc} */
  public boolean hasName() {
    return isStartElement() || isEndElement();
  }

  /** {@inheritDoc} */
  public boolean hasText() {
    switch (getEventType()) {
      case CHARACTERS:
      case COMMENT:
      case SPACE:
      case CDATA:
      case DTD: // ???
      case ENTITY_REFERENCE: return true;
      default: return false;
    }
  }

  /** {@inheritDoc} */
  public String getEncoding() {
    return null;
  }

  /** {@inheritDoc} */
  public Location getLocation() {
    return new UnknownLocation();
  }

  /** {@inheritDoc} */
  public String getVersion() {
    return null;
  }

  /** {@inheritDoc} */
  public boolean isStandalone() {
    return false;
  }

  /** {@inheritDoc} */
  public boolean standaloneSet() {
    return false;
  }

  /** {@inheritDoc} */
  public String getCharacterEncodingScheme() {
    return null;
  }

  /** {@inheritDoc} */
  public Object getProperty(String name) throws IllegalArgumentException {
    // TODO: more effective lookup via a HashMap?
    if (name == null)
      throw new IllegalArgumentException("name must not be null")
   
    if (name.equals(XMLInputFactory.IS_NAMESPACE_AWARE)) {
      return Boolean.TRUE;
    }
    if (name.equals(XMLInputFactory.IS_COALESCING)) {
      return isCoalescing ? Boolean.TRUE : Boolean.FALSE;
    }
    if (name.equals(XMLInputFactory.IS_VALIDATING)) {
      return Boolean.FALSE;
    }
    if (name.equals(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES)) {
      return Boolean.TRUE; // by virtue of XOM
    }
    if (name.equals(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES)) {
      return Boolean.TRUE; // by virtue of XOM
    }
    if (name.equals(XMLInputFactory.SUPPORT_DTD)) {
      return Boolean.TRUE; // by virtue of XOM
    }
    if (name.equals(XMLInputFactory.REPORTER)) {
      return null;
    }
    if (name.equals(XMLInputFactory.RESOLVER)) {
      return null;
    }
    if (name.equals(XMLInputFactory.ALLOCATOR)) {
      return null;
    }
   
    throw new IllegalArgumentException("Unsupported property: " + name);
//    return null;
  }
 
  /**
   * The standard StAX API does not provide enough info, so this is an
   * experimental StAX extension borrowed from woodstox, to be used via pure
   * runtime reflection.
   * <p>
   * Also see StaxParser.readDocType()
   */
  public Object getDTDInfo() {
    return new DTDInfo(currentDocType());
  }

  /** {@inheritDoc} */
  public void require(int type, String namespaceURI, String localName)
      throws XMLStreamException {
   
    if (getEventType() != type) {
      throwXMLStreamException("type", toString(type), toString(getEventType()));
    }   
   
    if (localName != null && !localName.equals(getLocalName())) {
      throwXMLStreamException("localName", localName, getLocalName());
    }
   
    if (namespaceURI != null && !namespaceURI.equals(getNamespaceURI())) {
      throwXMLStreamException("namespaceURI", namespaceURI, getNamespaceURI());
    }
   
  }

  // moved slow path out of hotspot to enable better inlining
  private void throwIllegalStateException(String expected) {
    throw new IllegalStateException("Required type: " + expected
        + ", actual type: " + toString(getEventType()));   
  }
 
  private void throwXMLStreamException(String msg, String expected, String actual)
      throws XMLStreamException {
   
    msg = "Required " + msg + ":'" + expected + "', actual " + msg + ":'" + actual + "'";
    throwXMLStreamException(msg);
  }
 
  private void throwXMLStreamException(String msg) throws XMLStreamException {
    throw new XMLStreamException(msg, getLocation());
  }
 
  private static boolean isWhitespaceOnly(String str) {
    for (int i=str.length(); --i >= 0; ) {
      if (!isWhitespace(str.charAt(i))) return false;
    }
    return true;
  }
 
  /** see XML spec */
  private static boolean isWhitespace(char c) {
    switch (c) {
      case '\t': return true;
      case '\n': return true;
      case '\r': return true;
      case ' ' : return true;
      default  : return false;     
    }
  }
 
  private static String toNull(String str) {
    return (str != null && str.length() > 0) ? str : null;
  }
 
  private static String toEmpty(String str) {
    return str == null ? "" : str;
  }
 
  /** {@inheritDoc} */
  public String toString() {
    return toString(getEventType());   
  }
 
  private static String toString(int ev) {
    return StaxUtil.toString(ev);
  }
 
//  /** Fires into the given SAX ContentHandler */
//  public void convertToSAX(ContentHandler saxHandler) throws XMLStreamException {
//    new javanet.staxutils.XMLStreamReaderToContentHandler(this, saxHandler).bridge();
//  }
 
//  /** Makes this object appears as an XMLEventReader pull parser */
//  public XMLEventReader asStaxEventReader() {
//    return new javanet.staxutils.XMLStreamEventReader(this);
//    return XMLInputFactory.newInstance().createXMLEventReader(this);
//  }

 
  ///////////////////////////////////////////////////////////////////////////////
  // Nested classes:
  ///////////////////////////////////////////////////////////////////////////////
 
  private static final class NamespaceContextImpl implements NamespaceContext {
   
    private final Element element;
   
    private NamespaceContextImpl(Element element) {
      this.element = element;
    }
   
    /** {@inheritDoc} */
    public String getNamespaceURI(String prefix) {
      return StaxReader.getNamespaceURI(prefix, element);
    }
       
    /** {@inheritDoc} */
    public String getPrefix(String namespaceURI) {
      // TODO: could be implemented much more efficiently,
      Iterator iter = getPrefixes(namespaceURI);
      if (iter.hasNext()) return (String) iter.next();
      return null; // not found
    }

    /** {@inheritDoc} */
    public Iterator getPrefixes(String namespaceURI) {
      // TODO: could be implemented much more efficiently,
      // for example ala nux.xom.io.NamespacesInScope   
      if (namespaceURI == null)
        throw new IllegalArgumentException("namespaceURI must not be null");
     
      // "http://www.w3.org/XML/1998/namespace" --> "xml"
      if (namespaceURI.equals(XMLConstants.XML_NS_URI)) {
        return getSingletonIterator(XMLConstants.XML_NS_PREFIX);
      }
      // "http://www.w3.org/2000/xmlns/" --> "xmlns"
      if (namespaceURI.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
        return getSingletonIterator(XMLConstants.XMLNS_ATTRIBUTE);
      }
     
      ArrayList prefixes = new ArrayList(1);
      Element elem = element;
      do {
        int count = elem.getNamespaceDeclarationCount();
        for (int i = 0; i < count; i++) {
          String prefix = elem.getNamespacePrefix(i);
          String uri = elem.getNamespaceURI(prefix);
          if (namespaceURI.equals(uri)) {
            if (!prefixes.contains(prefix)) {
              prefixes.add(prefix);
            }
          }
        }
       
        ParentNode parent = elem.getParent();
        elem = (parent instanceof Element ? (Element) parent : null);
      } while (elem != null); // walk towards the root

      return Collections.unmodifiableList(prefixes).iterator();
    }
   
    // TODO: replace with SingletonIterator class?
    private static Iterator getSingletonIterator(String str) {
      List prefixes = Arrays.asList(new String[] {str});
      return Collections.unmodifiableList(prefixes).iterator();
    }
         
  }
 
 
  ///////////////////////////////////////////////////////////////////////////////
  // Nested classes:
  ///////////////////////////////////////////////////////////////////////////////
 
  private final class UnknownLocation implements Location {

    /** {@inheritDoc} */
    public int getLineNumber() { return -1; }
   
    /** {@inheritDoc} */
    public int getColumnNumber() { return -1; }
   
    /** {@inheritDoc} */
    public int getCharacterOffset() { return -1; }

    /** {@inheritDoc} */
    public String getPublicId() {
      // TODO: could try to find a DocType and take info from there, if available
      return null;
    }

    /** {@inheritDoc} */
    public String getSystemId() {
      String systemID = null;
      if (root != null) {
        // a baseURI isn't exactly a systemID, but better than nothing
        systemID = root.getBaseURI();
        if (systemID != null && systemID.length() == 0) systemID = null;
      }
      return systemID;
    }
  }
 
 
  ///////////////////////////////////////////////////////////////////////////////
  // Nested classes:
  ///////////////////////////////////////////////////////////////////////////////
 
  /** StAX extension hack to pass through DocType related info. */
  private static final class DTDInfo {

    private final DocType docType;

    private DTDInfo(DocType docType) {
      this.docType = docType;
    }

    public String getDTDRootName() {
      return docType.getRootElementName();
    }

    public String getDTDSystemId() {
      return docType.getSystemID();
    }

    public String getDTDPublicId() {
      return docType.getPublicID();
    }

    public String getDTDInternalSubset() {
      return docType.getInternalDTDSubset();
    }

  }

 
  ///////////////////////////////////////////////////////////////////////////////
  // Nested classes:
  ///////////////////////////////////////////////////////////////////////////////
 
//  /** Just in case user does not have javax.xml.XMLConstants on classpath. */
//  private static final class XMLConstants {
//   
//    private static final String XML_NS_PREFIX = "xml";
//    private static final String XML_NS_URI = "http://www.w3.org/XML/1998/namespace";
//       
//    private static final String XMLNS_ATTRIBUTE = "xmlns";
//    private static final String XMLNS_ATTRIBUTE_NS_URI = "http://www.w3.org/2000/xmlns/";
//   
//  }
 
}
TOP

Related Classes of nux.xom.io.StaxReader$DTDInfo

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.