All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.java.com.ctc.wstx.dom.DOMWrappingReader Maven / Gradle / Ivy

There is a newer version: 4.0.6
Show newest version
/* Woodstox XML processor
 *
 * Copyright (c) 2004- Tatu Saloranta, [email protected]
 *
 * Licensed under the License specified in the file LICENSE which is
 * included with the source code.
 * You may not use this file except in compliance with the License.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.ctc.wstx.dom;

import java.io.IOException;
import java.io.Writer;
import java.text.MessageFormat;
import java.util.*;

import javax.xml.transform.dom.DOMSource;

import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.*;

import org.w3c.dom.*;

import org.codehaus.stax2.AttributeInfo;
import org.codehaus.stax2.DTDInfo;
import org.codehaus.stax2.LocationInfo;
import org.codehaus.stax2.XMLInputFactory2;
import org.codehaus.stax2.XMLStreamLocation2;
import org.codehaus.stax2.XMLStreamReader2;
import org.codehaus.stax2.validation.DTDValidationSchema;
import org.codehaus.stax2.validation.ValidationProblemHandler;
import org.codehaus.stax2.validation.XMLValidationSchema;
import org.codehaus.stax2.validation.XMLValidator;

import com.ctc.wstx.api.ReaderConfig;
import com.ctc.wstx.cfg.ErrorConsts;
import com.ctc.wstx.exc.WstxParsingException;
import com.ctc.wstx.io.WstxInputLocation;
import com.ctc.wstx.util.EmptyIterator;
import com.ctc.wstx.util.EmptyNamespaceContext;
import com.ctc.wstx.util.SingletonIterator;
import com.ctc.wstx.util.TextAccumulator;

/**
 * This is an adapter class that presents a DOM document as if it was
 * a regular {@link XMLStreamReader}.
 *

* Note that the implementation is only to be used for use with * javax.xml.transform.dom.DOMSource. It can however be * used for both full documents, and single element root fragments, * depending on what node is passed as the argument. *

* Some notes regarding missing/incomplete functionality: *

    *
  • DOM does not seem to have access to information from the XML * declaration (although Document node can be viewed as representing * it). Consequently, all accessors return no information (version, * encoding, standalone). *
  • *
  • No location info is provided, since (you guessed it!) DOM * does not provide that info. *
  • *
*/ public class DOMWrappingReader implements XMLStreamReader2, DTDInfo, LocationInfo, NamespaceContext, XMLStreamConstants { // // // Bit masks used for quick type comparisons final private static int MASK_GET_TEXT = (1 << CHARACTERS) | (1 << CDATA) | (1 << SPACE) | (1 << COMMENT) | (1 << DTD) | (1 << ENTITY_REFERENCE); final private static int MASK_GET_ELEMENT_TEXT = (1 << CHARACTERS) | (1 << CDATA) | (1 << SPACE) | (1 << ENTITY_REFERENCE); // // // Configuration: protected final ReaderConfig mConfig; protected final String mSystemId; protected final Node mRootNode; protected final boolean mNsAware; /** * Whether stream reader is to coalesce adjacent textual * (CHARACTERS, SPACE, CDATA) events (as per property * {@link XMLInputFactory#IS_COALESCING}) or not */ protected final boolean mCoalescing; // // // State: protected int mCurrEvent = START_DOCUMENT; /** * Current node is the DOM node that contains information * regarding the current event. */ protected Node mCurrNode; protected int mDepth = 0; /** * In coalescing mode, we may need to combine textual content * from multiple adjacent nodes. Since we shouldn't be modifying * the underlying DOM tree, need to accumulate it into a temporary * variable */ protected String mCoalescedText; /** * Helper object used for combining segments of text as needed */ protected TextAccumulator mTextBuffer = new TextAccumulator(); // // // Attribute/namespace declaration state /* DOM, alas, does not distinguish between namespace declarations * and attributes (due to its roots prior to XML namespaces?). * Because of this, two lists need to be separated. Since this * information is often not needed, it will be lazily generated. */ /** * Lazily instantiated List of all actual attributes for the * current (start) element, NOT including namespace declarations. * As such, elements are {@link org.w3c.dom.Attr} instances. *

*/ protected List mAttrList = null; /** * Lazily instantiated String pairs of all namespace declarations for the * current (start/end) element. String pair means that for each * declarations there are two Strings in the list: first one is prefix * (empty String for the default namespace declaration), and second * URI it is bound to. */ protected List mNsDeclList = null; /* //////////////////////////////////////////////////// // Construction //////////////////////////////////////////////////// */ /** * @param cfg Configuration of this reader * @param treeRoot Node that is the tree of the DOM document, or * fragment. */ private DOMWrappingReader(ReaderConfig cfg, Node treeRoot, String sysId) throws XMLStreamException { if (treeRoot == null) { throw new IllegalArgumentException("Can not pass null Node for constructing a DOM-based XMLStreamReader"); } mConfig = cfg; mNsAware = cfg.willSupportNamespaces(); mCoalescing = cfg.willCoalesceText(); mSystemId = sysId; /* Ok; we need a document node; or an element node; or a document * fragment node. */ switch (treeRoot.getNodeType()) { case Node.DOCUMENT_NODE: // fine /* Should try to find encoding, version and stand-alone * settings... but is there a standard way of doing that? */ case Node.ELEMENT_NODE: // can make sub-tree... ok // But should we skip START/END_DOCUMENT? For now, let's not case Node.DOCUMENT_FRAGMENT_NODE: // as with element... // Above types are fine break; default: // other Nodes not usable throw new XMLStreamException("Can not create an XMLStreamReader for a DOM node of type "+treeRoot.getClass()); } mRootNode = mCurrNode = treeRoot; } public static DOMWrappingReader createFrom(ReaderConfig cfg, DOMSource src) throws XMLStreamException { Node rootNode = src.getNode(); String systemId = src.getSystemId(); return new DOMWrappingReader(cfg, rootNode, systemId); } /* //////////////////////////////////////////////////// // XMLStreamReader, document info //////////////////////////////////////////////////// */ /** * As per Stax (1.0) specs, needs to return whatever xml declaration * claimed encoding is, if any; or null if no xml declaration found. */ public String getCharacterEncodingScheme() { /* No standard way to figure it out from a DOM Document node; * have to return null */ return null; } /** * As per Stax (1.0) specs, needs to return whatever parser determined * the encoding was, if it was able to figure it out. If not (there are * cases where this can not be found; specifically when being passed a * {@link java.io.Reader}), it should return null. */ public String getEncoding() { /* We have no information regarding underlying stream/Reader, so * best we can do is to see if we know xml declaration encoding. */ return getCharacterEncodingScheme(); } public String getVersion() { /* No standard way to figure it out from a DOM Document node; * have to return null */ return null; } public boolean isStandalone() { /* No standard way to figure it out from a DOM Document node; * have to return false */ return false; } public boolean standaloneSet() { /* No standard way to figure it out from a DOM Document node; * have to return false */ return false; } /* //////////////////////////////////////////////////// // Public API, configuration //////////////////////////////////////////////////// */ public Object getProperty(String name) { if (name.equals("javax.xml.stream.entities")) { // !!! TBI return Collections.EMPTY_LIST; } if (name.equals("javax.xml.stream.notations")) { // !!! TBI return Collections.EMPTY_LIST; } // [WSTX-162]: no way to cleanly enable name/nsURI interning if (XMLInputFactory2.P_INTERN_NAMES.equals(name) || XMLInputFactory2.P_INTERN_NS_URIS.equals(name)) { return Boolean.FALSE; } return mConfig.getProperty(name); } /* //////////////////////////////////////////////////// // XMLStreamReader, current state //////////////////////////////////////////////////// */ // // // Attribute access: public int getAttributeCount() { if (mCurrEvent != START_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_STELEM); } if (mAttrList == null) { calcNsAndAttrLists(true); } return mAttrList.size(); } public String getAttributeLocalName(int index) { if (mCurrEvent != START_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_STELEM); } if (mAttrList == null) { calcNsAndAttrLists(true); } if (index >= mAttrList.size() || index < 0) { handleIllegalAttrIndex(index); return null; } Attr attr = (Attr) mAttrList.get(index); return safeGetLocalName(attr); } public QName getAttributeName(int index) { if (mCurrEvent != START_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_STELEM); } if (mAttrList == null) { calcNsAndAttrLists(true); } if (index >= mAttrList.size() || index < 0) { handleIllegalAttrIndex(index); return null; } Attr attr = (Attr) mAttrList.get(index); return constructQName(attr.getNamespaceURI(), safeGetLocalName(attr), attr.getPrefix()); } public String getAttributeNamespace(int index) { if (mCurrEvent != START_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_STELEM); } if (mAttrList == null) { calcNsAndAttrLists(true); } if (index >= mAttrList.size() || index < 0) { handleIllegalAttrIndex(index); return null; } Attr attr = (Attr) mAttrList.get(index); return attr.getNamespaceURI(); } public String getAttributePrefix(int index) { if (mCurrEvent != START_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_STELEM); } if (mAttrList == null) { calcNsAndAttrLists(true); } if (index >= mAttrList.size() || index < 0) { handleIllegalAttrIndex(index); return null; } Attr attr = (Attr) mAttrList.get(index); return attr.getPrefix(); } public String getAttributeType(int index) { if (mCurrEvent != START_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_STELEM); } if (mAttrList == null) { calcNsAndAttrLists(true); } if (index >= mAttrList.size() || index < 0) { handleIllegalAttrIndex(index); return null; } //Attr attr = (Attr) mAttrList.get(index); // First, a special case, ID... since it's potentially most useful /* 26-Apr-2006, TSa: Turns out that following methods are * DOM Level3, and as such not available in JDK 1.4 and prior. * Thus, let's not yet use them (could use dynamic discovery * for graceful downgrade) */ /* if (attr.isId()) { return "ID"; } TypeInfo schemaType = attr.getSchemaTypeInfo(); return (schemaType == null) ? "CDATA" : schemaType.getTypeName(); */ return "CDATA"; } public String getAttributeValue(int index) { if (mCurrEvent != START_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_STELEM); } if (mAttrList == null) { calcNsAndAttrLists(true); } if (index >= mAttrList.size() || index < 0) { handleIllegalAttrIndex(index); return null; } Attr attr = (Attr) mAttrList.get(index); return attr.getValue(); } public String getAttributeValue(String nsURI, String localName) { if (mCurrEvent != START_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_STELEM); } Element elem = (Element) mCurrNode; NamedNodeMap attrs = elem.getAttributes(); /* Hmmh. DOM javadocs claim "Per [XML Namespaces], applications * must use the value null as the namespaceURI parameter for methods * if they wish to have no namespace.". * Not sure how true that is, but: */ if (nsURI != null && nsURI.length() == 0) { nsURI = null; } Attr attr = (Attr) attrs.getNamedItemNS(nsURI, localName); return (attr == null) ? null : attr.getValue(); } /** * From StAX specs: *

* Reads the content of a text-only element, an exception is thrown if * this is not a text-only element. * Regardless of value of javax.xml.stream.isCoalescing this method always * returns coalesced content. *
Precondition: the current event is START_ELEMENT. *
Postcondition: the current event is the corresponding END_ELEMENT. *
*/ public String getElementText() throws XMLStreamException { if (mCurrEvent != START_ELEMENT) { throwParseError(ErrorConsts.ERR_STATE_NOT_STELEM); } /** * Need to loop to get rid of PIs, comments */ while (true) { int type = next(); if (type == END_ELEMENT) { break; } if (type == COMMENT || type == PROCESSING_INSTRUCTION) { continue; } if (((1 << type) & MASK_GET_ELEMENT_TEXT) == 0) { throwParseError("Expected a text token, got "+ErrorConsts.tokenTypeDesc(type)+"."); } mTextBuffer.addText(getText()); } return mTextBuffer.getAndClear(); } /** * Returns type of the last event returned; or START_DOCUMENT before * any events has been explicitly returned. */ public int getEventType() { return mCurrEvent; } public String getLocalName() { if (mCurrEvent == START_ELEMENT || mCurrEvent == END_ELEMENT) { return safeGetLocalName(mCurrNode); } if (mCurrEvent == ENTITY_REFERENCE) { return mCurrNode.getNodeName(); } throw new IllegalStateException("Current state ("+ErrorConsts.tokenTypeDesc(mCurrEvent)+") not START_ELEMENT, END_ELEMENT or ENTITY_REFERENCE"); } // // // getLocation() defined in StreamScanner public QName getName() { if (mCurrEvent != START_ELEMENT && mCurrEvent != END_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_ELEM); } return constructQName(mCurrNode.getNamespaceURI(), safeGetLocalName(mCurrNode), mCurrNode.getPrefix()); } // // // Namespace access public NamespaceContext getNamespaceContext() { return this; } public int getNamespaceCount() { if (mCurrEvent != START_ELEMENT && mCurrEvent != END_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_ELEM); } if (mNsDeclList == null) { if (!mNsAware) { return 0; } calcNsAndAttrLists(mCurrEvent == START_ELEMENT); } return mNsDeclList.size() / 2; } /** * Alas, DOM does not expose any of information necessary for * determining actual declarations. Thus, have to indicate that * there are no declarations. */ public String getNamespacePrefix(int index) { if (mCurrEvent != START_ELEMENT && mCurrEvent != END_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_ELEM); } if (mNsDeclList == null) { if (!mNsAware) { handleIllegalNsIndex(index); } calcNsAndAttrLists(mCurrEvent == START_ELEMENT); } if (index < 0 || (index + index) >= mNsDeclList.size()) { handleIllegalNsIndex(index); } return (String) mNsDeclList.get(index + index); } public String getNamespaceURI() { if (mCurrEvent != START_ELEMENT && mCurrEvent != END_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_ELEM); } return mCurrNode.getNamespaceURI(); } public String getNamespaceURI(int index) { if (mCurrEvent != START_ELEMENT && mCurrEvent != END_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_ELEM); } if (mNsDeclList == null) { if (!mNsAware) { handleIllegalNsIndex(index); } calcNsAndAttrLists(mCurrEvent == START_ELEMENT); } if (index < 0 || (index + index) >= mNsDeclList.size()) { handleIllegalNsIndex(index); } return (String) mNsDeclList.get(index + index + 1); } // Note: implemented as part of NamespaceContext //public String getNamespaceURI(String prefix) public String getPIData() { if (mCurrEvent != PROCESSING_INSTRUCTION) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_PI); } return mCurrNode.getNodeValue(); } public String getPITarget() { if (mCurrEvent != PROCESSING_INSTRUCTION) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_PI); } return mCurrNode.getNodeName(); } public String getPrefix() { if (mCurrEvent != START_ELEMENT && mCurrEvent != END_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_ELEM); } return mCurrNode.getPrefix(); } public String getText() { if (mCoalescedText != null) { return mCoalescedText; } if (((1 << mCurrEvent) & MASK_GET_TEXT) == 0) { throwNotTextual(mCurrEvent); } return mCurrNode.getNodeValue(); } public char[] getTextCharacters() { String text = getText(); return text.toCharArray(); } public int getTextCharacters(int sourceStart, char[] target, int targetStart, int len) { if (((1 << mCurrEvent) & MASK_GET_TEXT) == 0) { throwNotTextual(mCurrEvent); } String text = getText(); if (len > text.length()) { len = text.length(); } text.getChars(sourceStart, sourceStart+len, target, targetStart); return len; } public int getTextLength() { if (((1 << mCurrEvent) & MASK_GET_TEXT) == 0) { throwNotTextual(mCurrEvent); } return getText().length(); } public int getTextStart() { if (((1 << mCurrEvent) & MASK_GET_TEXT) == 0) { throwNotTextual(mCurrEvent); } return 0; } public boolean hasName() { return (mCurrEvent == START_ELEMENT) || (mCurrEvent == END_ELEMENT); } public boolean hasNext() { return (mCurrEvent != END_DOCUMENT); } public boolean hasText() { return (((1 << mCurrEvent) & MASK_GET_TEXT) != 0); } public boolean isAttributeSpecified(int index) { if (mCurrEvent != START_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_STELEM); } Element elem = (Element) mCurrNode; Attr attr = (Attr) elem.getAttributes().item(index); if (attr == null) { handleIllegalAttrIndex(index); return false; } return attr.getSpecified(); } public boolean isCharacters() { return (mCurrEvent == CHARACTERS); } public boolean isEndElement() { return (mCurrEvent == END_ELEMENT); } public boolean isStartElement() { return (mCurrEvent == START_ELEMENT); } /** *

* 05-Apr-2004, TSa: Could try to determine status when text is actually * read. That'd prevent double reads... but would it slow down that * one reading so that net effect would be negative? */ public boolean isWhiteSpace() { if (mCurrEvent == CHARACTERS || mCurrEvent == CDATA) { String text = getText(); for (int i = 0, len = text.length(); i < len; ++i) { /* !!! If xml 1.1 was to be handled, should check for * LSEP and NEL too */ if (text.charAt(i) > 0x0020) { return false; } } return true; } return (mCurrEvent == SPACE); } public void require(int type, String nsUri, String localName) throws XMLStreamException { int curr = mCurrEvent; /* There are some special cases; specifically, SPACE and CDATA * are sometimes reported as CHARACTERS. Let's be lenient by * allowing both 'real' and reported types, for now. */ if (curr != type) { if (curr == CDATA) { curr = CHARACTERS; } else if (curr == SPACE) { curr = CHARACTERS; } } if (type != curr) { throwParseError("Expected type "+ErrorConsts.tokenTypeDesc(type) +", current type " +ErrorConsts.tokenTypeDesc(curr)); } if (localName != null) { if (curr != START_ELEMENT && curr != END_ELEMENT && curr != ENTITY_REFERENCE) { throwParseError("Expected non-null local name, but current token not a START_ELEMENT, END_ELEMENT or ENTITY_REFERENCE (was "+ErrorConsts.tokenTypeDesc(mCurrEvent)+")"); } String n = getLocalName(); if (n != localName && !n.equals(localName)) { throwParseError("Expected local name '"+localName+"'; current local name '"+n+"'."); } } if (nsUri != null) { if (curr != START_ELEMENT && curr != END_ELEMENT) { throwParseError("Expected non-null NS URI, but current token not a START_ELEMENT or END_ELEMENT (was "+ErrorConsts.tokenTypeDesc(curr)+")"); } String uri = getNamespaceURI(); // No namespace? if (nsUri.length() == 0) { if (uri != null && uri.length() > 0) { throwParseError("Expected empty namespace, instead have '"+uri+"'."); } } else { if ((nsUri != uri) && !nsUri.equals(uri)) { throwParseError("Expected namespace '"+nsUri+"'; have '" +uri+"'."); } } } // Ok, fine, all's good } /* //////////////////////////////////////////////////// // XMLStreamReader, iterating //////////////////////////////////////////////////// */ public int next() throws XMLStreamException { mCoalescedText = null; /* For most events, we just need to find the next sibling; and * that failing, close the parent element. But there are couple * of special cases, which are handled first: */ switch (mCurrEvent) { case START_DOCUMENT: // initial state /* What to do here depends on what kind of node we started * with... */ switch (mCurrNode.getNodeType()) { case Node.DOCUMENT_NODE: case Node.DOCUMENT_FRAGMENT_NODE: // For doc, fragment, need to find first child mCurrNode = mCurrNode.getFirstChild(); break; case Node.ELEMENT_NODE: // For element, curr node is fine: return (mCurrEvent = START_ELEMENT); default: throw new XMLStreamException("Internal error: unexpected DOM root node type "+mCurrNode.getNodeType()+" for node '"+mCurrNode+"'"); } break; case END_DOCUMENT: // end reached: should not call! throw new java.util.NoSuchElementException("Can not call next() after receiving END_DOCUMENT"); case START_ELEMENT: // element returned, need to traverse children, if any ++mDepth; mAttrList = null; // so it will not get reused accidentally { Node firstChild = mCurrNode.getFirstChild(); if (firstChild == null) { // empty? need to return virtual END_ELEMENT /* Note: need not clear namespace declarations, because * it'll be the same as for the start elem! */ return (mCurrEvent = END_ELEMENT); } mNsDeclList = null; /* non-empty is easy: let's just swap curr node, and * fall through to regular handling */ mCurrNode = firstChild; break; } case END_ELEMENT: --mDepth; // Need to clear these lists mAttrList = null; mNsDeclList = null; /* One special case: if we hit the end of children of * the root element (when tree constructed with Element, * instead of Document or DocumentFragment). If so, it'll * be END_DOCUMENT: */ if (mCurrNode == mRootNode) { return (mCurrEvent = END_DOCUMENT); } // Otherwise need to fall through to default handling: default: /* For anything else, we can and should just get the * following sibling. */ { Node next = mCurrNode.getNextSibling(); // If sibling, let's just assign and fall through if (next != null) { mCurrNode = next; break; } /* Otherwise, need to climb up the stack and either * return END_ELEMENT (if parent is element) or * END_DOCUMENT (if not; needs to be root, then) */ mCurrNode = mCurrNode.getParentNode(); int type = mCurrNode.getNodeType(); if (type == Node.ELEMENT_NODE) { return (mCurrEvent = END_ELEMENT); } // Let's do sanity check; should really be Doc/DocFragment if (mCurrNode != mRootNode || (type != Node.DOCUMENT_NODE && type != Node.DOCUMENT_FRAGMENT_NODE)) { throw new XMLStreamException("Internal error: non-element parent node ("+type+") that is not the initial root node"); } return (mCurrEvent = END_DOCUMENT); } } // Ok, need to determine current node type: switch (mCurrNode.getNodeType()) { case Node.CDATA_SECTION_NODE: if (mCoalescing) { coalesceText(CDATA); } else { mCurrEvent = CDATA; } break; case Node.COMMENT_NODE: mCurrEvent = COMMENT; break; case Node.DOCUMENT_TYPE_NODE: mCurrEvent = DTD; break; case Node.ELEMENT_NODE: mCurrEvent = START_ELEMENT; break; case Node.ENTITY_REFERENCE_NODE: mCurrEvent = ENTITY_REFERENCE; break; case Node.PROCESSING_INSTRUCTION_NODE: mCurrEvent = PROCESSING_INSTRUCTION; break; case Node.TEXT_NODE: if (mCoalescing) { coalesceText(CHARACTERS); } else { mCurrEvent = CHARACTERS; } break; // Should not get other nodes (notation/entity decl., attr) case Node.ATTRIBUTE_NODE: case Node.ENTITY_NODE: case Node.NOTATION_NODE: throw new XMLStreamException("Internal error: unexpected DOM node type "+mCurrNode.getNodeType()+" (attr/entity/notation?), for node '"+mCurrNode+"'"); default: throw new XMLStreamException("Internal error: unrecognized DOM node type "+mCurrNode.getNodeType()+", for node '"+mCurrNode+"'"); } return mCurrEvent; } public int nextTag() throws XMLStreamException { while (true) { int next = next(); switch (next) { case SPACE: case COMMENT: case PROCESSING_INSTRUCTION: continue; case CDATA: case CHARACTERS: if (isWhiteSpace()) { continue; } throwParseError("Received non-all-whitespace CHARACTERS or CDATA event in nextTag()."); break; // never gets here, but jikes complains without case START_ELEMENT: case END_ELEMENT: return next; } throwParseError("Received event "+ErrorConsts.tokenTypeDesc(next) +", instead of START_ELEMENT or END_ELEMENT."); } } /** *

* Note: as per StAX 1.0 specs, this method does NOT close the underlying * input reader. That is, unless the new StAX2 property * {@link org.codehaus.stax2.XMLInputFactory2#P_AUTO_CLOSE_INPUT} is * set to true. */ public void close() throws XMLStreamException { // Since DOM tree has no real input source, nothing to do } /* //////////////////////////////////////////////////// // NamespaceContext //////////////////////////////////////////////////// */ public String getNamespaceURI(String prefix) { /* 26-Apr-2006, TSa: Alas, these methods are DOM Level 3, * i.e. require JDK 1.5 or higher */ /* if (prefix.length() == 0) { // def NS return mCurrNode.lookupNamespaceURI(null); } return mCurrNode.lookupNamespaceURI(prefix); */ return null; } public String getPrefix(String namespaceURI) { /* 26-Apr-2006, TSa: Alas, these methods are DOM Level 3, * i.e. require JDK 1.5 or higher */ /* String prefix = mCurrNode.lookupPrefix(namespaceURI); if (prefix == null) { // maybe default NS? String defURI = mCurrNode.lookupNamespaceURI(null); if (defURI != null && defURI.equals(namespaceURI)) { return ""; } } return prefix; */ return null; } public Iterator getPrefixes(String namespaceURI) { String prefix = getPrefix(namespaceURI); if (prefix == null) { return EmptyIterator.getInstance(); } return new SingletonIterator(prefix); } /* //////////////////////////////////////////////////// // XMLStreamReader2 (StAX2) implementation //////////////////////////////////////////////////// */ // // // StAX2, per-reader configuration public Object getFeature(String name) { // No readable features defined yet... throw new IllegalArgumentException(MessageFormat.format(ErrorConsts.ERR_UNKNOWN_FEATURE, new Object[] { name })); } public void setFeature(String name, Object value) { // Base-class has no settable features at this point. throw new IllegalArgumentException(MessageFormat.format(ErrorConsts.ERR_UNKNOWN_FEATURE, new Object[] { name })); } // NOTE: getProperty() defined in Stax 1.0 interface public boolean isPropertySupported(String name) { // !!! TBI: not all these properties are really supported return mConfig.isPropertySupported(name); } /** * @param name Name of the property to set * @param value Value to set property to. * * @return True, if the specified property was succesfully * set to specified value; false if its value was not changed */ public boolean setProperty(String name, Object value) { /* Note: can not call local method, since it'll return false for * recognized but non-mutable properties */ if (XMLInputFactory2.P_INTERN_NAMES.equals(name) || XMLInputFactory2.P_INTERN_NS_URIS.equals(name)) { /* [WTSX-162]: Name/Namespace URI interning seemingly enabled, * isn't. Alas, not easy to enable it, so let's force it to * always be disabled */ if (!(value instanceof Boolean) || ((Boolean) value).booleanValue()) { throw new IllegalArgumentException("DOM-based reader does not support interning of names or namespace URIs"); } return true; } return mConfig.setProperty(name, value); } // // // StAX2, additional traversal methods public void skipElement() throws XMLStreamException { if (mCurrEvent != START_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_STELEM); } int nesting = 1; // need one more end elements than start elements while (true) { int type = next(); if (type == START_ELEMENT) { ++nesting; } else if (type == END_ELEMENT) { if (--nesting == 0) { break; } } } } // // // StAX2, additional attribute access public AttributeInfo getAttributeInfo() throws XMLStreamException { if (mCurrEvent != START_ELEMENT) { throw new IllegalStateException(ErrorConsts.ERR_STATE_NOT_STELEM); } // !!! TBI return null; } // // // StAX2, Additional DTD access /** * Since this class implements {@link DTDInfo}, method can just * return this. */ public DTDInfo getDTDInfo() throws XMLStreamException { /* Let's not allow it to be accessed during other events -- that * way callers won't count on it being available afterwards. */ if (mCurrEvent != DTD) { return null; } return this; } // // // StAX2, Additional location information /** * Location information is always accessible, for this reader. */ public final LocationInfo getLocationInfo() { return this; } // // // StAX2, Pass-through text accessors /** * Method similar to {@link #getText()}, except * that it just uses provided Writer to write all textual content. * For further optimization, it may also be allowed to do true * pass-through, thus possibly avoiding one temporary copy of the * data. *

* TODO: try to optimize to allow completely streaming pass-through: * currently will still read all data in memory buffers before * outputting * * @param w Writer to use for writing textual contents * @param preserveContents If true, reader has to preserve contents * so that further calls to getText will return * proper conntets. If false, reader is allowed to skip creation * of such copies: this can improve performance, but it also means * that further calls to getText is not guaranteed to * return meaningful data. * * @return Number of characters written to the reader */ public int getText(Writer w, boolean preserveContents) throws IOException, XMLStreamException { if (((1 << mCurrEvent) & MASK_GET_TEXT) == 0) { throwNotTextual(mCurrEvent); } String text = getText(); w.write(text); return text.length(); } // // // StAX 2, Other accessors /** * @return Number of open elements in the stack; 0 when parser is in * prolog/epilog, 1 inside root element and so on. */ public int getDepth() { return mDepth; } /** * @return True, if cursor points to a start or end element that is * constructed from 'empty' element (ends with '/>'); * false otherwise. */ public boolean isEmptyElement() throws XMLStreamException { // No way to really figure it out via DOM is there? return false; } public NamespaceContext getNonTransientNamespaceContext() { /* Since DOM does not expose enough functionality to figure * out complete declaration stack, can not implement. * Can either return null, or a dummy instance. For now, let's * do latter: */ return EmptyNamespaceContext.getInstance(); } public String getPrefixedName() { switch (mCurrEvent) { case START_ELEMENT: case END_ELEMENT: { String prefix = getPrefix(); String ln = getLocalName(); if (prefix == null) { return ln; } StringBuffer sb = new StringBuffer(ln.length() + 1 + prefix.length()); sb.append(prefix); sb.append(':'); sb.append(ln); return sb.toString(); } case ENTITY_REFERENCE: return getLocalName(); case PROCESSING_INSTRUCTION: return getPITarget(); case DTD: return getDTDRootName(); } throw new IllegalStateException("Current state ("+ErrorConsts.tokenTypeDesc(mCurrEvent)+") not START_ELEMENT, END_ELEMENT, ENTITY_REFERENCE, PROCESSING_INSTRUCTION or DTD"); } public void closeCompletely() throws XMLStreamException { // Nothing special to do... } /* //////////////////////////////////////////////////// // DTDInfo implementation (StAX 2) //////////////////////////////////////////////////// */ public Object getProcessedDTD() { return null; } public String getDTDRootName() { if (mCurrEvent == DTD) { return ((DocumentType) mCurrNode).getName(); } return null; } public String getDTDPublicId() { if (mCurrEvent == DTD) { return ((DocumentType) mCurrNode).getPublicId(); } return null; } public String getDTDSystemId() { if (mCurrEvent == DTD) { return ((DocumentType) mCurrNode).getSystemId(); } return null; } /** * @return Internal subset portion of the DOCTYPE declaration, if any; * empty String if none */ public String getDTDInternalSubset() { /* DOM (level 3) doesn't expose anything extra; would need to * synthetize subset... which would only contain some of the * entity and notation declarations. */ return null; } // // StAX2, v2.0 public DTDValidationSchema getProcessedDTDSchema() { return null; } /* //////////////////////////////////////////////////// // LocationInfo implementation (StAX 2) //////////////////////////////////////////////////// */ // // // First, the "raw" offset accessors: public long getStartingByteOffset() { // !!! TBI return -1L; } public long getStartingCharOffset() { // !!! TBI return 0; } public long getEndingByteOffset() throws XMLStreamException { // !!! TBI return -1; } public long getEndingCharOffset() throws XMLStreamException { // !!! TBI return -1; } // // // and then the object-based access methods: public final Location getLocation() { return getStartLocation(); } public XMLStreamLocation2 getStartLocation() { // !!! TBI return null; } public XMLStreamLocation2 getCurrentLocation() { // !!! TBI return null; } public final XMLStreamLocation2 getEndLocation() throws XMLStreamException { // !!! TBI return null; } /* //////////////////////////////////////////////////// // Stax2 validation //////////////////////////////////////////////////// */ public XMLValidator validateAgainst(XMLValidationSchema schema) throws XMLStreamException { // Not implemented by the basic reader: return null; } public XMLValidator stopValidatingAgainst(XMLValidationSchema schema) throws XMLStreamException { // Not implemented by the basic reader: return null; } public XMLValidator stopValidatingAgainst(XMLValidator validator) throws XMLStreamException { // Not implemented by the basic reader: return null; } public ValidationProblemHandler setValidationProblemHandler(ValidationProblemHandler h) { // Not implemented by the basic reader return null; } /* //////////////////////////////////////////// // Internal methods, text gathering //////////////////////////////////////////// */ protected void coalesceText(int initialType) { mTextBuffer.addText(mCurrNode.getNodeValue()); Node n; while ((n = mCurrNode.getNextSibling()) != null) { int type = n.getNodeType(); if (type != Node.TEXT_NODE && type != Node.CDATA_SECTION_NODE) { break; } mCurrNode = n; mTextBuffer.addText(mCurrNode.getNodeValue()); } mCoalescedText = mTextBuffer.getAndClear(); // Either way, type gets always set to be CHARACTERS mCurrEvent = CHARACTERS; } /* //////////////////////////////////////////// // Internal methods //////////////////////////////////////////// */ private QName constructQName(String uri, String ln, String prefix) { // Stupid QName impls barf on nulls... return new QName((uri == null) ? "" : uri, ln, (prefix == null) ? "" : prefix); } /** * @param attrsToo Whether to include actual attributes too, or * just namespace declarations */ private void calcNsAndAttrLists(boolean attrsToo) { NamedNodeMap attrsIn = mCurrNode.getAttributes(); // A common case: neither attrs nor ns decls, can use short-cut int len = attrsIn.getLength(); if (len == 0) { mAttrList = mNsDeclList = Collections.EMPTY_LIST; return; } if (!mNsAware) { mAttrList = new ArrayList(len); for (int i = 0; i < len; ++i) { mAttrList.add(attrsIn.item(i)); } mNsDeclList = Collections.EMPTY_LIST; return; } // most should be attributes... and possibly no ns decls: ArrayList attrsOut = null; ArrayList nsOut = null; for (int i = 0; i < len; ++i) { Node attr = attrsIn.item(i); String prefix = attr.getPrefix(); // Prefix? if (prefix == null || prefix.length() == 0) { // nope // default ns decl? if (!"xmlns".equals(attr.getLocalName())) { // nope if (attrsToo) { if (attrsOut == null) { attrsOut = new ArrayList(len - i); } attrsOut.add(attr); } continue; } prefix = ""; } else { // explicit ns decl? if (!"xmlns".equals(prefix)) { // nope if (attrsToo) { if (attrsOut == null) { attrsOut = new ArrayList(len - i); } attrsOut.add(attr); } continue; } prefix = attr.getLocalName(); } if (nsOut == null) { nsOut = new ArrayList((len - i) * 2); } nsOut.add(prefix); nsOut.add(attr.getNodeValue()); } mAttrList = (attrsOut == null) ? Collections.EMPTY_LIST : attrsOut; mNsDeclList = (nsOut == null) ? Collections.EMPTY_LIST : nsOut; } /** * Method that returns location of the last character returned by this * reader; that is, location "one less" than the currently pointed to * location. */ protected WstxInputLocation getLastCharLocation() { // !!! TBI return null; } private void throwNotTextual(int type) { throw new IllegalStateException("Not a textual event (" +ErrorConsts.tokenTypeDesc(mCurrEvent)+")"); } private void throwParseError(String msg) throws WstxParsingException { throw new WstxParsingException(msg, getLastCharLocation()); } /* private void throwParseError(String format, Object arg) throws WstxParsingException { String msg = MessageFormat.format(format, new Object[] { arg }); throw new WstxParsingException(msg, getLastCharLocation()); } */ private void handleIllegalAttrIndex(int index) { Element elem = (Element) mCurrNode; NamedNodeMap attrs = elem.getAttributes(); int len = attrs.getLength(); String msg = "Illegal attribute index "+index+"; element <"+elem.getNodeName()+"> has "+((len == 0) ? "no" : String.valueOf(len))+" attributes"; throw new IllegalArgumentException(msg); } private void handleIllegalNsIndex(int index) { String msg = "Illegal namespace declaration index "+index+" (has "+getNamespaceCount()+" ns declarations)"; throw new IllegalArgumentException(msg); } /** * Due to differences in how namespace-aware and non-namespace modes * work in DOM, different methods are needed. We may or may not be * able to detect namespace-awareness mode of the source Nodes * directly; but at any rate, should contain some logic for handling * problem cases. */ private String safeGetLocalName(Node n) { String ln = n.getLocalName(); if (ln == null) { ln = n.getNodeName(); } return ln; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy