src/share/jaxws_classes/com/sun/xml/internal/ws/streaming/DOMStreamReader.java

Thu, 31 Aug 2017 15:18:52 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:18:52 +0800
changeset 637
9c07ef4934dd
parent 368
0989ad8c0860
parent 0
373ffda63c9a
permissions
-rw-r--r--

merge

     1 /*
     2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.xml.internal.ws.streaming;
    28 import com.sun.istack.internal.FinalArrayList;
    29 import com.sun.istack.internal.NotNull;
    30 import com.sun.istack.internal.XMLStreamException2;
    31 import com.sun.xml.internal.ws.util.xml.DummyLocation;
    32 import com.sun.xml.internal.ws.util.xml.XmlUtil;
    33 import org.w3c.dom.Attr;
    34 import org.w3c.dom.Element;
    35 import org.w3c.dom.NamedNodeMap;
    36 import org.w3c.dom.Node;
    37 import static org.w3c.dom.Node.*;
    38 import org.w3c.dom.NodeList;
    39 import org.w3c.dom.ProcessingInstruction;
    40 import org.w3c.dom.Text;
    42 import javax.xml.namespace.NamespaceContext;
    43 import javax.xml.namespace.QName;
    44 import javax.xml.stream.Location;
    45 import javax.xml.stream.XMLStreamException;
    46 import javax.xml.stream.XMLStreamReader;
    47 import javax.xml.transform.dom.DOMSource;
    48 import javax.xml.transform.stream.StreamResult;
    49 import java.util.Collections;
    50 import java.util.Iterator;
    52 /**
    53  * Create an {@link XMLStreamReader} on top of a DOM tree.
    54  *
    55  * <p>
    56  * Since various libraries as well as users often create "incorrect" DOM node,
    57  * this class spends a lot of efforts making sure that broken DOM trees are
    58  * nevertheless interpreted correctly.
    59  *
    60  * <p>
    61  * For example, if a DOM level
    62  * 1 tree is passed, each method will attempt to return the correct value
    63  * by using {@link Node#getNodeName()}.
    64  *
    65  * <p>
    66  * Similarly, if DOM is missing explicit namespace declarations,
    67  * this class attempts to emulate necessary declarations.
    68  *
    69  *
    70  * @author Santiago.PericasGeertsen@sun.com
    71  * @author Kohsuke Kawaguchi
    72  */
    73 public class DOMStreamReader implements XMLStreamReader, NamespaceContext {
    75     /**
    76      * Current DOM node being traversed.
    77      */
    78     protected Node _current;
    80     /**
    81      * Starting node of the subtree being traversed.
    82      */
    83     private Node _start;
    85     /**
    86      * Named mapping for attributes and NS decls for the current node.
    87      */
    88     private NamedNodeMap _namedNodeMap;
    90     /**
    91      * If the reader points at {@link #CHARACTERS the text node},
    92      * its whole value.
    93      *
    94      * <p>
    95      * This is simply a cache of {@link Text#getWholeText()} of {@link #_current},
    96      * but when a large binary data sent as base64 text, this could get very much
    97      * non-trivial.
    98      */
    99     protected String wholeText;
   101     /**
   102      * List of attributes extracted from <code>_namedNodeMap</code>.
   103      */
   104     private final FinalArrayList<Attr> _currentAttributes = new FinalArrayList<Attr>();
   106     /**
   107      * {@link Scope} buffer.
   108      */
   109     protected Scope[] scopes = new Scope[8];
   111     /**
   112      * Depth of the current element. The first element gets depth==0.
   113      * Also used as the index to {@link #scopes}.
   114      */
   115     protected int depth = 0;
   117     /**
   118      * State of this reader. Any of the valid states defined in StAX'
   119      * XMLStreamConstants class.
   120      */
   121     protected int _state;
   123     /**
   124      * Namespace declarations on one element.
   125      *
   126      * Instances are reused.
   127      */
   128     protected static final class Scope {
   129         /**
   130          * Scope for the parent element.
   131          */
   132         final Scope parent;
   134         /**
   135          * List of namespace declarations extracted from <code>_namedNodeMap</code>
   136          */
   137         final FinalArrayList<Attr> currentNamespaces = new FinalArrayList<Attr>();
   139         /**
   140          * Additional namespace declarations obtained as a result of "fixing" DOM tree,
   141          * which were not part of the original DOM tree.
   142          *
   143          * One entry occupies two spaces (prefix followed by URI.)
   144          */
   145         final FinalArrayList<String> additionalNamespaces = new FinalArrayList<String>();
   147         Scope(Scope parent) {
   148             this.parent = parent;
   149         }
   151         void reset() {
   152             currentNamespaces.clear();
   153             additionalNamespaces.clear();
   154         }
   156         int getNamespaceCount() {
   157             return currentNamespaces.size()+additionalNamespaces.size()/2;
   158         }
   160         String getNamespacePrefix(int index) {
   161             int sz = currentNamespaces.size();
   162             if(index< sz) {
   163                 Attr attr = currentNamespaces.get(index);
   164                 String result = attr.getLocalName();
   165                 if (result == null) {
   166                     result = QName.valueOf(attr.getNodeName()).getLocalPart();
   167                 }
   168                 return result.equals("xmlns") ? null : result;
   169             } else {
   170                 return additionalNamespaces.get((index-sz)*2);
   171             }
   172         }
   174         String getNamespaceURI(int index) {
   175             int sz = currentNamespaces.size();
   176             if(index< sz) {
   177                 return currentNamespaces.get(index).getValue();
   178             } else {
   179                 return additionalNamespaces.get((index-sz)*2+1);
   180             }
   181         }
   183         /**
   184          * Returns the prefix bound to the given URI, or null.
   185          * This method recurses to the parent.
   186          */
   187         String getPrefix(String nsUri) {
   188             for( Scope sp=this; sp!=null; sp=sp.parent ) {
   189                 for( int i=sp.currentNamespaces.size()-1; i>=0; i--) {
   190                     String result = getPrefixForAttr(sp.currentNamespaces.get(i),nsUri);
   191                     if(result!=null)
   192                         return result;
   193                 }
   194                 for( int i=sp.additionalNamespaces.size()-2; i>=0; i-=2 )
   195                     if(sp.additionalNamespaces.get(i+1).equals(nsUri))
   196                         return sp.additionalNamespaces.get(i);
   197             }
   198             return null;
   199         }
   201         /**
   202          * Returns the namespace URI bound by the given prefix.
   203          *
   204          * @param prefix
   205          *      Prefix to look up.
   206          */
   207         String getNamespaceURI(@NotNull String prefix) {
   208             String nsDeclName = prefix.length()==0 ? "xmlns" : "xmlns:"+prefix;
   210             for( Scope sp=this; sp!=null; sp=sp.parent ) {
   211                 for( int i=sp.currentNamespaces.size()-1; i>=0; i--) {
   212                     Attr a = sp.currentNamespaces.get(i);
   213                     if(a.getNodeName().equals(nsDeclName))
   214                         return a.getValue();
   215                 }
   216                 for( int i=sp.additionalNamespaces.size()-2; i>=0; i-=2 )
   217                     if(sp.additionalNamespaces.get(i).equals(prefix))
   218                         return sp.additionalNamespaces.get(i+1);
   219             }
   220             return null;
   221         }
   222     }
   225     public DOMStreamReader() {
   226     }
   228     public DOMStreamReader(Node node) {
   229         setCurrentNode(node);
   230     }
   232     public void setCurrentNode(Node node) {
   233         scopes[0] = new Scope(null);
   234         depth=0;
   236         _start = _current = node;
   237         _state = START_DOCUMENT;
   238         // verifyDOMIntegrity(node);
   239         // displayDOM(node, System.out);
   240     }
   242     public void close() throws XMLStreamException {
   243     }
   245     /**
   246      * Called when the current node is {@link Element} to look at attribute list
   247      * (which contains both ns decl and attributes in DOM) and split them
   248      * to attributes-proper and namespace decls.
   249      */
   250     protected void splitAttributes() {
   251         // Clear attribute and namespace lists
   252         _currentAttributes.clear();
   254         Scope scope = allocateScope();
   256         _namedNodeMap = _current.getAttributes();
   257         if (_namedNodeMap != null) {
   258             final int n = _namedNodeMap.getLength();
   259             for (int i = 0; i < n; i++) {
   260                 final Attr attr = (Attr) _namedNodeMap.item(i);
   261                 final String attrName = attr.getNodeName();
   262                 if (attrName.startsWith("xmlns:") || attrName.equals("xmlns")) {     // NS decl?
   263                     scope.currentNamespaces.add(attr);
   264                 }
   265                 else {
   266                     _currentAttributes.add(attr);
   267                 }
   268             }
   269         }
   271         // verify that all the namespaces used in element and attributes are indeed available
   272         ensureNs(_current);
   273         for( int i=_currentAttributes.size()-1; i>=0; i-- ) {
   274             Attr a = _currentAttributes.get(i);
   275             if(fixNull(a.getNamespaceURI()).length()>0)
   276                 ensureNs(a);    // no need to declare "" for attributes in the default namespace
   277         }
   278     }
   280     /**
   281      * Sub-routine of {@link #splitAttributes()}.
   282      *
   283      * <p>
   284      * Makes sure that the namespace URI/prefix used in the given node is available,
   285      * and if not, declare it on the current scope to "fix" it.
   286      *
   287      * It's often common to create DOM trees without putting namespace declarations,
   288      * and this makes sure that such DOM tree will be properly marshalled.
   289      */
   290     private void ensureNs(Node n) {
   291         String prefix = fixNull(n.getPrefix());
   292         String uri = fixNull(n.getNamespaceURI());
   294         Scope scope = scopes[depth];
   296         String currentUri = scope.getNamespaceURI(prefix);
   298         if(prefix.length()==0) {
   299             currentUri = fixNull(currentUri);
   300             if(currentUri.equals(uri))
   301                 return; // declared correctly
   302         } else {
   303             if(currentUri!=null && currentUri.equals(uri))
   304                 return; // declared correctly
   305         }
   307         if(prefix.equals("xml") || prefix.equals("xmlns"))
   308             return; // implicitly declared namespaces
   310         // needs to be declared
   311         scope.additionalNamespaces.add(prefix);
   312         scope.additionalNamespaces.add(uri);
   313     }
   315     /**
   316      * Allocate new {@link Scope} for {@link #splitAttributes()}.
   317      */
   318     private Scope allocateScope() {
   319         if(scopes.length==++depth) {
   320             Scope[] newBuf = new Scope[scopes.length*2];
   321             System.arraycopy(scopes,0,newBuf,0,scopes.length);
   322             scopes = newBuf;
   323         }
   324         Scope scope = scopes[depth];
   325         if(scope==null) {
   326             scope = scopes[depth] = new Scope(scopes[depth-1]);
   327         } else {
   328             scope.reset();
   329         }
   330         return scope;
   331     }
   333     public int getAttributeCount() {
   334         if (_state == START_ELEMENT)
   335             return _currentAttributes.size();
   336         throw new IllegalStateException("DOMStreamReader: getAttributeCount() called in illegal state");
   337     }
   339     /**
   340      * Return an attribute's local name. Handle the case of DOM level 1 nodes.
   341      */
   342     public String getAttributeLocalName(int index) {
   343         if (_state == START_ELEMENT) {
   344             String localName = _currentAttributes.get(index).getLocalName();
   345             return (localName != null) ? localName :
   346                 QName.valueOf(_currentAttributes.get(index).getNodeName()).getLocalPart();
   347         }
   348         throw new IllegalStateException("DOMStreamReader: getAttributeLocalName() called in illegal state");
   349     }
   351     /**
   352      * Return an attribute's qname. Handle the case of DOM level 1 nodes.
   353      */
   354     public QName getAttributeName(int index) {
   355         if (_state == START_ELEMENT) {
   356             Node attr = _currentAttributes.get(index);
   357             String localName = attr.getLocalName();
   358             if (localName != null) {
   359                 String prefix = attr.getPrefix();
   360                 String uri = attr.getNamespaceURI();
   361                 return new QName(fixNull(uri), localName, fixNull(prefix));
   362             }
   363             else {
   364                 return QName.valueOf(attr.getNodeName());
   365             }
   366         }
   367         throw new IllegalStateException("DOMStreamReader: getAttributeName() called in illegal state");
   368     }
   370     public String getAttributeNamespace(int index) {
   371         if (_state == START_ELEMENT) {
   372             String uri = _currentAttributes.get(index).getNamespaceURI();
   373             return fixNull(uri);
   374         }
   375         throw new IllegalStateException("DOMStreamReader: getAttributeNamespace() called in illegal state");
   376     }
   378     public String getAttributePrefix(int index) {
   379         if (_state == START_ELEMENT) {
   380             String prefix = _currentAttributes.get(index).getPrefix();
   381             return fixNull(prefix);
   382         }
   383         throw new IllegalStateException("DOMStreamReader: getAttributePrefix() called in illegal state");
   384     }
   386     public String getAttributeType(int index) {
   387         if (_state == START_ELEMENT) {
   388             return "CDATA";
   389         }
   390         throw new IllegalStateException("DOMStreamReader: getAttributeType() called in illegal state");
   391     }
   393     public String getAttributeValue(int index) {
   394         if (_state == START_ELEMENT) {
   395             return _currentAttributes.get(index).getNodeValue();
   396         }
   397         throw new IllegalStateException("DOMStreamReader: getAttributeValue() called in illegal state");
   398     }
   400     public String getAttributeValue(String namespaceURI, String localName) {
   401         if (_state == START_ELEMENT) {
   402             if (_namedNodeMap != null) {
   403                 Node attr = _namedNodeMap.getNamedItemNS(namespaceURI, localName);
   404                 return attr != null ? attr.getNodeValue() : null;
   405             }
   406             return null;
   407         }
   408         throw new IllegalStateException("DOMStreamReader: getAttributeValue() called in illegal state");
   409     }
   411     public String getCharacterEncodingScheme() {
   412         return null;
   413     }
   415     public String getElementText() throws javax.xml.stream.XMLStreamException {
   416         throw new RuntimeException("DOMStreamReader: getElementText() not implemented");
   417     }
   419     public String getEncoding() {
   420         return null;
   421     }
   423     public int getEventType() {
   424         return _state;
   425     }
   427     /**
   428      * Return an element's local name. Handle the case of DOM level 1 nodes.
   429      */
   430     public String getLocalName() {
   431         if (_state == START_ELEMENT || _state == END_ELEMENT) {
   432             String localName = _current.getLocalName();
   433             return localName != null ? localName :
   434                 QName.valueOf(_current.getNodeName()).getLocalPart();
   435         }
   436         else if (_state == ENTITY_REFERENCE) {
   437             return _current.getNodeName();
   438         }
   439         throw new IllegalStateException("DOMStreamReader: getAttributeValue() called in illegal state");
   440     }
   442     public Location getLocation() {
   443         return DummyLocation.INSTANCE;
   444     }
   446     /**
   447      * Return an element's qname. Handle the case of DOM level 1 nodes.
   448      */
   449     public javax.xml.namespace.QName getName() {
   450         if (_state == START_ELEMENT || _state == END_ELEMENT) {
   451             String localName = _current.getLocalName();
   452             if (localName != null) {
   453                 String prefix = _current.getPrefix();
   454                 String uri = _current.getNamespaceURI();
   455                 return new QName(fixNull(uri), localName, fixNull(prefix));
   456             }
   457             else {
   458                 return QName.valueOf(_current.getNodeName());
   459             }
   460         }
   461         throw new IllegalStateException("DOMStreamReader: getName() called in illegal state");
   462     }
   464     public NamespaceContext getNamespaceContext() {
   465         return this;
   466     }
   468     /**
   469      * Verifies the current state to see if we can return the scope, and do so
   470      * if appropriate.
   471      *
   472      * Used to implement a bunch of StAX API methods that have the same usage restriction.
   473      */
   474     private Scope getCheckedScope() {
   475         if (_state == START_ELEMENT || _state == END_ELEMENT) {
   476             return scopes[depth];
   477         }
   478         throw new IllegalStateException("DOMStreamReader: neither on START_ELEMENT nor END_ELEMENT");
   479     }
   481     public int getNamespaceCount() {
   482         return getCheckedScope().getNamespaceCount();
   483     }
   485     public String getNamespacePrefix(int index) {
   486         return getCheckedScope().getNamespacePrefix(index);
   487     }
   489     public String getNamespaceURI(int index) {
   490         return getCheckedScope().getNamespaceURI(index);
   491     }
   493     public String getNamespaceURI() {
   494         if (_state == START_ELEMENT || _state == END_ELEMENT) {
   495             String uri = _current.getNamespaceURI();
   496             return fixNull(uri);
   497         }
   498         return null;
   499     }
   501     /**
   502      * This method is not particularly fast, but shouldn't be called very
   503      * often. If we start to use it more, we should keep track of the
   504      * NS declarations using a NamespaceContext implementation instead.
   505      */
   506     public String getNamespaceURI(String prefix) {
   507         if (prefix == null) {
   508             throw new IllegalArgumentException("DOMStreamReader: getNamespaceURI(String) call with a null prefix");
   509         }
   510         else if (prefix.equals("xml")) {
   511             return "http://www.w3.org/XML/1998/namespace";
   512         }
   513         else if (prefix.equals("xmlns")) {
   514             return "http://www.w3.org/2000/xmlns/";
   515         }
   517         // check scopes
   518         String nsUri = scopes[depth].getNamespaceURI(prefix);
   519         if(nsUri!=null)    return nsUri;
   521         // then ancestors above start node
   522         Node node = findRootElement();
   523         String nsDeclName = prefix.length()==0 ? "xmlns" : "xmlns:"+prefix;
   524         while (node.getNodeType() != DOCUMENT_NODE) {
   525             // Is ns declaration on this element?
   526             NamedNodeMap namedNodeMap = node.getAttributes();
   527             Attr attr = (Attr) namedNodeMap.getNamedItem(nsDeclName);
   528             if (attr != null)
   529                 return attr.getValue();
   530             node = node.getParentNode();
   531         }
   532         return null;
   533     }
   535     public String getPrefix(String nsUri) {
   536         if (nsUri == null) {
   537             throw new IllegalArgumentException("DOMStreamReader: getPrefix(String) call with a null namespace URI");
   538         }
   539         else if (nsUri.equals("http://www.w3.org/XML/1998/namespace")) {
   540             return "xml";
   541         }
   542         else if (nsUri.equals("http://www.w3.org/2000/xmlns/")) {
   543             return "xmlns";
   544         }
   546         // check scopes
   547         String prefix = scopes[depth].getPrefix(nsUri);
   548         if(prefix!=null)    return prefix;
   550         // then ancestors above start node
   551         Node node = findRootElement();
   553         while (node.getNodeType() != DOCUMENT_NODE) {
   554             // Is ns declaration on this element?
   555             NamedNodeMap namedNodeMap = node.getAttributes();
   556             for( int i=namedNodeMap.getLength()-1; i>=0; i-- ) {
   557                 Attr attr = (Attr)namedNodeMap.item(i);
   558                 prefix = getPrefixForAttr(attr,nsUri);
   559                 if(prefix!=null)
   560                     return prefix;
   561             }
   562             node = node.getParentNode();
   563         }
   564         return null;
   565     }
   567     /**
   568      * Finds the root element node of the traversal.
   569      */
   570     private Node findRootElement() {
   571         int type;
   573         Node node = _start;
   574         while ((type = node.getNodeType()) != DOCUMENT_NODE
   575                 && type != ELEMENT_NODE) {
   576             node = node.getParentNode();
   577         }
   578         return node;
   579     }
   581     /**
   582      * If the given attribute is a namespace declaration for the given namespace URI,
   583      * return its prefix. Otherwise null.
   584      */
   585     private static String getPrefixForAttr(Attr attr, String nsUri) {
   586         String attrName = attr.getNodeName();
   587         if (!attrName.startsWith("xmlns:") && !attrName.equals("xmlns"))
   588             return null;    // not nsdecl
   590         if(attr.getValue().equals(nsUri)) {
   591             if(attrName.equals("xmlns"))
   592                 return "";
   593             String localName = attr.getLocalName();
   594             return (localName != null) ? localName :
   595                 QName.valueOf(attrName).getLocalPart();
   596         }
   598         return null;
   599     }
   601     public Iterator getPrefixes(String nsUri) {
   602         // This is an incorrect implementation,
   603         // but AFAIK it's not used in the JAX-WS runtime
   604         String prefix = getPrefix(nsUri);
   605         if(prefix==null)    return Collections.emptyList().iterator();
   606         else                return Collections.singletonList(prefix).iterator();
   607     }
   609     public String getPIData() {
   610         if (_state == PROCESSING_INSTRUCTION) {
   611             return ((ProcessingInstruction) _current).getData();
   612         }
   613         return null;
   614     }
   616     public String getPITarget() {
   617         if (_state == PROCESSING_INSTRUCTION) {
   618             return ((ProcessingInstruction) _current).getTarget();
   619         }
   620         return null;
   621     }
   623     public String getPrefix() {
   624         if (_state == START_ELEMENT || _state == END_ELEMENT) {
   625             String prefix = _current.getPrefix();
   626             return fixNull(prefix);
   627         }
   628         return null;
   629     }
   631     public Object getProperty(String str) throws IllegalArgumentException {
   632         return null;
   633     }
   635     public String getText() {
   636         if (_state == CHARACTERS)
   637             return wholeText;
   638         if(_state == CDATA || _state == COMMENT || _state == ENTITY_REFERENCE)
   639             return _current.getNodeValue();
   640         throw new IllegalStateException("DOMStreamReader: getTextLength() called in illegal state");
   641     }
   643     public char[] getTextCharacters() {
   644         return getText().toCharArray();
   645     }
   647     public int getTextCharacters(int sourceStart, char[] target, int targetStart,
   648                                  int targetLength) throws XMLStreamException {
   649         String text = getText();
   650         int copiedSize = Math.min(targetLength, text.length() - sourceStart);
   651         text.getChars(sourceStart, sourceStart + copiedSize, target, targetStart);
   653         return copiedSize;
   654     }
   656     public int getTextLength() {
   657         return getText().length();
   658     }
   660     public int getTextStart() {
   661         if (_state == CHARACTERS || _state == CDATA || _state == COMMENT || _state == ENTITY_REFERENCE) {
   662             return 0;
   663         }
   664         throw new IllegalStateException("DOMStreamReader: getTextStart() called in illegal state");
   665     }
   667     public String getVersion() {
   668         return null;
   669     }
   671     public boolean hasName() {
   672         return (_state == START_ELEMENT || _state == END_ELEMENT);
   673     }
   675     public boolean hasNext() throws javax.xml.stream.XMLStreamException {
   676         return (_state != END_DOCUMENT);
   677     }
   679     public boolean hasText() {
   680         if (_state == CHARACTERS || _state == CDATA || _state == COMMENT || _state == ENTITY_REFERENCE) {
   681             return getText().trim().length() > 0;
   682         }
   683         return false;
   684     }
   686     public boolean isAttributeSpecified(int param) {
   687         return false;
   688     }
   690     public boolean isCharacters() {
   691         return (_state == CHARACTERS);
   692     }
   694     public boolean isEndElement() {
   695         return (_state == END_ELEMENT);
   696     }
   698     public boolean isStandalone() {
   699         return true;
   700     }
   702     public boolean isStartElement() {
   703         return (_state == START_ELEMENT);
   704     }
   706     public boolean isWhiteSpace() {
   707         if (_state == CHARACTERS || _state == CDATA)
   708             return getText().trim().length()==0;
   709         return false;
   710     }
   712     private static int mapNodeTypeToState(int nodetype) {
   713         switch (nodetype) {
   714             case CDATA_SECTION_NODE:
   715                 return CDATA;
   716             case COMMENT_NODE:
   717                 return COMMENT;
   718             case ELEMENT_NODE:
   719                 return START_ELEMENT;
   720             case ENTITY_NODE:
   721                 return ENTITY_DECLARATION;
   722             case ENTITY_REFERENCE_NODE:
   723                 return ENTITY_REFERENCE;
   724             case NOTATION_NODE:
   725                 return NOTATION_DECLARATION;
   726             case PROCESSING_INSTRUCTION_NODE:
   727                 return PROCESSING_INSTRUCTION;
   728             case TEXT_NODE:
   729                 return CHARACTERS;
   730             default:
   731                 throw new RuntimeException("DOMStreamReader: Unexpected node type");
   732         }
   733     }
   735     public int next() throws XMLStreamException {
   736         while(true) {
   737             int r = _next();
   738             switch (r) {
   739             case CHARACTERS:
   740                 // if we are currently at text node, make sure that this is a meaningful text node.
   741                 Node prev = _current.getPreviousSibling();
   742                 if(prev!=null && prev.getNodeType()==Node.TEXT_NODE)
   743                     continue;   // nope. this is just a continuation of previous text that should be invisible
   745                 Text t = (Text)_current;
   746                 wholeText = t.getWholeText();
   747                 if(wholeText.length()==0)
   748                     continue;   // nope. this is empty text.
   749                 return CHARACTERS;
   750             case START_ELEMENT:
   751                 splitAttributes();
   752                 return START_ELEMENT;
   753             default:
   754                 return r;
   755             }
   756         }
   757     }
   759     protected int _next() throws XMLStreamException {
   760         Node child;
   762         switch (_state) {
   763             case END_DOCUMENT:
   764                 throw new IllegalStateException("DOMStreamReader: Calling next() at END_DOCUMENT");
   765             case START_DOCUMENT:
   766                 // Don't skip document element if this is a fragment
   767                 if (_current.getNodeType() == ELEMENT_NODE) {
   768                     return (_state = START_ELEMENT);
   769                 }
   771                 child = _current.getFirstChild();
   772                 if (child == null) {
   773                     return (_state = END_DOCUMENT);
   774                 }
   775                 else {
   776                     _current = child;
   777                     return (_state = mapNodeTypeToState(_current.getNodeType()));
   778                 }
   779             case START_ELEMENT:
   780                 child = _current.getFirstChild();
   781                 if (child == null) {
   782                     return (_state = END_ELEMENT);
   783                 }
   784                 else {
   785                     _current = child;
   786                     return (_state = mapNodeTypeToState(_current.getNodeType()));
   787                 }
   788             case END_ELEMENT:
   789                 depth--;
   790                 // fall through next
   791             case CHARACTERS:
   792             case COMMENT:
   793             case CDATA:
   794             case ENTITY_REFERENCE:
   795             case PROCESSING_INSTRUCTION:
   796                 // If at the end of this fragment, then terminate traversal
   797                 if (_current == _start) {
   798                     return (_state = END_DOCUMENT);
   799                 }
   801                 Node sibling = _current.getNextSibling();
   802                 if (sibling == null) {
   803                     _current = _current.getParentNode();
   804                     // getParentNode() returns null for fragments
   805                     _state = (_current == null || _current.getNodeType() == DOCUMENT_NODE) ?
   806                              END_DOCUMENT : END_ELEMENT;
   807                     return _state;
   808                 }
   809                 else {
   810                     _current = sibling;
   811                     return (_state = mapNodeTypeToState(_current.getNodeType()));
   812                 }
   813             case DTD:
   814             case ATTRIBUTE:
   815             case NAMESPACE:
   816             default:
   817                 throw new RuntimeException("DOMStreamReader: Unexpected internal state");
   818         }
   819     }
   821     public int nextTag() throws javax.xml.stream.XMLStreamException {
   822         int eventType = next();
   823         while (eventType == CHARACTERS && isWhiteSpace()
   824                || eventType == CDATA && isWhiteSpace()
   825                || eventType == SPACE
   826                || eventType == PROCESSING_INSTRUCTION
   827                || eventType == COMMENT)
   828         {
   829             eventType = next();
   830         }
   831         if (eventType != START_ELEMENT && eventType != END_ELEMENT) {
   832             throw new XMLStreamException2("DOMStreamReader: Expected start or end tag");
   833         }
   834         return eventType;
   835     }
   837     public void require(int type, String namespaceURI, String localName)
   838         throws javax.xml.stream.XMLStreamException
   839     {
   840         if (type != _state) {
   841             throw new XMLStreamException2("DOMStreamReader: Required event type not found");
   842         }
   843         if (namespaceURI != null && !namespaceURI.equals(getNamespaceURI())) {
   844             throw new XMLStreamException2("DOMStreamReader: Required namespaceURI not found");
   845         }
   846         if (localName != null && !localName.equals(getLocalName())) {
   847             throw new XMLStreamException2("DOMStreamReader: Required localName not found");
   848         }
   849     }
   851     public boolean standaloneSet() {
   852         return true;
   853     }
   857     // -- Debugging ------------------------------------------------------
   859     private static void displayDOM(Node node, java.io.OutputStream ostream) {
   860         try {
   861             System.out.println("\n====\n");
   862             XmlUtil.newTransformer().transform(
   863                 new DOMSource(node), new StreamResult(ostream));
   864             System.out.println("\n====\n");
   865         }
   866         catch (Exception e) {
   867             e.printStackTrace();
   868         }
   869     }
   871     private static void verifyDOMIntegrity(Node node) {
   872         switch (node.getNodeType()) {
   873             case ELEMENT_NODE:
   874             case ATTRIBUTE_NODE:
   876                 // DOM level 1?
   877                 if (node.getLocalName() == null) {
   878                     System.out.println("WARNING: DOM level 1 node found");
   879                     System.out.println(" -> node.getNodeName() = " + node.getNodeName());
   880                     System.out.println(" -> node.getNamespaceURI() = " + node.getNamespaceURI());
   881                     System.out.println(" -> node.getLocalName() = " + node.getLocalName());
   882                     System.out.println(" -> node.getPrefix() = " + node.getPrefix());
   883                 }
   885                 if (node.getNodeType() == ATTRIBUTE_NODE) return;
   887                 NamedNodeMap attrs = node.getAttributes();
   888                 for (int i = 0; i < attrs.getLength(); i++) {
   889                     verifyDOMIntegrity(attrs.item(i));
   890                 }
   891             case DOCUMENT_NODE:
   892                 NodeList children = node.getChildNodes();
   893                 for (int i = 0; i < children.getLength(); i++) {
   894                     verifyDOMIntegrity(children.item(i));
   895                 }
   896         }
   897     }
   900     private static String fixNull(String s) {
   901         if(s==null) return "";
   902         else        return s;
   903     }
   904 }

mercurial