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

Fri, 14 Feb 2014 11:13:45 +0100

author
mkos
date
Fri, 14 Feb 2014 11:13:45 +0100
changeset 515
6cd506508147
parent 368
0989ad8c0860
child 637
9c07ef4934dd
permissions
-rw-r--r--

8026188: Enhance envelope factory
Summary: Avoiding caching data initialized via TCCL in static context; fix also reviewed by Alexander Fomin
Reviewed-by: ahgross, mgrebac, skoivu

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

mercurial