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         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 
 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         @Override
 646         public String toString() {
 647             return new String(_characters, _offset, _length);
 648         }
 649     }
 650 
 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 
 661     public final String getEncoding() {
 662         return "UTF-8";
 663     }
 664 
 665     public final boolean hasText() {
 666         return (_characters != null || _charSequence != null);
 667     }
 668 
 669     public final Location getLocation() {
 670         return new DummyLocation();
 671     }
 672 
 673     public final boolean hasName() {
 674         return (_eventType == START_ELEMENT || _eventType == END_ELEMENT);
 675     }
 676 
 677     public final QName getName() {
 678         return _stackTop.getQName();
 679     }
 680 
 681     public final String getLocalName() {
 682         return _stackTop.localName;
 683     }
 684 
 685     public final String getNamespaceURI() {
 686         return _stackTop.uri;
 687     }
 688 
 689     public final String getPrefix() {
 690         return _stackTop.prefix;
 691 
 692     }
 693 
 694     public final String getVersion() {
 695         return "1.0";
 696     }
 697 
 698     public final boolean isStandalone() {
 699         return false;
 700     }
 701 
 702     public final boolean standaloneSet() {
 703         return false;
 704     }
 705 
 706     public final String getCharacterEncodingScheme() {
 707         return "UTF-8";
 708     }
 709 
 710     public final String getPITarget() {
 711         if (_eventType == PROCESSING_INSTRUCTION) {
 712             return _piTarget;
 713         }
 714         throw new IllegalStateException("");
 715     }
 716 
 717     public final String getPIData() {
 718         if (_eventType == PROCESSING_INSTRUCTION) {
 719             return _piData;
 720         }
 721         throw new IllegalStateException("");
 722     }
 723 
 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 
 741     private boolean isInscope(int depth) {
 742         return _buffer.getInscopeNamespaces().size() > 0 && depth ==0;
 743     }
 744 
 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 
 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 
 821     private static String fixNull(String s) {
 822         if (s == null) return "";
 823         else return s;
 824     }
 825 
 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 
 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 
 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 
 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 
 905         public QName getQName() {
 906             if (qname == null) {
 907                 qname = new QName(fixNull(uri), localName, fixNull(prefix));
 908             }
 909             return qname;
 910         }
 911 
 912         private String fixNull(String s) {
 913             return (s == null) ? "" : s;
 914         }
 915     }
 916 
 917     private final class InternalNamespaceContext implements NamespaceContextEx {
 918         @SuppressWarnings({"StringEquality"})
 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 
 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 
 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 
1029             BindingImpl(String prefix, String namespaceURI) {
1030                 _prefix = prefix;
1031                 _namespaceURI = namespaceURI;
1032             }
1033 
1034             public String getPrefix() {
1035                 return _prefix;
1036             }
1037 
1038             public String getNamespaceURI() {
1039                 return _namespaceURI;
1040             }
1041         }
1042 
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 }