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 }