1 /* 2 * Copyright (c) 2005, 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 */ 25 26 package com.sun.xml.internal.stream.buffer.stax; 27 28 import com.sun.xml.internal.stream.buffer.AbstractProcessor; 29 import com.sun.xml.internal.stream.buffer.AttributesHolder; 30 import com.sun.xml.internal.stream.buffer.XMLStreamBuffer; 31 import com.sun.xml.internal.stream.buffer.XMLStreamBufferMark; 32 import com.sun.xml.internal.org.jvnet.staxex.NamespaceContextEx; 33 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamReaderEx; 34 35 import javax.xml.XMLConstants; 36 import javax.xml.namespace.QName; 37 import javax.xml.stream.Location; 38 import javax.xml.stream.XMLStreamException; 39 import javax.xml.stream.XMLStreamReader; 40 import java.util.*; 41 42 /** 43 * A processor of a {@link XMLStreamBuffer} that reads the XML infoset as 44 * {@link XMLStreamReader}. 45 * 46 * <p> 47 * Because of {@link XMLStreamReader} design, this processor always produce 48 * a full document infoset, even if the buffer just contains a fragment. 49 * 50 * <p> 51 * When {@link XMLStreamBuffer} contains a multiple tree (AKA "forest"), 52 * {@link XMLStreamReader} will behave as if there are multiple root elements 53 * (so you'll see {@link #START_ELEMENT} event where you'd normally expect 54 * {@link #END_DOCUMENT}.) 55 * 56 * @author Paul.Sandoz@Sun.Com 57 * @author K.Venugopal@sun.com 58 */ 59 public class StreamReaderBufferProcessor extends AbstractProcessor implements XMLStreamReaderEx { 60 private static final int CACHE_SIZE = 16; 61 62 // Stack to hold element and namespace declaration information 63 protected ElementStackEntry[] _stack = new ElementStackEntry[CACHE_SIZE]; 64 /** The top-most active entry of the {@link #_stack}. */ 65 protected ElementStackEntry _stackTop; 66 /** The element depth that we are in. Used to determine when we are done with a tree. */ 67 protected int _depth; 68 69 // Arrays to hold all namespace declarations 70 /** 71 * Namespace prefixes. Can be empty but not null. 72 */ 73 protected String[] _namespaceAIIsPrefix = new String[CACHE_SIZE]; 74 protected String[] _namespaceAIIsNamespaceName = new String[CACHE_SIZE]; 75 protected int _namespaceAIIsEnd; 76 77 // Internal namespace context implementation 78 protected InternalNamespaceContext _nsCtx = new InternalNamespaceContext(); 79 80 // The current event type 81 protected int _eventType; 82 83 /** 84 * Holder of the attributes. 85 * 86 * Be careful that this follows the SAX convention of using "" instead of null. 87 */ 88 protected AttributesHolder _attributeCache; 89 90 // Characters as a CharSequence 91 protected CharSequence _charSequence; 92 93 // Characters as a char array with offset and length 94 protected char[] _characters; 95 protected int _textOffset; 96 protected int _textLen; 97 98 protected String _piTarget; 99 protected String _piData; 100 101 // 102 // Represents the parser state wrt the end of parsing. 103 // 104 /** 105 * The parser is in the middle of parsing a document, 106 * with no end in sight. 107 */ 108 private static final int PARSING = 1; 109 /** 110 * The parser has already reported the {@link #END_ELEMENT}, 111 * and we are parsing a fragment. We'll report {@link #END_DOCUMENT} 112 * next and be done. 113 */ 114 private static final int PENDING_END_DOCUMENT = 2; 115 /** 116 * The parser has reported the {@link #END_DOCUMENT} event, 117 * so we are really done parsing. 118 */ 119 private static final int COMPLETED = 3; 120 121 /** 122 * True if processing is complete. 123 */ 124 private int _completionState; 125 StreamReaderBufferProcessor()126 public StreamReaderBufferProcessor() { 127 for (int i=0; i < _stack.length; i++){ 128 _stack[i] = new ElementStackEntry(); 129 } 130 131 _attributeCache = new AttributesHolder(); 132 } 133 StreamReaderBufferProcessor(XMLStreamBuffer buffer)134 public StreamReaderBufferProcessor(XMLStreamBuffer buffer) throws XMLStreamException { 135 this(); 136 setXMLStreamBuffer(buffer); 137 } 138 setXMLStreamBuffer(XMLStreamBuffer buffer)139 public void setXMLStreamBuffer(XMLStreamBuffer buffer) throws XMLStreamException { 140 setBuffer(buffer,buffer.isFragment()); 141 142 _completionState = PARSING; 143 _namespaceAIIsEnd = 0; 144 _characters = null; 145 _charSequence = null; 146 _eventType = START_DOCUMENT; 147 } 148 149 /** 150 * Does {@link #nextTag()} and if the parser moved to a new start tag, 151 * returns a {@link XMLStreamBufferMark} that captures the infoset starting 152 * from the newly discovered element. 153 * 154 * <p> 155 * (Ideally we should have a method that works against the current position, 156 * but the way the data structure is read makes this somewhat difficult.) 157 * 158 * This creates a new {@link XMLStreamBufferMark} that shares the underlying 159 * data storage, thus it's fairly efficient. 160 */ nextTagAndMark()161 public XMLStreamBuffer nextTagAndMark() throws XMLStreamException { 162 while (true) { 163 int s = peekStructure(); 164 if((s &TYPE_MASK)==T_ELEMENT) { 165 // next is start element. 166 Map<String,String> inscope = new HashMap<String, String>(_namespaceAIIsEnd); 167 168 for (int i=0 ; i<_namespaceAIIsEnd; i++) 169 inscope.put(_namespaceAIIsPrefix[i],_namespaceAIIsNamespaceName[i]); 170 171 XMLStreamBufferMark mark = new XMLStreamBufferMark(inscope, this); 172 next(); 173 return mark; 174 } else if((s &TYPE_MASK)==T_DOCUMENT) { 175 //move the pointer to next structure. 176 readStructure(); 177 //mark the next start element 178 XMLStreamBufferMark mark = new XMLStreamBufferMark(new HashMap<String, String>(_namespaceAIIsEnd), this); 179 next(); 180 return mark; 181 } 182 183 if(next()==END_ELEMENT) 184 return null; 185 } 186 } 187 getProperty(String name)188 public Object getProperty(String name) { 189 return null; 190 } 191 next()192 public int next() throws XMLStreamException { 193 switch(_completionState) { 194 case COMPLETED: 195 throw new XMLStreamException("Invalid State"); 196 case PENDING_END_DOCUMENT: 197 _namespaceAIIsEnd = 0; 198 _completionState = COMPLETED; 199 return _eventType = END_DOCUMENT; 200 } 201 202 // Pop the stack of elements 203 // This is a post-processing operation 204 // The stack of the element should be poppoed after 205 // the END_ELEMENT event is returned so that the correct element name 206 // and namespace scope is returned 207 switch(_eventType) { 208 case END_ELEMENT: 209 if (_depth > 1) { 210 _depth--; 211 // _depth index is always set to the next free stack entry 212 // to push 213 popElementStack(_depth); 214 } else if (_depth == 1) { 215 _depth--; 216 } 217 } 218 219 _characters = null; 220 _charSequence = null; 221 while(true) {// loop only if we read STATE_DOCUMENT 222 int eiiState = readEiiState(); 223 switch(eiiState) { 224 case STATE_DOCUMENT: 225 // we'll always produce a full document, and we've already report START_DOCUMENT event. 226 // so simply skil this 227 continue; 228 case STATE_ELEMENT_U_LN_QN: { 229 final String uri = readStructureString(); 230 final String localName = readStructureString(); 231 final String prefix = getPrefixFromQName(readStructureString()); 232 233 processElement(prefix, uri, localName, isInscope(_depth)); 234 return _eventType = START_ELEMENT; 235 } 236 case STATE_ELEMENT_P_U_LN: 237 processElement(readStructureString(), readStructureString(), readStructureString(),isInscope(_depth)); 238 return _eventType = START_ELEMENT; 239 case STATE_ELEMENT_U_LN: 240 processElement(null, readStructureString(), readStructureString(),isInscope(_depth)); 241 return _eventType = START_ELEMENT; 242 case STATE_ELEMENT_LN: 243 processElement(null, null, readStructureString(),isInscope(_depth)); 244 return _eventType = START_ELEMENT; 245 case STATE_TEXT_AS_CHAR_ARRAY_SMALL: 246 _textLen = readStructure(); 247 _textOffset = readContentCharactersBuffer(_textLen); 248 _characters = _contentCharactersBuffer; 249 250 return _eventType = CHARACTERS; 251 case STATE_TEXT_AS_CHAR_ARRAY_MEDIUM: 252 _textLen = readStructure16(); 253 _textOffset = readContentCharactersBuffer(_textLen); 254 _characters = _contentCharactersBuffer; 255 256 return _eventType = CHARACTERS; 257 case STATE_TEXT_AS_CHAR_ARRAY_COPY: 258 _characters = readContentCharactersCopy(); 259 _textLen = _characters.length; 260 _textOffset = 0; 261 262 return _eventType = CHARACTERS; 263 case STATE_TEXT_AS_STRING: 264 _eventType = CHARACTERS; 265 _charSequence = readContentString(); 266 267 return _eventType = CHARACTERS; 268 case STATE_TEXT_AS_OBJECT: 269 _eventType = CHARACTERS; 270 _charSequence = (CharSequence)readContentObject(); 271 272 return _eventType = CHARACTERS; 273 case STATE_COMMENT_AS_CHAR_ARRAY_SMALL: 274 _textLen = readStructure(); 275 _textOffset = readContentCharactersBuffer(_textLen); 276 _characters = _contentCharactersBuffer; 277 278 return _eventType = COMMENT; 279 case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM: 280 _textLen = readStructure16(); 281 _textOffset = readContentCharactersBuffer(_textLen); 282 _characters = _contentCharactersBuffer; 283 284 return _eventType = COMMENT; 285 case STATE_COMMENT_AS_CHAR_ARRAY_COPY: 286 _characters = readContentCharactersCopy(); 287 _textLen = _characters.length; 288 _textOffset = 0; 289 290 return _eventType = COMMENT; 291 case STATE_COMMENT_AS_STRING: 292 _charSequence = readContentString(); 293 294 return _eventType = COMMENT; 295 case STATE_PROCESSING_INSTRUCTION: 296 _piTarget = readStructureString(); 297 _piData = readStructureString(); 298 299 return _eventType = PROCESSING_INSTRUCTION; 300 case STATE_END: 301 if (_depth > 1) { 302 // normal case 303 return _eventType = END_ELEMENT; 304 } else if (_depth == 1) { 305 // this is the last end element for the current tree. 306 if (_fragmentMode) { 307 if(--_treeCount==0) // is this the last tree in the forest? 308 _completionState = PENDING_END_DOCUMENT; 309 } 310 return _eventType = END_ELEMENT; 311 } else { 312 // this only happens when we are processing a full document 313 // and we hit the "end of document" marker 314 _namespaceAIIsEnd = 0; 315 _completionState = COMPLETED; 316 return _eventType = END_DOCUMENT; 317 } 318 default: 319 throw new XMLStreamException("Internal XSB error: Invalid State="+eiiState); 320 } 321 // this should be unreachable 322 } 323 } 324 require(int type, String namespaceURI, String localName)325 public final void require(int type, String namespaceURI, String localName) throws XMLStreamException { 326 if( type != _eventType) { 327 throw new XMLStreamException(""); 328 } 329 if( namespaceURI != null && !namespaceURI.equals(getNamespaceURI())) { 330 throw new XMLStreamException(""); 331 } 332 if(localName != null && !localName.equals(getLocalName())) { 333 throw new XMLStreamException(""); 334 } 335 } 336 getElementTextTrim()337 public final String getElementTextTrim() throws XMLStreamException { 338 // TODO getElementText* methods more efficiently 339 return getElementText().trim(); 340 } 341 getElementText()342 public final String getElementText() throws XMLStreamException { 343 if(_eventType != START_ELEMENT) { 344 throw new XMLStreamException(""); 345 } 346 347 next(); 348 return getElementText(true); 349 } 350 getElementText(boolean startElementRead)351 public final String getElementText(boolean startElementRead) throws XMLStreamException { 352 if (!startElementRead) { 353 throw new XMLStreamException(""); 354 } 355 356 int eventType = getEventType(); 357 StringBuilder content = new StringBuilder(); 358 while(eventType != END_ELEMENT ) { 359 if(eventType == CHARACTERS 360 || eventType == CDATA 361 || eventType == SPACE 362 || eventType == ENTITY_REFERENCE) { 363 content.append(getText()); 364 } else if(eventType == PROCESSING_INSTRUCTION 365 || eventType == COMMENT) { 366 // skipping 367 } else if(eventType == END_DOCUMENT) { 368 throw new XMLStreamException(""); 369 } else if(eventType == START_ELEMENT) { 370 throw new XMLStreamException(""); 371 } else { 372 throw new XMLStreamException(""); 373 } 374 eventType = next(); 375 } 376 return content.toString(); 377 } 378 nextTag()379 public final int nextTag() throws XMLStreamException { 380 next(); 381 return nextTag(true); 382 } 383 nextTag(boolean currentTagRead)384 public final int nextTag(boolean currentTagRead) throws XMLStreamException { 385 int eventType = getEventType(); 386 if (!currentTagRead) { 387 eventType = next(); 388 } 389 while((eventType == CHARACTERS && isWhiteSpace()) // skip whitespace 390 || (eventType == CDATA && isWhiteSpace()) 391 || eventType == SPACE 392 || eventType == PROCESSING_INSTRUCTION 393 || eventType == COMMENT) { 394 eventType = next(); 395 } 396 if (eventType != START_ELEMENT && eventType != END_ELEMENT) { 397 throw new XMLStreamException(""); 398 } 399 return eventType; 400 } 401 hasNext()402 public final boolean hasNext() { 403 return (_eventType != END_DOCUMENT); 404 } 405 close()406 public void close() throws XMLStreamException { 407 } 408 isStartElement()409 public final boolean isStartElement() { 410 return (_eventType == START_ELEMENT); 411 } 412 isEndElement()413 public final boolean isEndElement() { 414 return (_eventType == END_ELEMENT); 415 } 416 isCharacters()417 public final boolean isCharacters() { 418 return (_eventType == CHARACTERS); 419 } 420 isWhiteSpace()421 public final boolean isWhiteSpace() { 422 if(isCharacters() || (_eventType == CDATA)){ 423 char [] ch = this.getTextCharacters(); 424 int start = this.getTextStart(); 425 int length = this.getTextLength(); 426 for (int i = start; i < length; i++){ 427 final char c = ch[i]; 428 if (!(c == 0x20 || c == 0x9 || c == 0xD || c == 0xA)) 429 return false; 430 } 431 return true; 432 } 433 return false; 434 } 435 getAttributeValue(String namespaceURI, String localName)436 public final String getAttributeValue(String namespaceURI, String localName) { 437 if (_eventType != START_ELEMENT) { 438 throw new IllegalStateException(""); 439 } 440 441 if (namespaceURI == null) { 442 // Set to the empty string to be compatible with the 443 // org.xml.sax.Attributes interface 444 namespaceURI = ""; 445 } 446 447 return _attributeCache.getValue(namespaceURI, localName); 448 } 449 getAttributeCount()450 public final int getAttributeCount() { 451 if (_eventType != START_ELEMENT) { 452 throw new IllegalStateException(""); 453 } 454 455 return _attributeCache.getLength(); 456 } 457 getAttributeName(int index)458 public final javax.xml.namespace.QName getAttributeName(int index) { 459 if (_eventType != START_ELEMENT) { 460 throw new IllegalStateException(""); 461 } 462 463 final String prefix = _attributeCache.getPrefix(index); 464 final String localName = _attributeCache.getLocalName(index); 465 final String uri = _attributeCache.getURI(index); 466 return new QName(uri,localName,prefix); 467 } 468 469 getAttributeNamespace(int index)470 public final String getAttributeNamespace(int index) { 471 if (_eventType != START_ELEMENT) { 472 throw new IllegalStateException(""); 473 } 474 return fixEmptyString(_attributeCache.getURI(index)); 475 } 476 getAttributeLocalName(int index)477 public final String getAttributeLocalName(int index) { 478 if (_eventType != START_ELEMENT) { 479 throw new IllegalStateException(""); 480 } 481 return _attributeCache.getLocalName(index); 482 } 483 getAttributePrefix(int index)484 public final String getAttributePrefix(int index) { 485 if (_eventType != START_ELEMENT) { 486 throw new IllegalStateException(""); 487 } 488 return fixEmptyString(_attributeCache.getPrefix(index)); 489 } 490 getAttributeType(int index)491 public final String getAttributeType(int index) { 492 if (_eventType != START_ELEMENT) { 493 throw new IllegalStateException(""); 494 } 495 return _attributeCache.getType(index); 496 } 497 getAttributeValue(int index)498 public final String getAttributeValue(int index) { 499 if (_eventType != START_ELEMENT) { 500 throw new IllegalStateException(""); 501 } 502 503 return _attributeCache.getValue(index); 504 } 505 isAttributeSpecified(int index)506 public final boolean isAttributeSpecified(int index) { 507 return false; 508 } 509 getNamespaceCount()510 public final int getNamespaceCount() { 511 if (_eventType == START_ELEMENT || _eventType == END_ELEMENT) { 512 return _stackTop.namespaceAIIsEnd - _stackTop.namespaceAIIsStart; 513 } 514 515 throw new IllegalStateException(""); 516 } 517 getNamespacePrefix(int index)518 public final String getNamespacePrefix(int index) { 519 if (_eventType == START_ELEMENT || _eventType == END_ELEMENT) { 520 return _namespaceAIIsPrefix[_stackTop.namespaceAIIsStart + index]; 521 } 522 523 throw new IllegalStateException(""); 524 } 525 getNamespaceURI(int index)526 public final String getNamespaceURI(int index) { 527 if (_eventType == START_ELEMENT || _eventType == END_ELEMENT) { 528 return _namespaceAIIsNamespaceName[_stackTop.namespaceAIIsStart + index]; 529 } 530 531 throw new IllegalStateException(""); 532 } 533 getNamespaceURI(String prefix)534 public final String getNamespaceURI(String prefix) { 535 return _nsCtx.getNamespaceURI(prefix); 536 } 537 getNamespaceContext()538 public final NamespaceContextEx getNamespaceContext() { 539 return _nsCtx; 540 } 541 getEventType()542 public final int getEventType() { 543 return _eventType; 544 } 545 getText()546 public final String getText() { 547 if (_characters != null) { 548 String s = new String(_characters, _textOffset, _textLen); 549 _charSequence = s; 550 return s; 551 } else if (_charSequence != null) { 552 return _charSequence.toString(); 553 } else { 554 throw new IllegalStateException(); 555 } 556 } 557 getTextCharacters()558 public final char[] getTextCharacters() { 559 if (_characters != null) { 560 return _characters; 561 } else if (_charSequence != null) { 562 // TODO try to avoid creation of a temporary String for some 563 // CharSequence implementations 564 _characters = _charSequence.toString().toCharArray(); 565 _textLen = _characters.length; 566 _textOffset = 0; 567 return _characters; 568 } else { 569 throw new IllegalStateException(); 570 } 571 } 572 getTextStart()573 public final int getTextStart() { 574 if (_characters != null) { 575 return _textOffset; 576 } else if (_charSequence != null) { 577 return 0; 578 } else { 579 throw new IllegalStateException(); 580 } 581 } 582 getTextLength()583 public final int getTextLength() { 584 if (_characters != null) { 585 return _textLen; 586 } else if (_charSequence != null) { 587 return _charSequence.length(); 588 } else { 589 throw new IllegalStateException(); 590 } 591 } 592 getTextCharacters(int sourceStart, char[] target, int targetStart, int length)593 public final int getTextCharacters(int sourceStart, char[] target, 594 int targetStart, int length) throws XMLStreamException { 595 if (_characters != null) { 596 } else if (_charSequence != null) { 597 _characters = _charSequence.toString().toCharArray(); 598 _textLen = _characters.length; 599 _textOffset = 0; 600 } else { 601 throw new IllegalStateException(""); 602 } 603 604 try { 605 int remaining = _textLen - sourceStart; 606 int len = remaining > length ? length : remaining; 607 sourceStart += _textOffset; 608 System.arraycopy(_characters, sourceStart, target, targetStart, len); 609 return len; 610 } catch (IndexOutOfBoundsException e) { 611 throw new XMLStreamException(e); 612 } 613 } 614 615 private class CharSequenceImpl implements CharSequence { 616 private final int _offset; 617 private final int _length; 618 CharSequenceImpl(int offset, int length)619 CharSequenceImpl(int offset, int length) { 620 _offset = offset; 621 _length = length; 622 } 623 length()624 public int length() { 625 return _length; 626 } 627 charAt(int index)628 public char charAt(int index) { 629 if (index >= 0 && index < _textLen) { 630 return _characters[_textOffset + index]; 631 } else { 632 throw new IndexOutOfBoundsException(); 633 } 634 } 635 subSequence(int start, int end)636 public CharSequence subSequence(int start, int end) { 637 final int length = end - start; 638 if (end < 0 || start < 0 || end > length || start > end) { 639 throw new IndexOutOfBoundsException(); 640 } 641 642 return new CharSequenceImpl(_offset + start, length); 643 } 644 645 @Override toString()646 public String toString() { 647 return new String(_characters, _offset, _length); 648 } 649 } 650 getPCDATA()651 public final CharSequence getPCDATA() { 652 if (_characters != null) { 653 return new CharSequenceImpl(_textOffset, _textLen); 654 } else if (_charSequence != null) { 655 return _charSequence; 656 } else { 657 throw new IllegalStateException(); 658 } 659 } 660 getEncoding()661 public final String getEncoding() { 662 return "UTF-8"; 663 } 664 hasText()665 public final boolean hasText() { 666 return (_characters != null || _charSequence != null); 667 } 668 getLocation()669 public final Location getLocation() { 670 return new DummyLocation(); 671 } 672 hasName()673 public final boolean hasName() { 674 return (_eventType == START_ELEMENT || _eventType == END_ELEMENT); 675 } 676 getName()677 public final QName getName() { 678 return _stackTop.getQName(); 679 } 680 getLocalName()681 public final String getLocalName() { 682 return _stackTop.localName; 683 } 684 getNamespaceURI()685 public final String getNamespaceURI() { 686 return _stackTop.uri; 687 } 688 getPrefix()689 public final String getPrefix() { 690 return _stackTop.prefix; 691 692 } 693 getVersion()694 public final String getVersion() { 695 return "1.0"; 696 } 697 isStandalone()698 public final boolean isStandalone() { 699 return false; 700 } 701 standaloneSet()702 public final boolean standaloneSet() { 703 return false; 704 } 705 getCharacterEncodingScheme()706 public final String getCharacterEncodingScheme() { 707 return "UTF-8"; 708 } 709 getPITarget()710 public final String getPITarget() { 711 if (_eventType == PROCESSING_INSTRUCTION) { 712 return _piTarget; 713 } 714 throw new IllegalStateException(""); 715 } 716 getPIData()717 public final String getPIData() { 718 if (_eventType == PROCESSING_INSTRUCTION) { 719 return _piData; 720 } 721 throw new IllegalStateException(""); 722 } 723 processElement(String prefix, String uri, String localName, boolean inscope)724 protected void processElement(String prefix, String uri, String localName, boolean inscope) { 725 pushElementStack(); 726 _stackTop.set(prefix, uri, localName); 727 728 _attributeCache.clear(); 729 730 int item = peekStructure(); 731 if ((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE || inscope) { 732 // Skip the namespace declarations on the element 733 // they will have been added already 734 item = processNamespaceAttributes(item, inscope); 735 } 736 if ((item & TYPE_MASK) == T_ATTRIBUTE) { 737 processAttributes(item); 738 } 739 } 740 isInscope(int depth)741 private boolean isInscope(int depth) { 742 return _buffer.getInscopeNamespaces().size() > 0 && depth ==0; 743 } 744 resizeNamespaceAttributes()745 private void resizeNamespaceAttributes() { 746 final String[] namespaceAIIsPrefix = new String[_namespaceAIIsEnd * 2]; 747 System.arraycopy(_namespaceAIIsPrefix, 0, namespaceAIIsPrefix, 0, _namespaceAIIsEnd); 748 _namespaceAIIsPrefix = namespaceAIIsPrefix; 749 750 final String[] namespaceAIIsNamespaceName = new String[_namespaceAIIsEnd * 2]; 751 System.arraycopy(_namespaceAIIsNamespaceName, 0, namespaceAIIsNamespaceName, 0, _namespaceAIIsEnd); 752 _namespaceAIIsNamespaceName = namespaceAIIsNamespaceName; 753 } 754 processNamespaceAttributes(int item, boolean inscope)755 private int processNamespaceAttributes(int item, boolean inscope){ 756 _stackTop.namespaceAIIsStart = _namespaceAIIsEnd; 757 Set<String> prefixSet = inscope ? new HashSet<String>() : Collections.<String>emptySet(); 758 759 while((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE) { 760 if (_namespaceAIIsEnd == _namespaceAIIsPrefix.length) { 761 resizeNamespaceAttributes(); 762 } 763 764 switch(getNIIState(item)){ 765 case STATE_NAMESPACE_ATTRIBUTE: 766 // Undeclaration of default namespace 767 _namespaceAIIsPrefix[_namespaceAIIsEnd] = 768 _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = ""; 769 if (inscope) { 770 prefixSet.add(""); 771 } 772 break; 773 case STATE_NAMESPACE_ATTRIBUTE_P: 774 // Undeclaration of namespace 775 _namespaceAIIsPrefix[_namespaceAIIsEnd] = readStructureString(); 776 if (inscope) { 777 prefixSet.add(_namespaceAIIsPrefix[_namespaceAIIsEnd]); 778 } 779 _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = ""; 780 break; 781 case STATE_NAMESPACE_ATTRIBUTE_P_U: 782 // Declaration with prefix 783 _namespaceAIIsPrefix[_namespaceAIIsEnd] = readStructureString(); 784 if (inscope) { 785 prefixSet.add(_namespaceAIIsPrefix[_namespaceAIIsEnd]); 786 } 787 _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = readStructureString(); 788 break; 789 case STATE_NAMESPACE_ATTRIBUTE_U: 790 // Default declaration 791 _namespaceAIIsPrefix[_namespaceAIIsEnd] = ""; 792 if (inscope) { 793 prefixSet.add(""); 794 } 795 _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = readStructureString(); 796 break; 797 } 798 readStructure(); 799 800 item = peekStructure(); 801 } 802 803 if (inscope) { 804 for (Map.Entry<String, String> e : _buffer.getInscopeNamespaces().entrySet()) { 805 String key = fixNull(e.getKey()); 806 // If the prefix is already written, do not write the prefix 807 if (!prefixSet.contains(key)) { 808 if (_namespaceAIIsEnd == _namespaceAIIsPrefix.length) { 809 resizeNamespaceAttributes(); 810 } 811 _namespaceAIIsPrefix[_namespaceAIIsEnd] = key; 812 _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = e.getValue(); 813 } 814 } 815 } 816 _stackTop.namespaceAIIsEnd = _namespaceAIIsEnd; 817 818 return item; 819 } 820 fixNull(String s)821 private static String fixNull(String s) { 822 if (s == null) return ""; 823 else return s; 824 } 825 processAttributes(int item)826 private void processAttributes(int item){ 827 do { 828 switch(getAIIState(item)){ 829 case STATE_ATTRIBUTE_U_LN_QN: { 830 final String uri = readStructureString(); 831 final String localName = readStructureString(); 832 final String prefix = getPrefixFromQName(readStructureString()); 833 _attributeCache.addAttributeWithPrefix(prefix, uri, localName, readStructureString(), readContentString()); 834 break; 835 } 836 case STATE_ATTRIBUTE_P_U_LN: 837 _attributeCache.addAttributeWithPrefix(readStructureString(), readStructureString(), readStructureString(), readStructureString(), readContentString()); 838 break; 839 case STATE_ATTRIBUTE_U_LN: 840 // _attributeCache follows SAX convention 841 _attributeCache.addAttributeWithPrefix("", readStructureString(), readStructureString(), readStructureString(), readContentString()); 842 break; 843 case STATE_ATTRIBUTE_LN: { 844 _attributeCache.addAttributeWithPrefix("", "", readStructureString(), readStructureString(), readContentString()); 845 break; 846 } 847 default : 848 assert false : "Internal XSB Error: wrong attribute state, Item="+item; 849 } 850 readStructure(); 851 852 item = peekStructure(); 853 } while((item & TYPE_MASK) == T_ATTRIBUTE); 854 } 855 pushElementStack()856 private void pushElementStack() { 857 if (_depth == _stack.length) { 858 // resize stack 859 ElementStackEntry [] tmp = _stack; 860 _stack = new ElementStackEntry[_stack.length * 3 /2 + 1]; 861 System.arraycopy(tmp, 0, _stack, 0, tmp.length); 862 for (int i = tmp.length; i < _stack.length; i++){ 863 _stack[i] = new ElementStackEntry(); 864 } 865 } 866 867 _stackTop = _stack[_depth++]; 868 } 869 popElementStack(int depth)870 private void popElementStack(int depth) { 871 // _depth is checked outside this method 872 _stackTop = _stack[depth - 1]; 873 // Move back the position of the namespace index 874 _namespaceAIIsEnd = _stack[depth].namespaceAIIsStart; 875 } 876 877 private final class ElementStackEntry { 878 /** 879 * Prefix. 880 * Just like everywhere else in StAX, this can be null but can't be empty. 881 */ 882 String prefix; 883 /** 884 * Namespace URI. 885 * Just like everywhere else in StAX, this can be null but can't be empty. 886 */ 887 String uri; 888 String localName; 889 QName qname; 890 891 // Start and end of namespace declarations 892 // in namespace declaration arrays 893 int namespaceAIIsStart; 894 int namespaceAIIsEnd; 895 set(String prefix, String uri, String localName)896 public void set(String prefix, String uri, String localName) { 897 this.prefix = prefix; 898 this.uri = uri; 899 this.localName = localName; 900 this.qname = null; 901 902 this.namespaceAIIsStart = this.namespaceAIIsEnd = StreamReaderBufferProcessor.this._namespaceAIIsEnd; 903 } 904 getQName()905 public QName getQName() { 906 if (qname == null) { 907 qname = new QName(fixNull(uri), localName, fixNull(prefix)); 908 } 909 return qname; 910 } 911 fixNull(String s)912 private String fixNull(String s) { 913 return (s == null) ? "" : s; 914 } 915 } 916 917 private final class InternalNamespaceContext implements NamespaceContextEx { 918 @SuppressWarnings({"StringEquality"}) getNamespaceURI(String prefix)919 public String getNamespaceURI(String prefix) { 920 if (prefix == null) { 921 throw new IllegalArgumentException("Prefix cannot be null"); 922 } 923 924 /* 925 * If the buffer was created using string interning 926 * intern the prefix and check for reference equality 927 * rather than using String.equals(); 928 */ 929 if (_stringInterningFeature) { 930 prefix = prefix.intern(); 931 932 // Find the most recently declared prefix 933 for (int i = _namespaceAIIsEnd - 1; i >=0; i--) { 934 if (prefix == _namespaceAIIsPrefix[i]) { 935 return _namespaceAIIsNamespaceName[i]; 936 } 937 } 938 } else { 939 // Find the most recently declared prefix 940 for (int i = _namespaceAIIsEnd - 1; i >=0; i--) { 941 if (prefix.equals(_namespaceAIIsPrefix[i])) { 942 return _namespaceAIIsNamespaceName[i]; 943 } 944 } 945 } 946 947 // Check for XML-based prefixes 948 if (prefix.equals(XMLConstants.XML_NS_PREFIX)) { 949 return XMLConstants.XML_NS_URI; 950 } else if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) { 951 return XMLConstants.XMLNS_ATTRIBUTE_NS_URI; 952 } 953 954 return null; 955 } 956 getPrefix(String namespaceURI)957 public String getPrefix(String namespaceURI) { 958 final Iterator i = getPrefixes(namespaceURI); 959 if (i.hasNext()) { 960 return (String)i.next(); 961 } else { 962 return null; 963 } 964 } 965 getPrefixes(final String namespaceURI)966 public Iterator getPrefixes(final String namespaceURI) { 967 if (namespaceURI == null){ 968 throw new IllegalArgumentException("NamespaceURI cannot be null"); 969 } 970 971 if (namespaceURI.equals(XMLConstants.XML_NS_URI)) { 972 return Collections.singletonList(XMLConstants.XML_NS_PREFIX).iterator(); 973 } else if (namespaceURI.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) { 974 return Collections.singletonList(XMLConstants.XMLNS_ATTRIBUTE).iterator(); 975 } 976 977 return new Iterator() { 978 private int i = _namespaceAIIsEnd - 1; 979 private boolean requireFindNext = true; 980 private String p; 981 982 private String findNext() { 983 while(i >= 0) { 984 // Find the most recently declared namespace 985 if (namespaceURI.equals(_namespaceAIIsNamespaceName[i])) { 986 // Find the most recently declared prefix of the namespace 987 // and check if the prefix is in scope with that namespace 988 if (getNamespaceURI(_namespaceAIIsPrefix[i]).equals( 989 _namespaceAIIsNamespaceName[i])) { 990 return p = _namespaceAIIsPrefix[i]; 991 } 992 } 993 i--; 994 } 995 return p = null; 996 } 997 998 public boolean hasNext() { 999 if (requireFindNext) { 1000 findNext(); 1001 requireFindNext = false; 1002 } 1003 return (p != null); 1004 } 1005 1006 public Object next() { 1007 if (requireFindNext) { 1008 findNext(); 1009 } 1010 requireFindNext = true; 1011 1012 if (p == null) { 1013 throw new NoSuchElementException(); 1014 } 1015 1016 return p; 1017 } 1018 1019 public void remove() { 1020 throw new UnsupportedOperationException(); 1021 } 1022 }; 1023 } 1024 1025 private class BindingImpl implements NamespaceContextEx.Binding { 1026 final String _prefix; 1027 final String _namespaceURI; 1028 BindingImpl(String prefix, String namespaceURI)1029 BindingImpl(String prefix, String namespaceURI) { 1030 _prefix = prefix; 1031 _namespaceURI = namespaceURI; 1032 } 1033 getPrefix()1034 public String getPrefix() { 1035 return _prefix; 1036 } 1037 getNamespaceURI()1038 public String getNamespaceURI() { 1039 return _namespaceURI; 1040 } 1041 } 1042 iterator()1043 public Iterator<NamespaceContextEx.Binding> iterator() { 1044 return new Iterator<NamespaceContextEx.Binding>() { 1045 private final int end = _namespaceAIIsEnd - 1; 1046 private int current = end; 1047 private boolean requireFindNext = true; 1048 private NamespaceContextEx.Binding namespace; 1049 1050 private NamespaceContextEx.Binding findNext() { 1051 while(current >= 0) { 1052 final String prefix = _namespaceAIIsPrefix[current]; 1053 1054 // Find if the current prefix occurs more recently 1055 // If so then it is not in scope 1056 int i = end; 1057 for (;i > current; i--) { 1058 if (prefix.equals(_namespaceAIIsPrefix[i])) { 1059 break; 1060 } 1061 } 1062 if (i == current--) { 1063 // The current prefix is in-scope 1064 return namespace = new BindingImpl(prefix, _namespaceAIIsNamespaceName[current]); 1065 } 1066 } 1067 return namespace = null; 1068 } 1069 1070 public boolean hasNext() { 1071 if (requireFindNext) { 1072 findNext(); 1073 requireFindNext = false; 1074 } 1075 return (namespace != null); 1076 } 1077 1078 public NamespaceContextEx.Binding next() { 1079 if (requireFindNext) { 1080 findNext(); 1081 } 1082 requireFindNext = true; 1083 1084 if (namespace == null) { 1085 throw new NoSuchElementException(); 1086 } 1087 1088 return namespace; 1089 } 1090 1091 public void remove() { 1092 throw new UnsupportedOperationException(); 1093 } 1094 }; 1095 } 1096 } 1097 1098 private class DummyLocation implements Location { 1099 public int getLineNumber() { 1100 return -1; 1101 } 1102 1103 public int getColumnNumber() { 1104 return -1; 1105 } 1106 1107 public int getCharacterOffset() { 1108 return -1; 1109 } 1110 1111 public String getPublicId() { 1112 return null; 1113 } 1114 1115 public String getSystemId() { 1116 return _buffer.getSystemId(); 1117 } 1118 } 1119 1120 private static String fixEmptyString(String s) { 1121 // s must not be null, so no need to check for that. that would be bug. 1122 if(s.length()==0) return null; 1123 else return s; 1124 } 1125 1126 } 1127