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

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

mercurial