1 /* 2 * Copyright (c) 2005, 2010, 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 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 134 public StreamReaderBufferProcessor(XMLStreamBuffer buffer) throws XMLStreamException { 135 this(); 136 setXMLStreamBuffer(buffer); 137 } 138 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 */ 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 188 public Object getProperty(String name) { 189 return null; 190 } 191 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 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 337 public final String getElementTextTrim() throws XMLStreamException { 338 // TODO getElementText* methods more efficiently 339 return getElementText().trim(); 340 } 341 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 351 public final String getElementText(boolean startElementRead) throws XMLStreamException { 352 if (!startElementRead) { 353 throw new XMLStreamException(""); 354 } 355 356 int eventType = getEventType(); 357 StringBuffer content = new StringBuffer(); 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 379 public final int nextTag() throws XMLStreamException { 380 next(); 381 return nextTag(true); 382 } 383 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 402 public final boolean hasNext() { 403 return (_eventType != END_DOCUMENT); 404 } 405 406 public void close() throws XMLStreamException { 407 } 408 409 public final boolean isStartElement() { 410 return (_eventType == START_ELEMENT); 411 } 412 413 public final boolean isEndElement() { 414 return (_eventType == END_ELEMENT); 415 } 416 417 public final boolean isCharacters() { 418 return (_eventType == CHARACTERS); 419 } 420 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 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 450 public final int getAttributeCount() { 451 if (_eventType != START_ELEMENT) { 452 throw new IllegalStateException(""); 453 } 454 455 return _attributeCache.getLength(); 456 } 457 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 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 477 public final String getAttributeLocalName(int index) { 478 if (_eventType != START_ELEMENT) { 479 throw new IllegalStateException(""); 480 } 481 return _attributeCache.getLocalName(index); 482 } 483 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 491 public final String getAttributeType(int index) { 492 if (_eventType != START_ELEMENT) { 493 throw new IllegalStateException(""); 494 } 495 return _attributeCache.getType(index); 496 } 497 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 506 public final boolean isAttributeSpecified(int index) { 507 return false; 508 } 509 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 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 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 534 public final String getNamespaceURI(String prefix) { 535 return _nsCtx.getNamespaceURI(prefix); 536 } 537 538 public final NamespaceContextEx getNamespaceContext() { 539 return _nsCtx; 540 } 541 542 public final int getEventType() { 543 return _eventType; 544 } 545 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 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 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 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 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 619 CharSequenceImpl(int offset, int length) { 620 _offset = offset; 621 _length = length; 622 } 623 624 public int length() { 625 return _length; 626 } 627 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 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 public String toString() { 646 return new String(_characters, _offset, _length); 647 } 648 } 649 650 public final CharSequence getPCDATA() { 651 if (_characters != null) { 652 return new CharSequenceImpl(_textOffset, _textLen); 653 } else if (_charSequence != null) { 654 return _charSequence; 655 } else { 656 throw new IllegalStateException(); 657 } 658 } 659 660 public final String getEncoding() { 661 return "UTF-8"; 662 } 663 664 public final boolean hasText() { 665 return (_characters != null || _charSequence != null); 666 } 667 668 public final Location getLocation() { 669 return new DummyLocation(); 670 } 671 672 public final boolean hasName() { 673 return (_eventType == START_ELEMENT || _eventType == END_ELEMENT); 674 } 675 676 public final QName getName() { 677 return _stackTop.getQName(); 678 } 679 680 public final String getLocalName() { 681 return _stackTop.localName; 682 } 683 684 public final String getNamespaceURI() { 685 return _stackTop.uri; 686 } 687 688 public final String getPrefix() { 689 return _stackTop.prefix; 690 691 } 692 693 public final String getVersion() { 694 return "1.0"; 695 } 696 697 public final boolean isStandalone() { 698 return false; 699 } 700 701 public final boolean standaloneSet() { 702 return false; 703 } 704 705 public final String getCharacterEncodingScheme() { 706 return "UTF-8"; 707 } 708 709 public final String getPITarget() { 710 if (_eventType == PROCESSING_INSTRUCTION) { 711 return _piTarget; 712 } 713 throw new IllegalStateException(""); 714 } 715 716 public final String getPIData() { 717 if (_eventType == PROCESSING_INSTRUCTION) { 718 return _piData; 719 } 720 throw new IllegalStateException(""); 721 } 722 723 protected void processElement(String prefix, String uri, String localName, boolean inscope) { 724 pushElementStack(); 725 _stackTop.set(prefix, uri, localName); 726 727 _attributeCache.clear(); 728 729 int item = peekStructure(); 730 if ((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE || inscope) { 731 // Skip the namespace declarations on the element 732 // they will have been added already 733 item = processNamespaceAttributes(item, inscope); 734 } 735 if ((item & TYPE_MASK) == T_ATTRIBUTE) { 736 processAttributes(item); 737 } 738 } 739 740 private boolean isInscope(int depth) { 741 return _buffer.getInscopeNamespaces().size() > 0 && depth ==0; 742 } 743 744 private void resizeNamespaceAttributes() { 745 final String[] namespaceAIIsPrefix = new String[_namespaceAIIsEnd * 2]; 746 System.arraycopy(_namespaceAIIsPrefix, 0, namespaceAIIsPrefix, 0, _namespaceAIIsEnd); 747 _namespaceAIIsPrefix = namespaceAIIsPrefix; 748 749 final String[] namespaceAIIsNamespaceName = new String[_namespaceAIIsEnd * 2]; 750 System.arraycopy(_namespaceAIIsNamespaceName, 0, namespaceAIIsNamespaceName, 0, _namespaceAIIsEnd); 751 _namespaceAIIsNamespaceName = namespaceAIIsNamespaceName; 752 } 753 754 private int processNamespaceAttributes(int item, boolean inscope){ 755 _stackTop.namespaceAIIsStart = _namespaceAIIsEnd; 756 Set<String> prefixSet = inscope ? new HashSet<String>() : Collections.<String>emptySet(); 757 758 while((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE) { 759 if (_namespaceAIIsEnd == _namespaceAIIsPrefix.length) { 760 resizeNamespaceAttributes(); 761 } 762 763 switch(getNIIState(item)){ 764 case STATE_NAMESPACE_ATTRIBUTE: 765 // Undeclaration of default namespace 766 _namespaceAIIsPrefix[_namespaceAIIsEnd] = 767 _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = ""; 768 if (inscope) { 769 prefixSet.add(""); 770 } 771 break; 772 case STATE_NAMESPACE_ATTRIBUTE_P: 773 // Undeclaration of namespace 774 _namespaceAIIsPrefix[_namespaceAIIsEnd] = readStructureString(); 775 if (inscope) { 776 prefixSet.add(_namespaceAIIsPrefix[_namespaceAIIsEnd]); 777 } 778 _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = ""; 779 break; 780 case STATE_NAMESPACE_ATTRIBUTE_P_U: 781 // Declaration with prefix 782 _namespaceAIIsPrefix[_namespaceAIIsEnd] = readStructureString(); 783 if (inscope) { 784 prefixSet.add(_namespaceAIIsPrefix[_namespaceAIIsEnd]); 785 } 786 _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = readStructureString(); 787 break; 788 case STATE_NAMESPACE_ATTRIBUTE_U: 789 // Default declaration 790 _namespaceAIIsPrefix[_namespaceAIIsEnd] = ""; 791 if (inscope) { 792 prefixSet.add(""); 793 } 794 _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = readStructureString(); 795 break; 796 } 797 readStructure(); 798 799 item = peekStructure(); 800 } 801 802 if (inscope) { 803 for (Map.Entry<String, String> e : _buffer.getInscopeNamespaces().entrySet()) { 804 String key = fixNull(e.getKey()); 805 // If the prefix is already written, do not write the prefix 806 if (!prefixSet.contains(key)) { 807 if (_namespaceAIIsEnd == _namespaceAIIsPrefix.length) { 808 resizeNamespaceAttributes(); 809 } 810 _namespaceAIIsPrefix[_namespaceAIIsEnd] = key; 811 _namespaceAIIsNamespaceName[_namespaceAIIsEnd++] = e.getValue(); 812 } 813 } 814 } 815 _stackTop.namespaceAIIsEnd = _namespaceAIIsEnd; 816 817 return item; 818 } 819 820 private static String fixNull(String s) { 821 if (s == null) return ""; 822 else return s; 823 } 824 825 private void processAttributes(int item){ 826 do { 827 switch(getAIIState(item)){ 828 case STATE_ATTRIBUTE_U_LN_QN: { 829 final String uri = readStructureString(); 830 final String localName = readStructureString(); 831 final String prefix = getPrefixFromQName(readStructureString()); 832 _attributeCache.addAttributeWithPrefix(prefix, uri, localName, readStructureString(), readContentString()); 833 break; 834 } 835 case STATE_ATTRIBUTE_P_U_LN: 836 _attributeCache.addAttributeWithPrefix(readStructureString(), readStructureString(), readStructureString(), readStructureString(), readContentString()); 837 break; 838 case STATE_ATTRIBUTE_U_LN: 839 // _attributeCache follows SAX convention 840 _attributeCache.addAttributeWithPrefix("", readStructureString(), readStructureString(), readStructureString(), readContentString()); 841 break; 842 case STATE_ATTRIBUTE_LN: { 843 _attributeCache.addAttributeWithPrefix("", "", readStructureString(), readStructureString(), readContentString()); 844 break; 845 } 846 default : 847 assert false : "Internal XSB Error: wrong attribute state, Item="+item; 848 } 849 readStructure(); 850 851 item = peekStructure(); 852 } while((item & TYPE_MASK) == T_ATTRIBUTE); 853 } 854 855 private void pushElementStack() { 856 if (_depth == _stack.length) { 857 // resize stack 858 ElementStackEntry [] tmp = _stack; 859 _stack = new ElementStackEntry[_stack.length * 3 /2 + 1]; 860 System.arraycopy(tmp, 0, _stack, 0, tmp.length); 861 for (int i = tmp.length; i < _stack.length; i++){ 862 _stack[i] = new ElementStackEntry(); 863 } 864 } 865 866 _stackTop = _stack[_depth++]; 867 } 868 869 private void popElementStack(int depth) { 870 // _depth is checked outside this method 871 _stackTop = _stack[depth - 1]; 872 // Move back the position of the namespace index 873 _namespaceAIIsEnd = _stack[depth].namespaceAIIsStart; 874 } 875 876 private final class ElementStackEntry { 877 /** 878 * Prefix. 879 * Just like everywhere else in StAX, this can be null but can't be empty. 880 */ 881 String prefix; 882 /** 883 * Namespace URI. 884 * Just like everywhere else in StAX, this can be null but can't be empty. 885 */ 886 String uri; 887 String localName; 888 QName qname; 889 890 // Start and end of namespace declarations 891 // in namespace declaration arrays 892 int namespaceAIIsStart; 893 int namespaceAIIsEnd; 894 895 public void set(String prefix, String uri, String localName) { 896 this.prefix = prefix; 897 this.uri = uri; 898 this.localName = localName; 899 this.qname = null; 900 901 this.namespaceAIIsStart = this.namespaceAIIsEnd = StreamReaderBufferProcessor.this._namespaceAIIsEnd; 902 } 903 904 public QName getQName() { 905 if (qname == null) { 906 qname = new QName(fixNull(uri), localName, fixNull(prefix)); 907 } 908 return qname; 909 } 910 911 private String fixNull(String s) { 912 return (s == null) ? "" : s; 913 } 914 } 915 916 private final class InternalNamespaceContext implements NamespaceContextEx { 917 @SuppressWarnings({"StringEquality"}) 918 public String getNamespaceURI(String prefix) { 919 if (prefix == null) { 920 throw new IllegalArgumentException("Prefix cannot be null"); 921 } 922 923 /* 924 * If the buffer was created using string interning 925 * intern the prefix and check for reference equality 926 * rather than using String.equals(); 927 */ 928 if (_stringInterningFeature) { 929 prefix = prefix.intern(); 930 931 // Find the most recently declared prefix 932 for (int i = _namespaceAIIsEnd - 1; i >=0; i--) { 933 if (prefix == _namespaceAIIsPrefix[i]) { 934 return _namespaceAIIsNamespaceName[i]; 935 } 936 } 937 } else { 938 // Find the most recently declared prefix 939 for (int i = _namespaceAIIsEnd - 1; i >=0; i--) { 940 if (prefix.equals(_namespaceAIIsPrefix[i])) { 941 return _namespaceAIIsNamespaceName[i]; 942 } 943 } 944 } 945 946 // Check for XML-based prefixes 947 if (prefix.equals(XMLConstants.XML_NS_PREFIX)) { 948 return XMLConstants.XML_NS_URI; 949 } else if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) { 950 return XMLConstants.XMLNS_ATTRIBUTE_NS_URI; 951 } 952 953 return null; 954 } 955 956 public String getPrefix(String namespaceURI) { 957 final Iterator i = getPrefixes(namespaceURI); 958 if (i.hasNext()) { 959 return (String)i.next(); 960 } else { 961 return null; 962 } 963 } 964 965 public Iterator getPrefixes(final String namespaceURI) { 966 if (namespaceURI == null){ 967 throw new IllegalArgumentException("NamespaceURI cannot be null"); 968 } 969 970 if (namespaceURI.equals(XMLConstants.XML_NS_URI)) { 971 return Collections.singletonList(XMLConstants.XML_NS_PREFIX).iterator(); 972 } else if (namespaceURI.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) { 973 return Collections.singletonList(XMLConstants.XMLNS_ATTRIBUTE).iterator(); 974 } 975 976 return new Iterator() { 977 private int i = _namespaceAIIsEnd - 1; 978 private boolean requireFindNext = true; 979 private String p; 980 981 private String findNext() { 982 while(i >= 0) { 983 // Find the most recently declared namespace 984 if (namespaceURI.equals(_namespaceAIIsNamespaceName[i])) { 985 // Find the most recently declared prefix of the namespace 986 // and check if the prefix is in scope with that namespace 987 if (getNamespaceURI(_namespaceAIIsPrefix[i]).equals( 988 _namespaceAIIsNamespaceName[i])) { 989 return p = _namespaceAIIsPrefix[i]; 990 } 991 } 992 i--; 993 } 994 return p = null; 995 } 996 997 public boolean hasNext() { 998 if (requireFindNext) { 999 findNext(); 1000 requireFindNext = false; 1001 } 1002 return (p != null); 1003 } 1004 1005 public Object next() { 1006 if (requireFindNext) { 1007 findNext(); 1008 } 1009 requireFindNext = true; 1010 1011 if (p == null) { 1012 throw new NoSuchElementException(); 1013 } 1014 1015 return p; 1016 } 1017 1018 public void remove() { 1019 throw new UnsupportedOperationException(); 1020 } 1021 }; 1022 } 1023 1024 private class BindingImpl implements NamespaceContextEx.Binding { 1025 final String _prefix; 1026 final String _namespaceURI; 1027 1028 BindingImpl(String prefix, String namespaceURI) { 1029 _prefix = prefix; 1030 _namespaceURI = namespaceURI; 1031 } 1032 1033 public String getPrefix() { 1034 return _prefix; 1035 } 1036 1037 public String getNamespaceURI() { 1038 return _namespaceURI; 1039 } 1040 } 1041 1042 public Iterator<NamespaceContextEx.Binding> iterator() { 1043 return new Iterator<NamespaceContextEx.Binding>() { 1044 private final int end = _namespaceAIIsEnd - 1; 1045 private int current = end; 1046 private boolean requireFindNext = true; 1047 private NamespaceContextEx.Binding namespace; 1048 1049 private NamespaceContextEx.Binding findNext() { 1050 while(current >= 0) { 1051 final String prefix = _namespaceAIIsPrefix[current]; 1052 1053 // Find if the current prefix occurs more recently 1054 // If so then it is not in scope 1055 int i = end; 1056 for (;i > current; i--) { 1057 if (prefix.equals(_namespaceAIIsPrefix[i])) { 1058 break; 1059 } 1060 } 1061 if (i == current--) { 1062 // The current prefix is in-scope 1063 return namespace = new BindingImpl(prefix, _namespaceAIIsNamespaceName[current]); 1064 } 1065 } 1066 return namespace = null; 1067 } 1068 1069 public boolean hasNext() { 1070 if (requireFindNext) { 1071 findNext(); 1072 requireFindNext = false; 1073 } 1074 return (namespace != null); 1075 } 1076 1077 public NamespaceContextEx.Binding next() { 1078 if (requireFindNext) { 1079 findNext(); 1080 } 1081 requireFindNext = true; 1082 1083 if (namespace == null) { 1084 throw new NoSuchElementException(); 1085 } 1086 1087 return namespace; 1088 } 1089 1090 public void remove() { 1091 throw new UnsupportedOperationException(); 1092 } 1093 }; 1094 } 1095 } 1096 1097 private class DummyLocation implements Location { 1098 public int getLineNumber() { 1099 return -1; 1100 } 1101 1102 public int getColumnNumber() { 1103 return -1; 1104 } 1105 1106 public int getCharacterOffset() { 1107 return -1; 1108 } 1109 1110 public String getPublicId() { 1111 return null; 1112 } 1113 1114 public String getSystemId() { 1115 return _buffer.getSystemId(); 1116 } 1117 } 1118 1119 private static String fixEmptyString(String s) { 1120 // s must not be null, so no need to check for that. that would be bug. 1121 if(s.length()==0) return null; 1122 else return s; 1123 } 1124 1125 }