Thu, 31 Aug 2017 15:18:52 +0800
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 }