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

org.codehaus.stax2.ri.dom.DOMWrappingReader Maven / Gradle / Ivy

Go to download

Stax2 API is an extension to basic Stax 1.0 API that adds significant new functionality, such as full-featured bi-direction validation interface and high-performance Typed Access API.

There is a newer version: 4.2.2
Show newest version
/* Stax2 API extension for Streaming Api for Xml processing (StAX).
 *
 * Copyright (c) 2006- 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 org.codehaus.stax2.ri.dom;

import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
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.XMLStreamLocation2;
import org.codehaus.stax2.XMLStreamReader2;
import org.codehaus.stax2.ri.EmptyIterator;
import org.codehaus.stax2.ri.EmptyNamespaceContext;
import org.codehaus.stax2.ri.SingletonIterator;
import org.codehaus.stax2.ri.Stax2Util;
import org.codehaus.stax2.ri.typed.StringBase64Decoder;
import org.codehaus.stax2.ri.typed.ValueDecoderFactory;
import org.codehaus.stax2.typed.Base64Variant;
import org.codehaus.stax2.typed.Base64Variants;
import org.codehaus.stax2.typed.TypedArrayDecoder;
import org.codehaus.stax2.typed.TypedValueDecoder;
import org.codehaus.stax2.typed.TypedXMLStreamException;
import org.codehaus.stax2.validation.DTDValidationSchema;
import org.codehaus.stax2.validation.ValidationProblemHandler;
import org.codehaus.stax2.validation.XMLValidationSchema;
import org.codehaus.stax2.validation.XMLValidator;

/**
 * This is an adapter class that presents a DOM document as if it was
 * a regular {@link XMLStreamReader}. This is mostly useful for
 * inter-operability purposes, and should only be used when the
 * input has to come as a DOM object and the original xml content
 * is not available as a stream.
 *

* 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 abstract class DOMWrappingReader implements XMLStreamReader2, AttributeInfo, DTDInfo, LocationInfo, NamespaceContext, XMLStreamConstants { final static int INT_SPACE = 0x0020; // // // 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_TEXT_XXX = (1 << CHARACTERS) | (1 << CDATA) | (1 << SPACE) | (1 << COMMENT); final private static int MASK_GET_ELEMENT_TEXT = (1 << CHARACTERS) | (1 << CDATA) | (1 << SPACE) | (1 << ENTITY_REFERENCE); final protected static int MASK_TYPED_ACCESS_BINARY = (1 << START_ELEMENT) // note: END_ELEMENT handled separately | (1 << CHARACTERS) | (1 << CDATA) | (1 << SPACE) ; // // // Enumerated error case ids /** * Current state not START_ELEMENT, should be */ protected final static int ERR_STATE_NOT_START_ELEM = 1; /** * Current state not START_ELEMENT or END_ELEMENT, should be */ protected final static int ERR_STATE_NOT_ELEM = 2; /** * Current state not PROCESSING_INSTRUCTION */ protected final static int ERR_STATE_NOT_PI = 3; /** * Current state not one where getText() can be used */ protected final static int ERR_STATE_NOT_TEXTUAL = 4; /** * Current state not one where getTextXxx() can be used */ protected final static int ERR_STATE_NOT_TEXTUAL_XXX = 5; protected final static int ERR_STATE_NOT_TEXTUAL_OR_ELEM = 6; protected final static int ERR_STATE_NO_LOCALNAME = 7; // // // Configuration: protected final String _systemId; protected final Node _rootNode; /** * Whether stream reader is to be namespace aware (as per property * {@link XMLInputFactory#IS_NAMESPACE_AWARE}) or not */ protected final boolean _cfgNsAware; /** * Whether stream reader is to coalesce adjacent textual * (CHARACTERS, SPACE, CDATA) events (as per property * {@link XMLInputFactory#IS_COALESCING}) or not */ protected final boolean _coalescing; /** * By default we do not force interning of names: can be * reset by sub-classes. */ protected boolean _cfgInternNames = false; /** * By default we do not force interning of namespace URIs: can be * reset by sub-classes. */ protected boolean _cfgInternNsURIs = false; // // // State: protected int _currEvent = START_DOCUMENT; /** * Current node is the DOM node that contains information * regarding the current event. */ protected Node _currNode; protected int _depth = 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 _coalescedText; /** * Helper object used for combining segments of text as needed */ protected Stax2Util.TextBuffer _textBuffer = new Stax2Util.TextBuffer(); // // // 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 _attrList = 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 _nsDeclList = null; /** * Factory used for constructing decoders we need for typed access */ protected ValueDecoderFactory _decoderFactory; /** * Lazily-constructed decoder object for decoding base64 encoded * binary content. */ protected StringBase64Decoder _base64Decoder = null; /* //////////////////////////////////////////////////// // Construction, configuration //////////////////////////////////////////////////// */ /** * @param src Node that is the tree of the DOM document, or fragment. * @param nsAware Whether resulting reader should operate in namespace * aware mode or not. Note that this should be compatible with * settings for the DOM builder that produced DOM tree or fragment * being operated on, otherwise results are not defined. * @param coalescing Whether resulting reader should coalesce adjacent * text events or not */ protected DOMWrappingReader(DOMSource src, boolean nsAware, boolean coalescing) throws XMLStreamException { Node treeRoot = src.getNode(); if (treeRoot == null) { throw new IllegalArgumentException("Can not pass null Node for constructing a DOM-based XMLStreamReader"); } _cfgNsAware = nsAware; _coalescing = coalescing; _systemId = src.getSystemId(); /* 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()); } _rootNode = _currNode = treeRoot; } protected void setInternNames(boolean state) { _cfgInternNames = state; } protected void setInternNsURIs(boolean state) { _cfgInternNsURIs = state; } /* //////////////////////////////////////////////////// // Abstract methods for sub-classes to implement //////////////////////////////////////////////////// */ protected abstract void throwStreamException(String msg, Location loc) throws XMLStreamException; /* //////////////////////////////////////////////////// // 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 abstract Object getProperty(String name); // NOTE: getProperty() defined in Stax 1.0 interface public abstract boolean isPropertySupported(String 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 abstract boolean setProperty(String name, Object value); /* //////////////////////////////////////////////////// // XMLStreamReader, current state //////////////////////////////////////////////////// */ // // // Attribute access: public int getAttributeCount() { if (_currEvent != START_ELEMENT) { reportWrongState(ERR_STATE_NOT_START_ELEM); } if (_attrList == null) { _calcNsAndAttrLists(true); } return _attrList.size(); } public String getAttributeLocalName(int index) { if (_currEvent != START_ELEMENT) { reportWrongState(ERR_STATE_NOT_START_ELEM); } if (_attrList == null) { _calcNsAndAttrLists(true); } if (index >= _attrList.size() || index < 0) { handleIllegalAttrIndex(index); return null; } Attr attr = (Attr) _attrList.get(index); return _internName(_safeGetLocalName(attr)); } public QName getAttributeName(int index) { if (_currEvent != START_ELEMENT) { reportWrongState(ERR_STATE_NOT_START_ELEM); } if (_attrList == null) { _calcNsAndAttrLists(true); } if (index >= _attrList.size() || index < 0) { handleIllegalAttrIndex(index); return null; } Attr attr = (Attr) _attrList.get(index); return _constructQName(attr.getNamespaceURI(), _safeGetLocalName(attr), attr.getPrefix()); } public String getAttributeNamespace(int index) { if (_currEvent != START_ELEMENT) { reportWrongState(ERR_STATE_NOT_START_ELEM); } if (_attrList == null) { _calcNsAndAttrLists(true); } if (index >= _attrList.size() || index < 0) { handleIllegalAttrIndex(index); return null; } Attr attr = (Attr) _attrList.get(index); return _internNsURI(attr.getNamespaceURI()); } public String getAttributePrefix(int index) { if (_currEvent != START_ELEMENT) { reportWrongState(ERR_STATE_NOT_START_ELEM); } if (_attrList == null) { _calcNsAndAttrLists(true); } if (index >= _attrList.size() || index < 0) { handleIllegalAttrIndex(index); return null; } Attr attr = (Attr) _attrList.get(index); return _internName(attr.getPrefix()); } public String getAttributeType(int index) { if (_currEvent != START_ELEMENT) { reportWrongState(ERR_STATE_NOT_START_ELEM); } if (_attrList == null) { _calcNsAndAttrLists(true); } if (index >= _attrList.size() || index < 0) { handleIllegalAttrIndex(index); return null; } //Attr attr = (Attr) _attrList.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 (_currEvent != START_ELEMENT) { reportWrongState(ERR_STATE_NOT_START_ELEM); } if (_attrList == null) { _calcNsAndAttrLists(true); } if (index >= _attrList.size() || index < 0) { handleIllegalAttrIndex(index); return null; } Attr attr = (Attr) _attrList.get(index); return attr.getValue(); } public String getAttributeValue(String nsURI, String localName) { if (_currEvent != START_ELEMENT) { reportWrongState(ERR_STATE_NOT_START_ELEM); } Element elem = (Element) _currNode; 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 (_currEvent != START_ELEMENT) { /* Quite illogical: this is not an IllegalStateException * like other similar ones, but rather an XMLStreamException. * But that's how Stax JavaDocs outline how it should be. */ reportParseProblem(ERR_STATE_NOT_START_ELEM); } _textBuffer.reset(); // 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) { reportParseProblem(ERR_STATE_NOT_TEXTUAL); } _textBuffer.append(getText()); } return _textBuffer.get(); } /** * Returns type of the last event returned; or START_DOCUMENT before * any events has been explicitly returned. */ public int getEventType() { return _currEvent; } public String getLocalName() { if (_currEvent == START_ELEMENT || _currEvent == END_ELEMENT) { return _internName(_safeGetLocalName(_currNode)); } if (_currEvent != ENTITY_REFERENCE) { reportWrongState(ERR_STATE_NO_LOCALNAME); } return _internName(_currNode.getNodeName()); } public final Location getLocation() { return getStartLocation(); } public QName getName() { if (_currEvent != START_ELEMENT && _currEvent != END_ELEMENT) { reportWrongState(ERR_STATE_NOT_START_ELEM); } return _constructQName(_currNode.getNamespaceURI(), _safeGetLocalName(_currNode), _currNode.getPrefix()); } // // // Namespace access public NamespaceContext getNamespaceContext() { return this; } public int getNamespaceCount() { if (_currEvent != START_ELEMENT && _currEvent != END_ELEMENT) { reportWrongState(ERR_STATE_NOT_ELEM); } if (_nsDeclList == null) { if (!_cfgNsAware) { return 0; } _calcNsAndAttrLists(_currEvent == START_ELEMENT); } return _nsDeclList.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 (_currEvent != START_ELEMENT && _currEvent != END_ELEMENT) { reportWrongState(ERR_STATE_NOT_ELEM); } if (_nsDeclList == null) { if (!_cfgNsAware) { handleIllegalNsIndex(index); } _calcNsAndAttrLists(_currEvent == START_ELEMENT); } if (index < 0 || (index + index) >= _nsDeclList.size()) { handleIllegalNsIndex(index); } // Note: _nsDeclList entries have been appropriately intern()ed if need be return (String) _nsDeclList.get(index + index); } public String getNamespaceURI() { if (_currEvent != START_ELEMENT && _currEvent != END_ELEMENT) { reportWrongState(ERR_STATE_NOT_ELEM); } return _internNsURI(_currNode.getNamespaceURI()); } public String getNamespaceURI(int index) { if (_currEvent != START_ELEMENT && _currEvent != END_ELEMENT) { reportWrongState(ERR_STATE_NOT_ELEM); } if (_nsDeclList == null) { if (!_cfgNsAware) { handleIllegalNsIndex(index); } _calcNsAndAttrLists(_currEvent == START_ELEMENT); } if (index < 0 || (index + index) >= _nsDeclList.size()) { handleIllegalNsIndex(index); } // Note: _nsDeclList entries have been appropriately intern()ed if need be return (String) _nsDeclList.get(index + index + 1); } // Note: implemented as part of NamespaceContext //public String getNamespaceURI(String prefix) public String getPIData() { if (_currEvent != PROCESSING_INSTRUCTION) { reportWrongState(ERR_STATE_NOT_PI); } return _currNode.getNodeValue(); } public String getPITarget() { if (_currEvent != PROCESSING_INSTRUCTION) { reportWrongState(ERR_STATE_NOT_PI); } return _internName(_currNode.getNodeName()); } public String getPrefix() { if (_currEvent != START_ELEMENT && _currEvent != END_ELEMENT) { reportWrongState(ERR_STATE_NOT_ELEM); } return _internName(_currNode.getPrefix()); } public String getText() { if (_coalescedText != null) { return _coalescedText; } if (((1 << _currEvent) & MASK_GET_TEXT) == 0) { reportWrongState(ERR_STATE_NOT_TEXTUAL); } return _currNode.getNodeValue(); } public char[] getTextCharacters() { String text = getText(); return text.toCharArray(); } public int getTextCharacters(int sourceStart, char[] target, int targetStart, int len) { if (((1 << _currEvent) & MASK_GET_TEXT_XXX) == 0) { reportWrongState(ERR_STATE_NOT_TEXTUAL_XXX); } String text = getText(); if (len > text.length()) { len = text.length(); } text.getChars(sourceStart, sourceStart+len, target, targetStart); return len; } public int getTextLength() { if (((1 << _currEvent) & MASK_GET_TEXT_XXX) == 0) { reportWrongState(ERR_STATE_NOT_TEXTUAL_XXX); } return getText().length(); } public int getTextStart() { if (((1 << _currEvent) & MASK_GET_TEXT_XXX) == 0) { reportWrongState(ERR_STATE_NOT_TEXTUAL_XXX); } return 0; } public boolean hasName() { return (_currEvent == START_ELEMENT) || (_currEvent == END_ELEMENT); } public boolean hasNext() { return (_currEvent != END_DOCUMENT); } public boolean hasText() { return (((1 << _currEvent) & MASK_GET_TEXT) != 0); } public boolean isAttributeSpecified(int index) { if (_currEvent != START_ELEMENT) { reportWrongState(ERR_STATE_NOT_START_ELEM); } Element elem = (Element) _currNode; Attr attr = (Attr) elem.getAttributes().item(index); if (attr == null) { handleIllegalAttrIndex(index); return false; } return attr.getSpecified(); } public boolean isCharacters() { return (_currEvent == CHARACTERS); } public boolean isEndElement() { return (_currEvent == END_ELEMENT); } public boolean isStartElement() { return (_currEvent == START_ELEMENT); } public boolean isWhiteSpace() { if (_currEvent == CHARACTERS || _currEvent == 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) > INT_SPACE) { return false; } } return true; } return (_currEvent == SPACE); } public void require(int type, String nsUri, String localName) throws XMLStreamException { int curr = _currEvent; /* 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) { throwStreamException("Required type "+Stax2Util.eventTypeDesc(type) +", current type " +Stax2Util.eventTypeDesc(curr)); } if (localName != null) { if (curr != START_ELEMENT && curr != END_ELEMENT && curr != ENTITY_REFERENCE) { throwStreamException("Required a non-null local name, but current token not a START_ELEMENT, END_ELEMENT or ENTITY_REFERENCE (was "+Stax2Util.eventTypeDesc(_currEvent)+")"); } String n = getLocalName(); if (n != localName && !n.equals(localName)) { throwStreamException("Required local name '"+localName+"'; current local name '"+n+"'."); } } if (nsUri != null) { if (curr != START_ELEMENT && curr != END_ELEMENT) { throwStreamException("Required non-null NS URI, but current token not a START_ELEMENT or END_ELEMENT (was "+Stax2Util.eventTypeDesc(curr)+")"); } String uri = getNamespaceURI(); // No namespace? if (nsUri.length() == 0) { if (uri != null && uri.length() > 0) { throwStreamException("Required empty namespace, instead have '"+uri+"'."); } } else { if ((nsUri != uri) && !nsUri.equals(uri)) { throwStreamException("Required namespace '"+nsUri+"'; have '" +uri+"'."); } } } // Ok, fine, all's good } /* //////////////////////////////////////////////////// // XMLStreamReader, iterating //////////////////////////////////////////////////// */ public int next() throws XMLStreamException { _coalescedText = 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 (_currEvent) { case START_DOCUMENT: // initial state /* What to do here depends on what kind of node we started * with... */ switch (_currNode.getNodeType()) { case Node.DOCUMENT_NODE: case Node.DOCUMENT_FRAGMENT_NODE: // For doc, fragment, need to find first child _currNode = _currNode.getFirstChild(); break; case Node.ELEMENT_NODE: // For element, curr node is fine: return (_currEvent = START_ELEMENT); default: throw new XMLStreamException("Internal error: unexpected DOM root node type "+_currNode.getNodeType()+" for node '"+_currNode+"'"); } 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 ++_depth; _attrList = null; // so it will not get reused accidentally { Node firstChild = _currNode.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 (_currEvent = END_ELEMENT); } _nsDeclList = null; /* non-empty is easy: let's just swap curr node, and * fall through to regular handling */ _currNode = firstChild; break; } case END_ELEMENT: --_depth; // Need to clear these lists _attrList = null; _nsDeclList = 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 (_currNode == _rootNode) { return (_currEvent = 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 = _currNode.getNextSibling(); // If sibling, let's just assign and fall through if (next != null) { _currNode = 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) */ _currNode = _currNode.getParentNode(); int type = _currNode.getNodeType(); if (type == Node.ELEMENT_NODE) { return (_currEvent = END_ELEMENT); } // Let's do sanity check; should really be Doc/DocFragment if (_currNode != _rootNode || (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 (_currEvent = END_DOCUMENT); } } // Ok, need to determine current node type: switch (_currNode.getNodeType()) { case Node.CDATA_SECTION_NODE: if (_coalescing) { coalesceText(CDATA); } else { _currEvent = CDATA; } break; case Node.COMMENT_NODE: _currEvent = COMMENT; break; case Node.DOCUMENT_TYPE_NODE: _currEvent = DTD; break; case Node.ELEMENT_NODE: _currEvent = START_ELEMENT; break; case Node.ENTITY_REFERENCE_NODE: _currEvent = ENTITY_REFERENCE; break; case Node.PROCESSING_INSTRUCTION_NODE: _currEvent = PROCESSING_INSTRUCTION; break; case Node.TEXT_NODE: if (_coalescing) { coalesceText(CHARACTERS); } else { _currEvent = 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 "+_currNode.getNodeType()+" (attr/entity/notation?), for node '"+_currNode+"'"); default: throw new XMLStreamException("Internal error: unrecognized DOM node type "+_currNode.getNodeType()+", for node '"+_currNode+"'"); } return _currEvent; } 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; } throwStreamException("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; } throwStreamException("Received event "+Stax2Util.eventTypeDesc(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 _currNode.lookupNamespaceURI(null); } return _currNode.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 = _currNode.lookupPrefix(namespaceURI); if (prefix == null) { // maybe default NS? String defURI = _currNode.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); } /* ///////////////////////////////////////////////// // TypedXMLStreamReader2 implementation, element ///////////////////////////////////////////////// */ public boolean getElementAsBoolean() throws XMLStreamException { ValueDecoderFactory.BooleanDecoder dec = _decoderFactory().getBooleanDecoder(); getElementAs(dec); return dec.getValue(); } public int getElementAsInt() throws XMLStreamException { ValueDecoderFactory.IntDecoder dec = _decoderFactory().getIntDecoder(); getElementAs(dec); return dec.getValue(); } public long getElementAsLong() throws XMLStreamException { ValueDecoderFactory.LongDecoder dec = _decoderFactory().getLongDecoder(); getElementAs(dec); return dec.getValue(); } public float getElementAsFloat() throws XMLStreamException { ValueDecoderFactory.FloatDecoder dec = _decoderFactory().getFloatDecoder(); getElementAs(dec); return dec.getValue(); } public double getElementAsDouble() throws XMLStreamException { ValueDecoderFactory.DoubleDecoder dec = _decoderFactory().getDoubleDecoder(); getElementAs(dec); return dec.getValue(); } public BigInteger getElementAsInteger() throws XMLStreamException { ValueDecoderFactory.IntegerDecoder dec = _decoderFactory().getIntegerDecoder(); getElementAs(dec); return dec.getValue(); } public BigDecimal getElementAsDecimal() throws XMLStreamException { ValueDecoderFactory.DecimalDecoder dec = _decoderFactory().getDecimalDecoder(); getElementAs(dec); return dec.getValue(); } public QName getElementAsQName() throws XMLStreamException { ValueDecoderFactory.QNameDecoder dec = _decoderFactory().getQNameDecoder(getNamespaceContext()); getElementAs(dec); return dec.getValue(); } public byte[] getElementAsBinary() throws XMLStreamException { return getElementAsBinary(Base64Variants.getDefaultVariant()); } public byte[] getElementAsBinary(Base64Variant v) throws XMLStreamException { // note: code here is similar to Base64DecoderBase.aggregateAll(), see comments there Stax2Util.ByteAggregator aggr = _base64Decoder().getByteAggregator(); byte[] buffer = aggr.startAggregation(); while (true) { int offset = 0; int len = buffer.length; do { int readCount = readElementAsBinary(buffer, offset, len, v); if (readCount < 1) { // all done! return aggr.aggregateAll(buffer, offset); } offset += readCount; len -= readCount; } while (len > 0); buffer = aggr.addFullBlock(buffer); } } public void getElementAs(TypedValueDecoder tvd) throws XMLStreamException { String value = getElementText(); value = Stax2Util.trimSpaces(value); try { if (value == null) { tvd.handleEmptyValue(); } else { tvd.decode(value); } } catch (IllegalArgumentException iae) { throw _constructTypeException(iae, value); } } public int readElementAsIntArray(int[] value, int from, int length) throws XMLStreamException { return readElementAsArray(_decoderFactory().getIntArrayDecoder(value, from, length)); } public int readElementAsLongArray(long[] value, int from, int length) throws XMLStreamException { return readElementAsArray(_decoderFactory().getLongArrayDecoder(value, from, length)); } public int readElementAsFloatArray(float[] value, int from, int length) throws XMLStreamException { return readElementAsArray(_decoderFactory().getFloatArrayDecoder(value, from, length)); } public int readElementAsDoubleArray(double[] value, int from, int length) throws XMLStreamException { return readElementAsArray(_decoderFactory().getDoubleArrayDecoder(value, from, length)); } public int readElementAsArray(TypedArrayDecoder tad) throws XMLStreamException { /* Otherwise either we are just starting (START_ELEMENT), or * have collected all the stuff into _textBuffer. */ if (_currEvent == START_ELEMENT) { // One special case, no children: Node fc = _currNode.getFirstChild(); if (fc == null) { _currEvent = END_ELEMENT; return -1; } _coalescedText = coalesceTypedText(fc); _currEvent = CHARACTERS; _currNode = _currNode.getLastChild(); } else { if (_currEvent != CHARACTERS && _currEvent != CDATA) { // Maybe we are already done? if (_currEvent == END_ELEMENT) { return -1; } reportWrongState(ERR_STATE_NOT_TEXTUAL_OR_ELEM); } /* One more thing: do we have the data? It is possible * that caller has advanced to this text node by itself. * We could handle this mostly ok; but that is not a supported * use case as per Typed Access API definition (as it can not * be reliably supported by all implementations), so: */ if (_coalescedText == null) { throw new IllegalStateException("First call to readElementAsArray() must be for a START_ELEMENT, not directly for a textual event"); } } /* Otherwise, need to move pointer to point to the last * child node, and fake that it was a textual node */ // Ok, so what do we have left? String input = _coalescedText; final int end = input.length(); int ptr = 0; int count = 0; String value = null; try { decode_loop: while (ptr < end) { // First, any space to skip? while (input.charAt(ptr) <= INT_SPACE) { if (++ptr >= end) { break decode_loop; } } // Then let's figure out non-space char (token) int start = ptr; ++ptr; while (ptr < end && input.charAt(ptr) > INT_SPACE) { ++ptr; } ++count; // And there we have it value = input.substring(start, ptr); // Plus, can skip trailing space (or at end, just beyond it) ++ptr; if (tad.decodeValue(value)) { break; } } } catch (IllegalArgumentException iae) { // Need to convert to a checked stream exception /* Hmmh. This is not an accurate location... but it's * about the best we can do */ Location loc = getLocation(); throw new TypedXMLStreamException(value, iae.getMessage(), loc, iae); } finally { int len = end-ptr; _coalescedText = (len < 1) ? "" : input.substring(ptr); } if (count < 1) { // end _currEvent = END_ELEMENT; _currNode = _currNode.getParentNode(); return -1; } return count; } private String coalesceTypedText(Node firstNode) throws XMLStreamException { /* This is a bit tricky as we have to collect all the * text up end tag, but can not advance to END_ELEMENT * event itself (except if there is no content) */ _textBuffer.reset(); _attrList = null; // so it will not get reused accidentally for (Node n = firstNode; n != null; n = n.getNextSibling()) { switch (n.getNodeType()) { case Node.ELEMENT_NODE: // Illegal to have child elements... throwStreamException("Element content can not contain child START_ELEMENT when using Typed Access methods"); case Node.CDATA_SECTION_NODE: case Node.TEXT_NODE: _textBuffer.append(n.getNodeValue()); break; case Node.COMMENT_NODE: case Node.PROCESSING_INSTRUCTION_NODE: break; default: // Otherwise... do we care? For now, let's do throwStreamException("Unexpected DOM node type ("+n.getNodeType()+") when trying to decode Typed content"); } } return _textBuffer.get(); } /* //////////////////////////////////////////////////////// // TypedXMLStreamReader2 implementation, binary data //////////////////////////////////////////////////////// */ public int readElementAsBinary(byte[] resultBuffer, int offset, int maxLength) throws XMLStreamException { return readElementAsBinary(resultBuffer, offset, maxLength, Base64Variants.getDefaultVariant()); } public int readElementAsBinary(byte[] resultBuffer, int offset, int maxLength, Base64Variant v) throws XMLStreamException { if (resultBuffer == null) { throw new IllegalArgumentException("resultBuffer is null"); } if (offset < 0) { throw new IllegalArgumentException("Illegal offset ("+offset+"), must be [0, "+resultBuffer.length+"["); } if (maxLength < 1 || (offset + maxLength) > resultBuffer.length) { if (maxLength == 0) { // special case, allowed, but won't do anything return 0; } throw new IllegalArgumentException("Illegal maxLength ("+maxLength+"), has to be positive number, and offset+maxLength can not exceed"+resultBuffer.length); } final StringBase64Decoder dec = _base64Decoder(); int type = _currEvent; // First things first: must be acceptable start state: if (((1 << type) & MASK_TYPED_ACCESS_BINARY) == 0) { if (type == END_ELEMENT) { // Minor complication: may have unflushed stuff (non-padded versions) if (!dec.hasData()) { return -1; } } else { reportWrongState(ERR_STATE_NOT_TEXTUAL_OR_ELEM); } } // Are we just starting (START_ELEMENT)? if (type == START_ELEMENT) { // Just need to locate the first text segment (or reach END_ELEMENT) while (true) { type = next(); if (type == END_ELEMENT) { // Simple... no textual content return -1; } if (type == COMMENT || type == PROCESSING_INSTRUCTION) { continue; } if (((1 << type) & MASK_GET_ELEMENT_TEXT) == 0) { reportParseProblem(ERR_STATE_NOT_TEXTUAL); } dec.init(v, true, getText()); break; } } int totalCount = 0; main_loop: while (true) { // Ok, decode: int count; try { count = dec.decode(resultBuffer, offset, maxLength); } catch (IllegalArgumentException iae) { throw _constructTypeException(iae, ""); } offset += count; totalCount += count; maxLength -= count; /* And if we filled the buffer we are done. Or, an edge * case: reached END_ELEMENT (for non-padded variant) */ if (maxLength < 1 || _currEvent == END_ELEMENT) { break; } // Otherwise need to advance to the next event while (true) { type = next(); if (type == COMMENT || type == PROCESSING_INSTRUCTION || type == SPACE) { // space is ignorable too continue; } if (type == END_ELEMENT) { /* Just need to verify we don't have partial stuff * (missing one to three characters of a full quartet * that encodes 1 - 3 bytes). Also: non-padding * variants can be in incomplete state, from which * data may need to be flushed... */ int left = dec.endOfContent(); if (left < 0) { // incomplete, error throw _constructTypeException("Incomplete base64 triplet at the end of decoded content", ""); } else if (left > 0) { // 1 or 2 more bytes of data, loop some more continue main_loop; } // Otherwise, no more data, we are done break main_loop; } if (((1 << type) & MASK_GET_ELEMENT_TEXT) == 0) { reportParseProblem(ERR_STATE_NOT_TEXTUAL); } dec.init(v, false, getText()); break; } } // If nothing was found, needs to be indicated via -1, not 0 return (totalCount > 0) ? totalCount : -1; } /* ///////////////////////////////////////////////// // TypedXMLStreamReader2 implementation, attribute ///////////////////////////////////////////////// */ public int getAttributeIndex(String namespaceURI, String localName) { return findAttributeIndex(namespaceURI, localName); } public boolean getAttributeAsBoolean(int index) throws XMLStreamException { ValueDecoderFactory.BooleanDecoder dec = _decoderFactory().getBooleanDecoder(); getAttributeAs(index, dec); return dec.getValue(); } public int getAttributeAsInt(int index) throws XMLStreamException { ValueDecoderFactory.IntDecoder dec = _decoderFactory().getIntDecoder(); getAttributeAs(index, dec); return dec.getValue(); } public long getAttributeAsLong(int index) throws XMLStreamException { ValueDecoderFactory.LongDecoder dec = _decoderFactory().getLongDecoder(); getAttributeAs(index, dec); return dec.getValue(); } public float getAttributeAsFloat(int index) throws XMLStreamException { ValueDecoderFactory.FloatDecoder dec = _decoderFactory().getFloatDecoder(); getAttributeAs(index, dec); return dec.getValue(); } public double getAttributeAsDouble(int index) throws XMLStreamException { ValueDecoderFactory.DoubleDecoder dec = _decoderFactory().getDoubleDecoder(); getAttributeAs(index, dec); return dec.getValue(); } public BigInteger getAttributeAsInteger(int index) throws XMLStreamException { ValueDecoderFactory.IntegerDecoder dec = _decoderFactory().getIntegerDecoder(); getAttributeAs(index, dec); return dec.getValue(); } public BigDecimal getAttributeAsDecimal(int index) throws XMLStreamException { ValueDecoderFactory.DecimalDecoder dec = _decoderFactory().getDecimalDecoder(); getAttributeAs(index, dec); return dec.getValue(); } public QName getAttributeAsQName(int index) throws XMLStreamException { ValueDecoderFactory.QNameDecoder dec = _decoderFactory().getQNameDecoder(getNamespaceContext()); getAttributeAs(index, dec); return dec.getValue(); } public final void getAttributeAs(int index, TypedValueDecoder tvd) throws XMLStreamException { String value = getAttributeValue(index); value = Stax2Util.trimSpaces(value); try { if (value == null) { tvd.handleEmptyValue(); } else { tvd.decode(value); } } catch (IllegalArgumentException iae) { throw _constructTypeException(iae, value); } } public int[] getAttributeAsIntArray(int index) throws XMLStreamException { ValueDecoderFactory.IntArrayDecoder dec = _decoderFactory().getIntArrayDecoder(); _getAttributeAsArray(dec, getAttributeValue(index)); return dec.getValues(); } public long[] getAttributeAsLongArray(int index) throws XMLStreamException { ValueDecoderFactory.LongArrayDecoder dec = _decoderFactory().getLongArrayDecoder(); _getAttributeAsArray(dec, getAttributeValue(index)); return dec.getValues(); } public float[] getAttributeAsFloatArray(int index) throws XMLStreamException { ValueDecoderFactory.FloatArrayDecoder dec = _decoderFactory().getFloatArrayDecoder(); _getAttributeAsArray(dec, getAttributeValue(index)); return dec.getValues(); } public double[] getAttributeAsDoubleArray(int index) throws XMLStreamException { ValueDecoderFactory.DoubleArrayDecoder dec = _decoderFactory().getDoubleArrayDecoder(); _getAttributeAsArray(dec, getAttributeValue(index)); return dec.getValues(); } public int getAttributeAsArray(int index, TypedArrayDecoder tad) throws XMLStreamException { return _getAttributeAsArray(tad, getAttributeValue(index)); } protected int _getAttributeAsArray(TypedArrayDecoder tad, String attrValue) throws XMLStreamException { int ptr = 0; int start = 0; final int end = attrValue.length(); String lexical = null; int count = 0; try { decode_loop: while (ptr < end) { // First, any space to skip? while (attrValue.charAt(ptr) <= INT_SPACE) { if (++ptr >= end) { break decode_loop; } } // Then let's figure out non-space char (token) start = ptr; ++ptr; while (ptr < end && attrValue.charAt(ptr) > INT_SPACE) { ++ptr; } int tokenEnd = ptr; ++ptr; // to skip trailing space (or, beyond end) // And there we have it lexical = attrValue.substring(start, tokenEnd); ++count; if (tad.decodeValue(lexical)) { if (!checkExpand(tad)) { break; } } } } catch (IllegalArgumentException iae) { // Need to convert to a checked stream exception Location loc = getLocation(); throw new TypedXMLStreamException(lexical, iae.getMessage(), loc, iae); } return count; } /** * Internal method used to see if we can expand the buffer that * the array decoder has. Bit messy, but simpler than having * separately typed instances; and called rarely so that performance * downside of instanceof is irrelevant. */ private final boolean checkExpand(TypedArrayDecoder tad) { if (tad instanceof ValueDecoderFactory.BaseArrayDecoder) { ((ValueDecoderFactory.BaseArrayDecoder) tad).expand(); return true; } return false; } public byte[] getAttributeAsBinary(int index) throws XMLStreamException { return getAttributeAsBinary(index, Base64Variants.getDefaultVariant()); } public byte[] getAttributeAsBinary(int index, Base64Variant v) throws XMLStreamException { String lexical = getAttributeValue(index); final StringBase64Decoder dec = _base64Decoder(); dec.init(v, true, lexical); try { return dec.decodeCompletely(); } catch (IllegalArgumentException iae) { throw _constructTypeException(iae, lexical); } } /* //////////////////////////////////////////////////// // XMLStreamReader2 (StAX2) implementation //////////////////////////////////////////////////// */ // // // StAX2, per-reader configuration public Object getFeature(String name) { // No readable features supported yet throw new IllegalArgumentException("Unrecognized feature \""+name+"\""); } public void setFeature(String name, Object value) { throw new IllegalArgumentException("Unrecognized feature \""+name+"\""); } // // // StAX2, additional traversal methods public void skipElement() throws XMLStreamException { if (_currEvent != START_ELEMENT) { reportWrongState(ERR_STATE_NOT_START_ELEM); } 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 (_currEvent != START_ELEMENT) { reportWrongState(ERR_STATE_NOT_START_ELEM); } return this; } // AttributeInfo impl: //public int getAttributeCount() public int findAttributeIndex(String nsURI, String localName) { if (_currEvent != START_ELEMENT) { reportWrongState(ERR_STATE_NOT_START_ELEM); } Element elem = (Element) _currNode; NamedNodeMap attrs = elem.getAttributes(); if (nsURI != null && nsURI.length() == 0) { nsURI = null; } // Ugh. Horrible clumsy code. But has to do... for (int i = 0, len = attrs.getLength(); i < len; ++i) { Node attr = attrs.item(i); String ln = _safeGetLocalName(attr); if (localName.equals(ln)) { String thisUri = attr.getNamespaceURI(); boolean isEmpty = (thisUri == null) || thisUri.length() == 0; if (nsURI == null) { if (isEmpty) { return i; } } else { if (!isEmpty && nsURI.equals(thisUri)) { return i; } } } } return -1; } public int getIdAttributeIndex() { // !!! TBI // Note: will need Dom3 level support (JDK 1.5) return -1; } public int getNotationAttributeIndex() { // !!! TBI // Note: will need Dom3 level support (JDK 1.5) return -1; } // // // 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 (_currEvent != 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 { 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 _depth; } /** * @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 (_currEvent) { case START_ELEMENT: case END_ELEMENT: { String prefix = _currNode.getPrefix(); String ln = _safeGetLocalName(_currNode); if (prefix == null) { return _internName(ln); } StringBuffer sb = new StringBuffer(ln.length() + 1 + prefix.length()); sb.append(prefix); sb.append(':'); sb.append(ln); return _internName(sb.toString()); } case ENTITY_REFERENCE: return getLocalName(); case PROCESSING_INSTRUCTION: return getPITarget(); case DTD: return getDTDRootName(); } throw new IllegalStateException("Current state ("+Stax2Util.eventTypeDesc(_currEvent)+") 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 (_currEvent == DTD) { return _internName(((DocumentType) _currNode).getName()); } return null; } public String getDTDPublicId() { if (_currEvent == DTD) { return ((DocumentType) _currNode).getPublicId(); } return null; } public String getDTDSystemId() { if (_currEvent == DTD) { return ((DocumentType) _currNode).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 XMLStreamLocation2 getStartLocation() { // !!! TBI return null; } public XMLStreamLocation2 getCurrentLocation() { // !!! TBI return null; } public final XMLStreamLocation2 getEndLocation() throws XMLStreamException { // !!! TBI return null; } /* //////////////////////////////////////////////////// // Stax2 validation: !!! TODO //////////////////////////////////////////////////// */ 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) { _textBuffer.reset(); _textBuffer.append(_currNode.getNodeValue()); Node n; while ((n = _currNode.getNextSibling()) != null) { int type = n.getNodeType(); if (type != Node.TEXT_NODE && type != Node.CDATA_SECTION_NODE) { break; } _currNode = n; _textBuffer.append(_currNode.getNodeValue()); } _coalescedText = _textBuffer.get(); // Either way, type gets always set to be CHARACTERS _currEvent = CHARACTERS; } /* //////////////////////////////////////////// // Internal methods, namespace support //////////////////////////////////////////// */ private QName _constructQName(String uri, String ln, String prefix) { // Stupid QName impls barf on nulls... return new QName(_internNsURI(uri), _internName(ln), _internName(prefix)); } /** * @param attrsToo Whether to include actual attributes too, or * just namespace declarations */ private void _calcNsAndAttrLists(boolean attrsToo) { NamedNodeMap attrsIn = _currNode.getAttributes(); // A common case: neither attrs nor ns decls, can use short-cut int len = attrsIn.getLength(); if (len == 0) { _attrList = _nsDeclList = Collections.EMPTY_LIST; return; } if (!_cfgNsAware) { _attrList = new ArrayList(len); for (int i = 0; i < len; ++i) { _attrList.add(attrsIn.item(i)); } _nsDeclList = 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 = null; } 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(_internName(prefix)); nsOut.add(_internNsURI(attr.getNodeValue())); } _attrList = (attrsOut == null) ? Collections.EMPTY_LIST : attrsOut; _nsDeclList = (nsOut == null) ? Collections.EMPTY_LIST : nsOut; } private void handleIllegalAttrIndex(int index) { Element elem = (Element) _currNode; 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; } /* /////////////////////////////////////////////// // Overridable error reporting methods /////////////////////////////////////////////// */ protected void reportWrongState(int errorType) { throw new IllegalStateException(findErrorDesc(errorType, _currEvent)); } protected void reportParseProblem(int errorType) throws XMLStreamException { throwStreamException(findErrorDesc(errorType, _currEvent)); } protected void throwStreamException(String msg) throws XMLStreamException { throwStreamException(msg, getErrorLocation()); } protected Location getErrorLocation() { Location loc = getCurrentLocation(); if (loc == null) { loc = getLocation(); } return loc; } /** * Method called to wrap or convert given conversion-fail exception * into a full {@link TypedXMLStreamException}, * * @param iae Problem as reported by converter * @param lexicalValue Lexical value (element content, attribute value) * that could not be converted succesfully. */ protected TypedXMLStreamException _constructTypeException(IllegalArgumentException iae, String lexicalValue) { String msg = iae.getMessage(); if (msg == null) { msg = ""; } Location loc = getStartLocation(); if (loc == null) { return new TypedXMLStreamException(lexicalValue, msg, iae); } return new TypedXMLStreamException(lexicalValue, msg, loc); } protected TypedXMLStreamException _constructTypeException(String msg, String lexicalValue) { Location loc = getStartLocation(); if (loc == null) { return new TypedXMLStreamException(lexicalValue, msg); } return new TypedXMLStreamException(lexicalValue, msg, loc); } /* /////////////////////////////////////////////// // Other internal methods /////////////////////////////////////////////// */ protected ValueDecoderFactory _decoderFactory() { if (_decoderFactory == null) { _decoderFactory = new ValueDecoderFactory(); } return _decoderFactory; } protected StringBase64Decoder _base64Decoder() { if (_base64Decoder == null) { _base64Decoder = new StringBase64Decoder(); } return _base64Decoder; } /** * Method used to locate error message description to use. * Calls sub-classes findErrorDesc() first, and only * if no message found, uses default messages defined here. */ protected String findErrorDesc(int errorType, int currEvent) { String evtDesc = Stax2Util.eventTypeDesc(currEvent); switch (errorType) { case ERR_STATE_NOT_START_ELEM: return "Current event "+evtDesc+", needs to be START_ELEMENT"; case ERR_STATE_NOT_ELEM: return "Current event "+evtDesc+", needs to be START_ELEMENT or END_ELEMENT"; case ERR_STATE_NO_LOCALNAME: return "Current event ("+evtDesc+") has no local name"; case ERR_STATE_NOT_PI: return "Current event ("+evtDesc+") needs to be PROCESSING_INSTRUCTION"; case ERR_STATE_NOT_TEXTUAL: return "Current event ("+evtDesc+") not a textual event"; case ERR_STATE_NOT_TEXTUAL_OR_ELEM: return "Current event ("+evtDesc+" not START_ELEMENT, END_ELEMENT, CHARACTERS or CDATA"; case ERR_STATE_NOT_TEXTUAL_XXX: return "Current event "+evtDesc+", needs to be one of CHARACTERS, CDATA, SPACE or COMMENT"; } // should never happen, but it'd be bad to throw another exception... return "Internal error (unrecognized error type: "+errorType+")"; } /** * Method called to do additional intern()ing for a name, if and as * necessary */ protected String _internName(String name) { if (name == null) { return ""; } return _cfgInternNames ? name.intern() : name; } protected String _internNsURI(String uri) { if (uri == null) { return ""; } return _cfgInternNsURIs ? uri.intern() : uri; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy