1/* 2 * Copyright (c) 1997, 2013, 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 */ 25 26package com.sun.xml.internal.ws.streaming; 27 28import javax.xml.namespace.QName; 29import static javax.xml.stream.XMLStreamConstants.*; 30import javax.xml.stream.XMLStreamException; 31import javax.xml.stream.XMLStreamReader; 32import javax.xml.stream.XMLStreamConstants; 33 34/** 35 * <p> XMLStreamReaderUtil provides some utility methods intended to be used 36 * in conjunction with a StAX XMLStreamReader. </p> 37 * 38 * @author WS Development Team 39 */ 40public class XMLStreamReaderUtil { 41 42 private XMLStreamReaderUtil() { 43 } 44 45 public static void close(XMLStreamReader reader) { 46 try { 47 reader.close(); 48 } catch (XMLStreamException e) { 49 throw wrapException(e); 50 } 51 } 52 53 public static void readRest(XMLStreamReader reader) { 54 try { 55 while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT) { 56 reader.next(); 57 } 58 } catch (XMLStreamException e) { 59 throw wrapException(e); 60 } 61 } 62 63 public static int next(XMLStreamReader reader) { 64 try { 65 int readerEvent = reader.next(); 66 67 while (readerEvent != END_DOCUMENT) { 68 switch (readerEvent) { 69 case START_ELEMENT: 70 case END_ELEMENT: 71 case CDATA: 72 case CHARACTERS: 73 case PROCESSING_INSTRUCTION: 74 return readerEvent; 75 default: 76 // falls through ignoring event 77 } 78 readerEvent = reader.next(); 79 } 80 81 return readerEvent; 82 } 83 catch (XMLStreamException e) { 84 throw wrapException(e); 85 } 86 } 87 88 public static int nextElementContent(XMLStreamReader reader) { 89 int state = nextContent(reader); 90 if (state == CHARACTERS) { 91 throw new XMLStreamReaderException( 92 "xmlreader.unexpectedCharacterContent", reader.getText()); 93 } 94 return state; 95 } 96 97 public static void toNextTag(XMLStreamReader reader, QName name) { 98 // skip any whitespace 99 if (reader.getEventType() != XMLStreamConstants.START_ELEMENT && 100 reader.getEventType() != XMLStreamConstants.END_ELEMENT) { 101 XMLStreamReaderUtil.nextElementContent(reader); 102 } 103 if(reader.getEventType() == XMLStreamConstants.END_ELEMENT && name.equals(reader.getName())) { 104 XMLStreamReaderUtil.nextElementContent(reader); 105 } 106 } 107 108 /** 109 * Moves next and read spaces from the reader as long as to the next element. 110 * Comments are ignored 111 * @param reader 112 * @return 113 */ 114 public static String nextWhiteSpaceContent(XMLStreamReader reader) { 115 next(reader); 116 return currentWhiteSpaceContent(reader); 117 } 118 119 /** 120 * Read spaces from the reader as long as to the next element, starting from 121 * current position. Comments are ignored. 122 * @param reader 123 * @return 124 */ 125 public static String currentWhiteSpaceContent(XMLStreamReader reader) { 126 127 // since the there might be several valid chunks (spaces/comment/spaces) 128 // StringBuilder must be used; it's initialized lazily, only when needed 129 StringBuilder whiteSpaces = null; 130 131 for (;;) { 132 switch (reader.getEventType()) { 133 case START_ELEMENT: 134 case END_ELEMENT: 135 case END_DOCUMENT: 136 return whiteSpaces == null ? null : whiteSpaces.toString(); 137 case CHARACTERS: 138 if (reader.isWhiteSpace()) { 139 if (whiteSpaces == null) { 140 whiteSpaces = new StringBuilder(); 141 } 142 whiteSpaces.append(reader.getText()); 143 } else { 144 throw new XMLStreamReaderException( 145 "xmlreader.unexpectedCharacterContent", reader.getText()); 146 } 147 } 148 next(reader); 149 } 150 } 151 152 public static int nextContent(XMLStreamReader reader) { 153 for (;;) { 154 int state = next(reader); 155 switch (state) { 156 case START_ELEMENT: 157 case END_ELEMENT: 158 case END_DOCUMENT: 159 return state; 160 case CHARACTERS: 161 if (!reader.isWhiteSpace()) { 162 return CHARACTERS; 163 } 164 } 165 } 166 } 167 168 /** 169 * Skip current element, leaving the cursor at END_ELEMENT of 170 * current element. 171 */ 172 public static void skipElement(XMLStreamReader reader) { 173 assert reader.getEventType() == START_ELEMENT; 174 skipTags(reader, true); 175 assert reader.getEventType() == END_ELEMENT; 176 } 177 178 /** 179 * Skip following siblings, leaving cursor at END_ELEMENT of 180 * parent element. 181 */ 182 public static void skipSiblings(XMLStreamReader reader, QName parent) { 183 skipTags(reader, reader.getName().equals(parent)); 184 assert reader.getEventType() == END_ELEMENT; 185 } 186 187 private static void skipTags(XMLStreamReader reader, boolean exitCondition) { 188 try { 189 int state, tags = 0; 190 while ((state = reader.next()) != END_DOCUMENT) { 191 if (state == START_ELEMENT) { 192 tags++; 193 } 194 else if (state == END_ELEMENT) { 195 if (tags == 0 && exitCondition) return; 196 tags--; 197 } 198 } 199 } 200 catch (XMLStreamException e) { 201 throw wrapException(e); 202 } 203 } 204 205 /* 206 * Get the text of an element 207 */ 208 public static String getElementText(XMLStreamReader reader) { 209 try { 210 return reader.getElementText(); 211 } catch (XMLStreamException e) { 212 throw wrapException(e); 213 } 214 } 215 216 /* 217 * Get a QName with 'someUri' and 'localname' from an 218 * element of qname type: 219 * <xyz xmlns:ns1="someUri">ns1:localname</xyz> 220 */ 221 public static QName getElementQName(XMLStreamReader reader) { 222 try { 223 String text = reader.getElementText().trim(); 224 String prefix = text.substring(0,text.indexOf(':')); 225 String namespaceURI = reader.getNamespaceContext().getNamespaceURI(prefix); 226 if (namespaceURI == null) { 227 namespaceURI = ""; 228 } 229 String localPart = text.substring( 230 text.indexOf(':') + 1, text.length()); 231 return new QName(namespaceURI, localPart); 232 } catch (XMLStreamException e) { 233 throw wrapException(e); 234 } 235 } 236 237 /** 238 * Read all attributes into an data structure. Note that this method cannot 239 * be called multiple times to get the same list of attributes. 240 */ 241 public static Attributes getAttributes(XMLStreamReader reader) { 242 return (reader.getEventType() == START_ELEMENT || 243 reader.getEventType() == ATTRIBUTE) ? 244 new AttributesImpl(reader) : null; 245 } 246 247 public static void verifyReaderState(XMLStreamReader reader, int expectedState) { 248 int state = reader.getEventType(); 249 if (state != expectedState) { 250 throw new XMLStreamReaderException( 251 "xmlreader.unexpectedState", 252 getStateName(expectedState), getStateName(state)); 253 } 254 } 255 256 public static void verifyTag(XMLStreamReader reader, String namespaceURI, String localName) { 257 if (!localName.equals(reader.getLocalName()) || !namespaceURI.equals(reader.getNamespaceURI())) { 258 throw new XMLStreamReaderException( 259 "xmlreader.unexpectedState.tag", 260 "{" + namespaceURI + "}" + localName, 261 "{" + reader.getNamespaceURI() + "}" + reader.getLocalName()); 262 } 263 } 264 265 public static void verifyTag(XMLStreamReader reader, QName name) { 266 verifyTag(reader, name.getNamespaceURI(), name.getLocalPart()); 267 } 268 269 public static String getStateName(XMLStreamReader reader) { 270 return getStateName(reader.getEventType()); 271 } 272 273 public static String getStateName(int state) { 274 switch (state) { 275 case ATTRIBUTE: 276 return "ATTRIBUTE"; 277 case CDATA: 278 return "CDATA"; 279 case CHARACTERS: 280 return "CHARACTERS"; 281 case COMMENT: 282 return "COMMENT"; 283 case DTD: 284 return "DTD"; 285 case END_DOCUMENT: 286 return "END_DOCUMENT"; 287 case END_ELEMENT: 288 return "END_ELEMENT"; 289 case ENTITY_DECLARATION: 290 return "ENTITY_DECLARATION"; 291 case ENTITY_REFERENCE: 292 return "ENTITY_REFERENCE"; 293 case NAMESPACE: 294 return "NAMESPACE"; 295 case NOTATION_DECLARATION: 296 return "NOTATION_DECLARATION"; 297 case PROCESSING_INSTRUCTION: 298 return "PROCESSING_INSTRUCTION"; 299 case SPACE: 300 return "SPACE"; 301 case START_DOCUMENT: 302 return "START_DOCUMENT"; 303 case START_ELEMENT: 304 return "START_ELEMENT"; 305 default : 306 return "UNKNOWN"; 307 } 308 } 309 310 private static XMLStreamReaderException wrapException(XMLStreamException e) { 311 return new XMLStreamReaderException("xmlreader.ioException",e); 312 } 313 314 // -- Auxiliary classes ---------------------------------------------- 315 316 /** 317 * AttributesImpl class copied from old StAXReader. This class is used to implement 318 * getAttributes() on a StAX Reader. 319 */ 320 public static class AttributesImpl implements Attributes { 321 322 static final String XMLNS_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/"; 323 324 static class AttributeInfo { 325 326 private QName name; 327 private String value; 328 329 public AttributeInfo(QName name, String value) { 330 this.name = name; 331 if (value == null) { 332 // e.g., <return xmlns=""> -- stax returns null 333 this.value = ""; 334 } else { 335 this.value = value; 336 } 337 } 338 339 QName getName() { 340 return name; 341 } 342 343 String getValue() { 344 return value; 345 } 346 347 /* 348 * Return "xmlns:" as part of name if namespace. 349 */ 350 String getLocalName() { 351 if (isNamespaceDeclaration()) { 352 if (name.getLocalPart().equals("")) { 353 return "xmlns"; 354 } 355 return "xmlns:" + name.getLocalPart(); 356 } 357 return name.getLocalPart(); 358 } 359 360 boolean isNamespaceDeclaration() { 361 return (name.getNamespaceURI() == XMLNS_NAMESPACE_URI); 362 } 363 } 364 365 // stores qname and value for each attribute 366 AttributeInfo [] atInfos; 367 368 /* 369 * Will create a list that contains the namespace declarations 370 * as well as the other attributes. 371 */ 372 public AttributesImpl(XMLStreamReader reader) { 373 if (reader == null) { 374 375 // this is the case when we call getAttributes() on the 376 // reader when it is not on a start tag 377 atInfos = new AttributeInfo[0]; 378 } else { 379 380 // this is the normal case 381 int index = 0; 382 int namespaceCount = reader.getNamespaceCount(); 383 int attributeCount = reader.getAttributeCount(); 384 atInfos = new AttributeInfo[namespaceCount + attributeCount]; 385 for (int i=0; i<namespaceCount; i++) { 386 String namespacePrefix = reader.getNamespacePrefix(i); 387 388 // will be null if default prefix. QName can't take null 389 if (namespacePrefix == null) { 390 namespacePrefix = ""; 391 } 392 atInfos[index++] = new AttributeInfo( 393 new QName(XMLNS_NAMESPACE_URI, 394 namespacePrefix, 395 "xmlns"), 396 reader.getNamespaceURI(i)); 397 } 398 for (int i=0; i<attributeCount; i++) { 399 atInfos[index++] = new AttributeInfo( 400 reader.getAttributeName(i), 401 reader.getAttributeValue(i)); 402 } 403 } 404 } 405 406 public int getLength() { 407 return atInfos.length; 408 } 409 410 public String getLocalName(int index) { 411 if (index >= 0 && index < atInfos.length) { 412 return atInfos[index].getLocalName(); 413 } 414 return null; 415 } 416 417 public QName getName(int index) { 418 if (index >= 0 && index < atInfos.length) { 419 return atInfos[index].getName(); 420 } 421 return null; 422 } 423 424 public String getPrefix(int index) { 425 if (index >= 0 && index < atInfos.length) { 426 return atInfos[index].getName().getPrefix(); 427 } 428 return null; 429 } 430 431 public String getURI(int index) { 432 if (index >= 0 && index < atInfos.length) { 433 return atInfos[index].getName().getNamespaceURI(); 434 } 435 return null; 436 } 437 438 public String getValue(int index) { 439 if (index >= 0 && index < atInfos.length) { 440 return atInfos[index].getValue(); 441 } 442 return null; 443 } 444 445 public String getValue(QName name) { 446 int index = getIndex(name); 447 if (index != -1) { 448 return atInfos[index].getValue(); 449 } 450 return null; 451 } 452 453 public String getValue(String localName) { 454 int index = getIndex(localName); 455 if (index != -1) { 456 return atInfos[index].getValue(); 457 } 458 return null; 459 } 460 461 public String getValue(String uri, String localName) { 462 int index = getIndex(uri, localName); 463 if (index != -1) { 464 return atInfos[index].getValue(); 465 } 466 return null; 467 } 468 469 public boolean isNamespaceDeclaration(int index) { 470 if (index >= 0 && index < atInfos.length) { 471 return atInfos[index].isNamespaceDeclaration(); 472 } 473 return false; 474 } 475 476 public int getIndex(QName name) { 477 for (int i=0; i<atInfos.length; i++) { 478 if (atInfos[i].getName().equals(name)) { 479 return i; 480 } 481 } 482 return -1; 483 } 484 485 public int getIndex(String localName) { 486 for (int i=0; i<atInfos.length; i++) { 487 if (atInfos[i].getName().getLocalPart().equals(localName)) { 488 return i; 489 } 490 } 491 return -1; 492 } 493 494 public int getIndex(String uri, String localName) { 495 QName qName; 496 for (int i=0; i<atInfos.length; i++) { 497 qName = atInfos[i].getName(); 498 if (qName.getNamespaceURI().equals(uri) && 499 qName.getLocalPart().equals(localName)) { 500 501 return i; 502 } 503 } 504 return -1; 505 } 506 } 507} 508