--- /dev/null 2014-03-04 17:56:11.000000000 +0100 +++ new/src/share/jaxws_classes/com/sun/xml/internal/org/jvnet/staxex/util/DOMStreamReader.java 2014-03-04 17:56:11.000000000 +0100 @@ -0,0 +1,895 @@ +/* + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.xml.internal.org.jvnet.staxex.util; + +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import static org.w3c.dom.Node.*; +import org.w3c.dom.ProcessingInstruction; +import org.w3c.dom.Text; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; +import javax.xml.stream.Location; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import java.util.Collections; +import java.util.Iterator; + +/** + * Create an {@link XMLStreamReader} on top of a DOM tree. + * + *
+ * Since various libraries as well as users often create "incorrect" DOM node, + * this class spends a lot of efforts making sure that broken DOM trees are + * nevertheless interpreted correctly. + * + *
+ * For example, if a DOM level + * 1 tree is passed, each method will attempt to return the correct value + * by using {@link Node#getNodeName()}. + * + *
+ * Similarly, if DOM is missing explicit namespace declarations, + * this class attempts to emulate necessary declarations. + * + * + * @author Santiago.PericasGeertsen@sun.com + * @author Kohsuke Kawaguchi + */ +public class DOMStreamReader implements XMLStreamReader, NamespaceContext { + + /** + * Current DOM node being traversed. + */ + protected Node _current; + + /** + * Starting node of the subtree being traversed. + */ + private Node _start; + + /** + * Named mapping for attributes and NS decls for the current node. + */ + private NamedNodeMap _namedNodeMap; + + /** + * If the reader points at {@link #CHARACTERS the text node}, + * its whole value. + * + *
+ * This is simply a cache of {@link Text#getWholeText()} of {@link #_current},
+ * but when a large binary data sent as base64 text, this could get very much
+ * non-trivial.
+ */
+ protected String wholeText;
+
+ /**
+ * List of attributes extracted from
+ * Makes sure that the namespace URI/prefix used in the given node is available,
+ * and if not, declare it on the current scope to "fix" it.
+ *
+ * It's often common to create DOM trees without putting namespace declarations,
+ * and this makes sure that such DOM tree will be properly marshalled.
+ */
+ private void ensureNs(Node n) {
+ String prefix = fixNull(n.getPrefix());
+ String uri = fixNull(n.getNamespaceURI());
+
+ Scope scope = scopes[depth];
+
+ String currentUri = scope.getNamespaceURI(prefix);
+
+ if(prefix.length()==0) {
+ currentUri = fixNull(currentUri);
+ if(currentUri.equals(uri))
+ return; // declared correctly
+ } else {
+ if(currentUri!=null && currentUri.equals(uri))
+ return; // declared correctly
+ }
+
+ if(prefix.equals("xml") || prefix.equals("xmlns"))
+ return; // implicitly declared namespaces
+
+ // needs to be declared
+ scope.additionalNamespaces.add(prefix);
+ scope.additionalNamespaces.add(uri);
+ }
+
+ /**
+ * Allocate new {@link Scope} for {@link #splitAttributes()}.
+ */
+ private Scope allocateScope() {
+ if(scopes.length==++depth) {
+ Scope[] newBuf = new Scope[scopes.length*2];
+ System.arraycopy(scopes,0,newBuf,0,scopes.length);
+ scopes = newBuf;
+ }
+ Scope scope = scopes[depth];
+ if(scope==null) {
+ scope = scopes[depth] = new Scope(scopes[depth-1]);
+ } else {
+ scope.reset();
+ }
+ return scope;
+ }
+
+ public int getAttributeCount() {
+ if (_state == START_ELEMENT)
+ return _currentAttributes.size();
+ throw new IllegalStateException("DOMStreamReader: getAttributeCount() called in illegal state");
+ }
+
+ /**
+ * Return an attribute's local name. Handle the case of DOM level 1 nodes.
+ */
+ public String getAttributeLocalName(int index) {
+ if (_state == START_ELEMENT) {
+ String localName = _currentAttributes.get(index).getLocalName();
+ return (localName != null) ? localName :
+ QName.valueOf(_currentAttributes.get(index).getNodeName()).getLocalPart();
+ }
+ throw new IllegalStateException("DOMStreamReader: getAttributeLocalName() called in illegal state");
+ }
+
+ /**
+ * Return an attribute's qname. Handle the case of DOM level 1 nodes.
+ */
+ public QName getAttributeName(int index) {
+ if (_state == START_ELEMENT) {
+ Node attr = _currentAttributes.get(index);
+ String localName = attr.getLocalName();
+ if (localName != null) {
+ String prefix = attr.getPrefix();
+ String uri = attr.getNamespaceURI();
+ return new QName(fixNull(uri), localName, fixNull(prefix));
+ }
+ else {
+ return QName.valueOf(attr.getNodeName());
+ }
+ }
+ throw new IllegalStateException("DOMStreamReader: getAttributeName() called in illegal state");
+ }
+
+ public String getAttributeNamespace(int index) {
+ if (_state == START_ELEMENT) {
+ String uri = _currentAttributes.get(index).getNamespaceURI();
+ return fixNull(uri);
+ }
+ throw new IllegalStateException("DOMStreamReader: getAttributeNamespace() called in illegal state");
+ }
+
+ public String getAttributePrefix(int index) {
+ if (_state == START_ELEMENT) {
+ String prefix = _currentAttributes.get(index).getPrefix();
+ return fixNull(prefix);
+ }
+ throw new IllegalStateException("DOMStreamReader: getAttributePrefix() called in illegal state");
+ }
+
+ public String getAttributeType(int index) {
+ if (_state == START_ELEMENT) {
+ return "CDATA";
+ }
+ throw new IllegalStateException("DOMStreamReader: getAttributeType() called in illegal state");
+ }
+
+ public String getAttributeValue(int index) {
+ if (_state == START_ELEMENT) {
+ return _currentAttributes.get(index).getNodeValue();
+ }
+ throw new IllegalStateException("DOMStreamReader: getAttributeValue() called in illegal state");
+ }
+
+ public String getAttributeValue(String namespaceURI, String localName) {
+ if (_state == START_ELEMENT) {
+ if (_namedNodeMap != null) {
+ Node attr = _namedNodeMap.getNamedItemNS(namespaceURI, localName);
+ return attr != null ? attr.getNodeValue() : null;
+ }
+ return null;
+ }
+ throw new IllegalStateException("DOMStreamReader: getAttributeValue() called in illegal state");
+ }
+
+ public String getCharacterEncodingScheme() {
+ return null;
+ }
+
+ public String getElementText() throws javax.xml.stream.XMLStreamException {
+ throw new RuntimeException("DOMStreamReader: getElementText() not implemented");
+ }
+
+ public String getEncoding() {
+ return null;
+ }
+
+ public int getEventType() {
+ return _state;
+ }
+
+ /**
+ * Return an element's local name. Handle the case of DOM level 1 nodes.
+ */
+ public String getLocalName() {
+ if (_state == START_ELEMENT || _state == END_ELEMENT) {
+ String localName = _current.getLocalName();
+ return localName != null ? localName :
+ QName.valueOf(_current.getNodeName()).getLocalPart();
+ }
+ else if (_state == ENTITY_REFERENCE) {
+ return _current.getNodeName();
+ }
+ throw new IllegalStateException("DOMStreamReader: getAttributeValue() called in illegal state");
+ }
+
+ public Location getLocation() {
+ return DummyLocation.INSTANCE;
+ }
+
+ /**
+ * Return an element's qname. Handle the case of DOM level 1 nodes.
+ */
+ public javax.xml.namespace.QName getName() {
+ if (_state == START_ELEMENT || _state == END_ELEMENT) {
+ String localName = _current.getLocalName();
+ if (localName != null) {
+ String prefix = _current.getPrefix();
+ String uri = _current.getNamespaceURI();
+ return new QName(fixNull(uri), localName, fixNull(prefix));
+ }
+ else {
+ return QName.valueOf(_current.getNodeName());
+ }
+ }
+ throw new IllegalStateException("DOMStreamReader: getName() called in illegal state");
+ }
+
+ public NamespaceContext getNamespaceContext() {
+ return this;
+ }
+
+ /**
+ * Verifies the current state to see if we can return the scope, and do so
+ * if appropriate.
+ *
+ * Used to implement a bunch of StAX API methods that have the same usage restriction.
+ */
+ private Scope getCheckedScope() {
+ if (_state == START_ELEMENT || _state == END_ELEMENT) {
+ return scopes[depth];
+ }
+ throw new IllegalStateException("DOMStreamReader: neither on START_ELEMENT nor END_ELEMENT");
+ }
+
+ public int getNamespaceCount() {
+ return getCheckedScope().getNamespaceCount();
+ }
+
+ public String getNamespacePrefix(int index) {
+ return getCheckedScope().getNamespacePrefix(index);
+ }
+
+ public String getNamespaceURI(int index) {
+ return getCheckedScope().getNamespaceURI(index);
+ }
+
+ public String getNamespaceURI() {
+ if (_state == START_ELEMENT || _state == END_ELEMENT) {
+ String uri = _current.getNamespaceURI();
+ return fixNull(uri);
+ }
+ return null;
+ }
+
+ /**
+ * This method is not particularly fast, but shouldn't be called very
+ * often. If we start to use it more, we should keep track of the
+ * NS declarations using a NamespaceContext implementation instead.
+ */
+ public String getNamespaceURI(String prefix) {
+ if (prefix == null) {
+ throw new IllegalArgumentException("DOMStreamReader: getNamespaceURI(String) call with a null prefix");
+ }
+ else if (prefix.equals("xml")) {
+ return "http://www.w3.org/XML/1998/namespace";
+ }
+ else if (prefix.equals("xmlns")) {
+ return "http://www.w3.org/2000/xmlns/";
+ }
+
+ // check scopes
+ String nsUri = scopes[depth].getNamespaceURI(prefix);
+ if(nsUri!=null) return nsUri;
+
+ // then ancestors above start node
+ Node node = findRootElement();
+ String nsDeclName = prefix.length()==0 ? "xmlns" : "xmlns:"+prefix;
+ while (node.getNodeType() != DOCUMENT_NODE) {
+ // Is ns declaration on this element?
+ NamedNodeMap namedNodeMap = node.getAttributes();
+ Attr attr = (Attr) namedNodeMap.getNamedItem(nsDeclName);
+ if (attr != null)
+ return attr.getValue();
+ node = node.getParentNode();
+ }
+ return null;
+ }
+
+ public String getPrefix(String nsUri) {
+ if (nsUri == null) {
+ throw new IllegalArgumentException("DOMStreamReader: getPrefix(String) call with a null namespace URI");
+ }
+ else if (nsUri.equals("http://www.w3.org/XML/1998/namespace")) {
+ return "xml";
+ }
+ else if (nsUri.equals("http://www.w3.org/2000/xmlns/")) {
+ return "xmlns";
+ }
+
+ // check scopes
+ String prefix = scopes[depth].getPrefix(nsUri);
+ if(prefix!=null) return prefix;
+
+ // then ancestors above start node
+ Node node = findRootElement();
+
+ while (node.getNodeType() != DOCUMENT_NODE) {
+ // Is ns declaration on this element?
+ NamedNodeMap namedNodeMap = node.getAttributes();
+ for( int i=namedNodeMap.getLength()-1; i>=0; i-- ) {
+ Attr attr = (Attr)namedNodeMap.item(i);
+ prefix = getPrefixForAttr(attr,nsUri);
+ if(prefix!=null)
+ return prefix;
+ }
+ node = node.getParentNode();
+ }
+ return null;
+ }
+
+ /**
+ * Finds the root element node of the traversal.
+ */
+ private Node findRootElement() {
+ int type;
+
+ Node node = _start;
+ while ((type = node.getNodeType()) != DOCUMENT_NODE
+ && type != ELEMENT_NODE) {
+ node = node.getParentNode();
+ }
+ return node;
+ }
+
+ /**
+ * If the given attribute is a namespace declaration for the given namespace URI,
+ * return its prefix. Otherwise null.
+ */
+ private static String getPrefixForAttr(Attr attr, String nsUri) {
+ String attrName = attr.getNodeName();
+ if (!attrName.startsWith("xmlns:") && !attrName.equals("xmlns"))
+ return null; // not nsdecl
+
+ if(attr.getValue().equals(nsUri)) {
+ if(attrName.equals("xmlns"))
+ return "";
+ String localName = attr.getLocalName();
+ return (localName != null) ? localName :
+ QName.valueOf(attrName).getLocalPart();
+ }
+
+ return null;
+ }
+
+ public Iterator getPrefixes(String nsUri) {
+ // This is an incorrect implementation,
+ // but AFAIK it's not used in the JAX-WS runtime
+ String prefix = getPrefix(nsUri);
+ if(prefix==null) return Collections.emptyList().iterator();
+ else return Collections.singletonList(prefix).iterator();
+ }
+
+ public String getPIData() {
+ if (_state == PROCESSING_INSTRUCTION) {
+ return ((ProcessingInstruction) _current).getData();
+ }
+ return null;
+ }
+
+ public String getPITarget() {
+ if (_state == PROCESSING_INSTRUCTION) {
+ return ((ProcessingInstruction) _current).getTarget();
+ }
+ return null;
+ }
+
+ public String getPrefix() {
+ if (_state == START_ELEMENT || _state == END_ELEMENT) {
+ String prefix = _current.getPrefix();
+ return fixNull(prefix);
+ }
+ return null;
+ }
+
+ public Object getProperty(String str) throws IllegalArgumentException {
+ return null;
+ }
+
+ public String getText() {
+ if (_state == CHARACTERS)
+ return wholeText;
+ if(_state == CDATA || _state == COMMENT || _state == ENTITY_REFERENCE)
+ return _current.getNodeValue();
+ throw new IllegalStateException("DOMStreamReader: getTextLength() called in illegal state");
+ }
+
+ public char[] getTextCharacters() {
+ return getText().toCharArray();
+ }
+
+ public int getTextCharacters(int sourceStart, char[] target, int targetStart,
+ int targetLength) throws XMLStreamException {
+ String text = getText();
+ int copiedSize = Math.min(targetLength, text.length() - sourceStart);
+ text.getChars(sourceStart, sourceStart + copiedSize, target, targetStart);
+
+ return copiedSize;
+ }
+
+ public int getTextLength() {
+ return getText().length();
+ }
+
+ public int getTextStart() {
+ if (_state == CHARACTERS || _state == CDATA || _state == COMMENT || _state == ENTITY_REFERENCE) {
+ return 0;
+ }
+ throw new IllegalStateException("DOMStreamReader: getTextStart() called in illegal state");
+ }
+
+ public String getVersion() {
+ return null;
+ }
+
+ public boolean hasName() {
+ return (_state == START_ELEMENT || _state == END_ELEMENT);
+ }
+
+ public boolean hasNext() throws javax.xml.stream.XMLStreamException {
+ return (_state != END_DOCUMENT);
+ }
+
+ public boolean hasText() {
+ if (_state == CHARACTERS || _state == CDATA || _state == COMMENT || _state == ENTITY_REFERENCE) {
+ return getText().trim().length() > 0;
+ }
+ return false;
+ }
+
+ public boolean isAttributeSpecified(int param) {
+ return false;
+ }
+
+ public boolean isCharacters() {
+ return (_state == CHARACTERS);
+ }
+
+ public boolean isEndElement() {
+ return (_state == END_ELEMENT);
+ }
+
+ public boolean isStandalone() {
+ return true;
+ }
+
+ public boolean isStartElement() {
+ return (_state == START_ELEMENT);
+ }
+
+ public boolean isWhiteSpace() {
+ if (_state == CHARACTERS || _state == CDATA)
+ return getText().trim().length()==0;
+ return false;
+ }
+
+ private static int mapNodeTypeToState(int nodetype) {
+ switch (nodetype) {
+ case CDATA_SECTION_NODE:
+ return CDATA;
+ case COMMENT_NODE:
+ return COMMENT;
+ case ELEMENT_NODE:
+ return START_ELEMENT;
+ case ENTITY_NODE:
+ return ENTITY_DECLARATION;
+ case ENTITY_REFERENCE_NODE:
+ return ENTITY_REFERENCE;
+ case NOTATION_NODE:
+ return NOTATION_DECLARATION;
+ case PROCESSING_INSTRUCTION_NODE:
+ return PROCESSING_INSTRUCTION;
+ case TEXT_NODE:
+ return CHARACTERS;
+ default:
+ throw new RuntimeException("DOMStreamReader: Unexpected node type");
+ }
+ }
+
+ public int next() throws XMLStreamException {
+ while(true) {
+ int r = _next();
+ switch (r) {
+ case CHARACTERS:
+ // if we are currently at text node, make sure that this is a meaningful text node.
+ Node prev = _current.getPreviousSibling();
+ if(prev!=null && prev.getNodeType()==Node.TEXT_NODE)
+ continue; // nope. this is just a continuation of previous text that should be invisible
+
+ Text t = (Text)_current;
+ wholeText = t.getWholeText();
+ if(wholeText.length()==0)
+ continue; // nope. this is empty text.
+ return CHARACTERS;
+ case START_ELEMENT:
+ splitAttributes();
+ return START_ELEMENT;
+ default:
+ return r;
+ }
+ }
+ }
+
+ protected int _next() throws XMLStreamException {
+ Node child;
+
+ switch (_state) {
+ case END_DOCUMENT:
+ throw new IllegalStateException("DOMStreamReader: Calling next() at END_DOCUMENT");
+ case START_DOCUMENT:
+ // Don't skip document element if this is a fragment
+ if (_current.getNodeType() == ELEMENT_NODE) {
+ return (_state = START_ELEMENT);
+ }
+
+ child = _current.getFirstChild();
+ if (child == null) {
+ return (_state = END_DOCUMENT);
+ }
+ else {
+ _current = child;
+ return (_state = mapNodeTypeToState(_current.getNodeType()));
+ }
+ case START_ELEMENT:
+ child = _current.getFirstChild();
+ if (child == null) {
+ return (_state = END_ELEMENT);
+ }
+ else {
+ _current = child;
+ return (_state = mapNodeTypeToState(_current.getNodeType()));
+ }
+ case END_ELEMENT:
+ case CHARACTERS:
+ case COMMENT:
+ case CDATA:
+ case ENTITY_REFERENCE:
+ case PROCESSING_INSTRUCTION:
+ if (_state == END_ELEMENT) depth--;
+ // If at the end of this fragment, then terminate traversal
+ if (_current == _start) {
+ return (_state = END_DOCUMENT);
+ }
+
+ Node sibling = _current.getNextSibling();
+ if (sibling == null) {
+ _current = _current.getParentNode();
+ // getParentNode() returns null for fragments
+ _state = (_current == null || _current.getNodeType() == DOCUMENT_NODE) ?
+ END_DOCUMENT : END_ELEMENT;
+ return _state;
+ }
+ else {
+ _current = sibling;
+ return (_state = mapNodeTypeToState(_current.getNodeType()));
+ }
+ case DTD:
+ case ATTRIBUTE:
+ case NAMESPACE:
+ default:
+ throw new RuntimeException("DOMStreamReader: Unexpected internal state");
+ }
+ }
+
+ public int nextTag() throws javax.xml.stream.XMLStreamException {
+ int eventType = next();
+ while (eventType == CHARACTERS && isWhiteSpace()
+ || eventType == CDATA && isWhiteSpace()
+ || eventType == SPACE
+ || eventType == PROCESSING_INSTRUCTION
+ || eventType == COMMENT)
+ {
+ eventType = next();
+ }
+ if (eventType != START_ELEMENT && eventType != END_ELEMENT) {
+ throw new XMLStreamException("DOMStreamReader: Expected start or end tag");
+ }
+ return eventType;
+ }
+
+ public void require(int type, String namespaceURI, String localName)
+ throws javax.xml.stream.XMLStreamException
+ {
+ if (type != _state) {
+ throw new XMLStreamException("DOMStreamReader: Required event type not found");
+ }
+ if (namespaceURI != null && !namespaceURI.equals(getNamespaceURI())) {
+ throw new XMLStreamException("DOMStreamReader: Required namespaceURI not found");
+ }
+ if (localName != null && !localName.equals(getLocalName())) {
+ throw new XMLStreamException("DOMStreamReader: Required localName not found");
+ }
+ }
+
+ public boolean standaloneSet() {
+ return true;
+ }
+
+
+
+ // -- Debugging ------------------------------------------------------
+/*
+ private static void displayDOM(Node node, java.io.OutputStream ostream) {
+ try {
+ System.out.println("\n====\n");
+ XmlUtil.newTransformer().transform(
+ new DOMSource(node), new StreamResult(ostream));
+ System.out.println("\n====\n");
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void verifyDOMIntegrity(Node node) {
+ switch (node.getNodeType()) {
+ case ELEMENT_NODE:
+ case ATTRIBUTE_NODE:
+
+ // DOM level 1?
+ if (node.getLocalName() == null) {
+ System.out.println("WARNING: DOM level 1 node found");
+ System.out.println(" -> node.getNodeName() = " + node.getNodeName());
+ System.out.println(" -> node.getNamespaceURI() = " + node.getNamespaceURI());
+ System.out.println(" -> node.getLocalName() = " + node.getLocalName());
+ System.out.println(" -> node.getPrefix() = " + node.getPrefix());
+ }
+
+ if (node.getNodeType() == ATTRIBUTE_NODE) return;
+
+ NamedNodeMap attrs = node.getAttributes();
+ for (int i = 0; i < attrs.getLength(); i++) {
+ verifyDOMIntegrity(attrs.item(i));
+ }
+ case DOCUMENT_NODE:
+ NodeList children = node.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ verifyDOMIntegrity(children.item(i));
+ }
+ }
+ }
+*/
+
+ private static String fixNull(String s) {
+ if(s==null) return "";
+ else return s;
+ }
+}
_namedNodeMap
.
+ */
+ private final FinalArrayList_namedNodeMap
+ */
+ final FinalArrayList