1 /*
   2  * Copyright (c) 2005, 2017, 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.org.apache.xerces.internal.impl;
  27 
  28 import com.sun.org.apache.xerces.internal.util.NamespaceContextWrapper;
  29 import com.sun.org.apache.xerces.internal.util.NamespaceSupport;
  30 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  31 import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
  32 import com.sun.org.apache.xerces.internal.util.XMLChar;
  33 import com.sun.org.apache.xerces.internal.util.XMLStringBuffer;
  34 import com.sun.org.apache.xerces.internal.xni.XNIException;
  35 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
  36 import com.sun.xml.internal.stream.Entity;
  37 import com.sun.xml.internal.stream.StaxErrorReporter;
  38 import com.sun.xml.internal.stream.XMLEntityStorage;
  39 import com.sun.xml.internal.stream.dtd.nonvalidating.DTDGrammar;
  40 import com.sun.xml.internal.stream.dtd.nonvalidating.XMLNotationDecl;
  41 import com.sun.xml.internal.stream.events.EntityDeclarationImpl;
  42 import com.sun.xml.internal.stream.events.NotationDeclarationImpl;
  43 import java.io.BufferedInputStream;
  44 import java.io.BufferedReader;
  45 import java.io.IOException;
  46 import java.io.InputStream;
  47 import java.io.Reader;
  48 import java.util.ArrayList;
  49 import java.util.List;
  50 import java.util.Map;
  51 import javax.xml.XMLConstants;
  52 import javax.xml.namespace.NamespaceContext;
  53 import javax.xml.namespace.QName;
  54 import javax.xml.stream.Location;
  55 import javax.xml.stream.XMLInputFactory;
  56 import javax.xml.stream.XMLStreamConstants;
  57 import javax.xml.stream.XMLStreamException;
  58 import javax.xml.stream.events.EntityDeclaration;
  59 import javax.xml.stream.events.NotationDeclaration;
  60 import javax.xml.stream.events.XMLEvent;
  61 
  62 /**
  63  * This class implements javax.xml.stream.XMLStreamReader. It makes use of
  64  * XML*Scanner classes to derive most of its functionality. If desired,
  65  * Application can reuse this instance by calling reset() and setInputSource().
  66  *
  67  * @author Neeraj Bajaj Sun Microsystems,Inc.
  68  * @author K.Venugopal Sun Microsystems,Inc.
  69  * @author Sunitha Reddy Sun Microsystems,Inc.
  70  */
  71 public class XMLStreamReaderImpl implements javax.xml.stream.XMLStreamReader {
  72 
  73     /**
  74      * Property identifier: entity manager.
  75      */
  76     protected static final String ENTITY_MANAGER
  77             = Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_MANAGER_PROPERTY;
  78 
  79     /**
  80      * Property identifier: Error Reporter.
  81      */
  82     protected static final String ERROR_REPORTER
  83             = Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
  84 
  85     /**
  86      * Property identifier: Symbol table.
  87      */
  88     protected static final String SYMBOL_TABLE
  89             = Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
  90 
  91     protected static final String READER_IN_DEFINED_STATE
  92             = Constants.READER_IN_DEFINED_STATE;
  93 
  94     private SymbolTable fSymbolTable = new SymbolTable();
  95 
  96     /**
  97      * Document scanner.
  98      */
  99     protected XMLDocumentScannerImpl fScanner = new XMLNSDocumentScannerImpl();
 100 
 101     //make Global NamespaceContextWrapper object,  fScanner.getNamespaceContext()
 102     //is dynamic object and ita value changes as per the state of the parser.
 103     protected NamespaceContextWrapper fNamespaceContextWrapper =
 104             new NamespaceContextWrapper((NamespaceSupport) fScanner.getNamespaceContext());
 105     protected XMLEntityManager fEntityManager = new XMLEntityManager();
 106     protected StaxErrorReporter fErrorReporter = new StaxErrorReporter();
 107 
 108     /**
 109      * Entity scanner, this alwasy works on last entity that was opened.
 110      */
 111     protected XMLEntityScanner fEntityScanner = null;
 112 
 113     /**
 114      * Input Source
 115      */
 116     protected XMLInputSource fInputSource = null;
 117     /**
 118      * Store properties
 119      */
 120     protected PropertyManager fPropertyManager = null;
 121 
 122     /**
 123      * current event type
 124      */
 125     private int fEventType;
 126     /**
 127      * debug flag
 128      */
 129     static final boolean DEBUG = false;
 130     /**
 131      * more to scan
 132      */
 133     private boolean fReuse = true;
 134     private boolean fReaderInDefinedState = true;
 135     private String fDTDDecl = null;
 136     private String versionStr = null;
 137 
 138     /**
 139      * @param inputStream
 140      * @param props
 141      * @throws XMLStreamException
 142      */
 143     public XMLStreamReaderImpl(InputStream inputStream, PropertyManager props) throws XMLStreamException {
 144         init(props);
 145         //publicId, systemid, baseSystemId, inputStream, enocding
 146         XMLInputSource inputSource = new XMLInputSource(null, null, null, inputStream, null);
 147         //pass the input source to document scanner impl.
 148         setInputSource(inputSource);
 149     }
 150 
 151     public XMLDocumentScannerImpl getScanner() {
 152         System.out.println("returning scanner");
 153         return fScanner;
 154     }
 155 
 156     /**
 157      * @param systemid
 158      * @param props
 159      * @throws XMLStreamException
 160      */
 161     public XMLStreamReaderImpl(String systemid, PropertyManager props) throws XMLStreamException {
 162         init(props);
 163         //publicId, systemid, baseSystemId, inputStream, enocding
 164         XMLInputSource inputSource = new XMLInputSource(null, systemid, null, false);
 165         //pass the input source to document scanner impl.
 166         setInputSource(inputSource);
 167     }
 168 
 169     /**
 170      * @param inputStream
 171      * @param encoding
 172      * @param props
 173      * @throws XMLStreamException
 174      */
 175     public XMLStreamReaderImpl(InputStream inputStream, String encoding, PropertyManager props)
 176             throws XMLStreamException {
 177         init(props);
 178         //publicId, systemid, baseSystemId, inputStream, enocding
 179         XMLInputSource inputSource = new XMLInputSource(null, null, null,
 180                 new BufferedInputStream(inputStream), encoding);
 181         //pass the input source to document scanner impl.
 182         setInputSource(inputSource);
 183     }
 184 
 185     /**
 186      * @param reader
 187      * @param props
 188      * @throws XMLStreamException
 189      */
 190     public XMLStreamReaderImpl(Reader reader, PropertyManager props)
 191             throws XMLStreamException {
 192         init(props);
 193         //publicId, systemid, baseSystemId, inputStream, enocding
 194         //xxx: Using buffered reader
 195         XMLInputSource inputSource = new XMLInputSource(null, null, null,
 196                 new BufferedReader(reader), null);
 197         //pass the input source to document scanner impl.
 198         setInputSource(inputSource);
 199     }
 200 
 201     /**
 202      * @param inputSource
 203      * @param props
 204      * @throws XMLStreamException
 205      */
 206     public XMLStreamReaderImpl(XMLInputSource inputSource, PropertyManager props)
 207             throws XMLStreamException {
 208         init(props);
 209         //pass the input source to document scanner impl.
 210         setInputSource(inputSource);
 211     }
 212 
 213     /**
 214      * @param inputSource
 215      * @throws XMLStreamException
 216      */
 217     public final void setInputSource(XMLInputSource inputSource) throws XMLStreamException {
 218         //once setInputSource() is called this instance is busy parsing the inputsource supplied
 219         //this instances is free for reuse if parser has reached END_DOCUMENT state or application has
 220         //called close()
 221         fReuse = false;
 222 
 223         try {
 224 
 225             fScanner.setInputSource(inputSource);
 226             //XMLStreamReader should be in defined state
 227             if (fReaderInDefinedState) {
 228                 fEventType = fScanner.next();
 229                 if (versionStr == null) {
 230                     versionStr = getVersion();
 231                 }
 232 
 233                 if (fEventType == XMLStreamConstants.START_DOCUMENT && versionStr != null
 234                         && versionStr.equals("1.1")) {
 235                     switchToXML11Scanner();
 236                 }
 237 
 238             }
 239         } catch (java.io.IOException ex) {
 240             throw new XMLStreamException(ex);
 241         } catch (XNIException ex) { //Issue 56 XNIException not caught
 242             throw new XMLStreamException(ex.getMessage(), getLocation(), ex.getException());
 243         }
 244     }//setInputSource
 245 
 246     final void init(PropertyManager propertyManager) throws XMLStreamException {
 247         fPropertyManager = propertyManager;
 248         //set Stax internal properties -- Note that these instances are being created in XMLReaderImpl.
 249         //1.SymbolTable
 250         //2.XMLMessageFormatter
 251         //3.XMLEntityManager
 252         //4. call reset()
 253         //1.
 254         propertyManager.setProperty(SYMBOL_TABLE, fSymbolTable);
 255         //2.
 256         propertyManager.setProperty(ERROR_REPORTER, fErrorReporter);
 257         //3.
 258         propertyManager.setProperty(ENTITY_MANAGER, fEntityManager);
 259         //4.
 260         reset();
 261     }
 262 
 263     /**
 264      * This function tells if this instances is available for reuse. One must
 265      * call reset() and setInputSource() to be able to reuse this instance.
 266      */
 267     public boolean canReuse() {
 268         if (DEBUG) {
 269             System.out.println("fReuse = " + fReuse);
 270             System.out.println("fEventType = " + getEventTypeString(fEventType));
 271         }
 272         //when parsing begins, fReuse is set to false
 273         //fReuse is set to 'true' when application calls close()
 274         return fReuse;
 275     }
 276 
 277     /**
 278      * Resets this instance so that this instance is ready for reuse.
 279      */
 280     public void reset() {
 281         fReuse = true;
 282         fEventType = 0;
 283         //reset entity manager
 284         fEntityManager.reset(fPropertyManager);
 285         //reset the scanner
 286         fScanner.reset(fPropertyManager);
 287         //REVISIT:this is too ugly -- we are getting XMLEntityManager and XMLEntityReader from
 288         //property manager, it should be only XMLEntityManager
 289         fDTDDecl = null;
 290         fEntityScanner = fEntityManager.getEntityScanner();
 291         //default value for this property is true. However, this should be false
 292         //when using XMLEventReader, because XMLEventReader should not have defined state.
 293         fReaderInDefinedState = ((Boolean) fPropertyManager.getProperty(READER_IN_DEFINED_STATE));
 294         versionStr = null;
 295     }
 296 
 297     /**
 298      * Frees any resources associated with this Reader. This method does not
 299      * close the underlying input source.
 300      *
 301      * @throws XMLStreamException if there are errors freeing associated
 302      * resources
 303      */
 304     public void close() throws XMLStreamException {
 305         //xxx: Check what this function is intended to do.
 306         //reset();
 307         fReuse = true;
 308     }
 309 
 310     /**
 311      * Returns the character encoding declared on the xml declaration Returns
 312      * null if none was declared
 313      *
 314      * @return the encoding declared in the document or null
 315      */
 316     public String getCharacterEncodingScheme() {
 317         return fScanner.getCharacterEncodingScheme();
 318 
 319     }
 320 
 321     /**
 322      * @return
 323      */
 324     public int getColumnNumber() {
 325         return fEntityScanner.getColumnNumber();
 326     }//getColumnNumber
 327 
 328     /**
 329      * Return input encoding if known or null if unknown.
 330      *
 331      * @return the encoding of this instance or null
 332      */
 333     public String getEncoding() {
 334         return fEntityScanner.getEncoding();
 335     }//getEncoding
 336 
 337     /**
 338      * Returns the current value of the parse event as a string, this returns
 339      * the string value of a CHARACTERS event, returns the value of a COMMENT,
 340      * the replacement value for an ENTITY_REFERENCE, the string value of a
 341      * CDATA section, the string value for a SPACE event, or the String value of
 342      * the internal subset of the DTD. If an ENTITY_REFERENCE has been resolved,
 343      * any character data will be reported as CHARACTERS events.
 344      *
 345      * @return the current text or null
 346      */
 347     public int getEventType() {
 348         return fEventType;
 349     }//getEventType
 350 
 351     /**
 352      * @return
 353      */
 354     public int getLineNumber() {
 355         return fEntityScanner.getLineNumber();
 356     }//getLineNumber
 357 
 358     public String getLocalName() {
 359         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.END_ELEMENT) {
 360             //xxx check whats the value of fCurrentElement
 361             return fScanner.getElementQName().localpart;
 362         } else if (fEventType == XMLEvent.ENTITY_REFERENCE) {
 363             return fScanner.getEntityName();
 364         }
 365         throw new IllegalStateException("Method getLocalName() cannot be called for "
 366                 + getEventTypeString(fEventType) + " event.");
 367     }//getLocalName()
 368 
 369     /**
 370      * @return
 371      */
 372     public String getNamespaceURI() {
 373         //doesn't take care of Attribute as separte event
 374         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.END_ELEMENT) {
 375             return fScanner.getElementQName().uri;
 376         }
 377         return null;
 378     }//getNamespaceURI
 379 
 380     /**
 381      * Get the data section of a processing instruction
 382      *
 383      * @return the data or null
 384      */
 385     public String getPIData() {
 386         if (fEventType == XMLEvent.PROCESSING_INSTRUCTION) {
 387             return fScanner.getPIData().toString();
 388         } else {
 389             throw new java.lang.IllegalStateException("Current state of the parser is " + getEventTypeString(fEventType)
 390                     + " But Expected state is " + XMLEvent.PROCESSING_INSTRUCTION);
 391         }
 392     }//getPIData
 393 
 394     /**
 395      * Get the target of a processing instruction
 396      *
 397      * @return the target or null
 398      */
 399     public String getPITarget() {
 400         if (fEventType == XMLEvent.PROCESSING_INSTRUCTION) {
 401             return fScanner.getPITarget();
 402         } else {
 403             throw new java.lang.IllegalStateException("Current state of the parser is " + getEventTypeString(fEventType)
 404                     + " But Expected state is " + XMLEvent.PROCESSING_INSTRUCTION);
 405         }
 406 
 407     }//getPITarget
 408 
 409     /**
 410      * @return the prefix of the current event, or null if the event does not
 411      * have a prefix. For START_ELEMENT and END_ELEMENT, return
 412      * XMLConstants.DEFAULT_NS_PREFIX when no prefix is available.
 413      */
 414     public String getPrefix() {
 415         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.END_ELEMENT) {
 416             String prefix = fScanner.getElementQName().prefix;
 417             return prefix == null ? XMLConstants.DEFAULT_NS_PREFIX : prefix;
 418         }
 419         return null;
 420     }//getPrefix()
 421 
 422     /**
 423      * @return
 424      */
 425     public char[] getTextCharacters() {
 426         if (fEventType == XMLEvent.CHARACTERS || fEventType == XMLEvent.COMMENT
 427                 || fEventType == XMLEvent.CDATA || fEventType == XMLEvent.SPACE) {
 428             return fScanner.getCharacterData().ch;
 429         } else {
 430             throw new IllegalStateException("Current state = " + getEventTypeString(fEventType)
 431                     + " is not among the states " + getEventTypeString(XMLEvent.CHARACTERS) + " , "
 432                     + getEventTypeString(XMLEvent.COMMENT) + " , " + getEventTypeString(XMLEvent.CDATA)
 433                     + " , " + getEventTypeString(XMLEvent.SPACE) + " valid for getTextCharacters() ");
 434         }
 435     }
 436 
 437     /**
 438      * @return
 439      */
 440     public int getTextLength() {
 441         if (fEventType == XMLEvent.CHARACTERS || fEventType == XMLEvent.COMMENT
 442                 || fEventType == XMLEvent.CDATA || fEventType == XMLEvent.SPACE) {
 443             return fScanner.getCharacterData().length;
 444         } else {
 445             throw new IllegalStateException("Current state = " + getEventTypeString(fEventType)
 446                     + " is not among the states " + getEventTypeString(XMLEvent.CHARACTERS) + " , "
 447                     + getEventTypeString(XMLEvent.COMMENT) + " , " + getEventTypeString(XMLEvent.CDATA)
 448                     + " , " + getEventTypeString(XMLEvent.SPACE) + " valid for getTextLength() ");
 449         }
 450 
 451     }
 452 
 453     /**
 454      * @return
 455      */
 456     public int getTextStart() {
 457         if (fEventType == XMLEvent.CHARACTERS || fEventType == XMLEvent.COMMENT
 458                 || fEventType == XMLEvent.CDATA || fEventType == XMLEvent.SPACE) {
 459             return fScanner.getCharacterData().offset;
 460         } else {
 461             throw new IllegalStateException("Current state = " + getEventTypeString(fEventType)
 462                     + " is not among the states " + getEventTypeString(XMLEvent.CHARACTERS) + " , "
 463                     + getEventTypeString(XMLEvent.COMMENT) + " , " + getEventTypeString(XMLEvent.CDATA)
 464                     + " , " + getEventTypeString(XMLEvent.SPACE) + " valid for getTextStart() ");
 465         }
 466     }
 467 
 468     /**
 469      * @return
 470      */
 471     public String getValue() {
 472         if (fEventType == XMLEvent.PROCESSING_INSTRUCTION) {
 473             return fScanner.getPIData().toString();
 474         } else if (fEventType == XMLEvent.COMMENT) {
 475             return fScanner.getComment();
 476         } else if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.END_ELEMENT) {
 477             return fScanner.getElementQName().localpart;
 478         } else if (fEventType == XMLEvent.CHARACTERS) {
 479             return fScanner.getCharacterData().toString();
 480         }
 481         return null;
 482     }//getValue()
 483 
 484     /**
 485      * Get the XML language version of the current document being parsed
 486      */
 487     public String getVersion() {
 488         //apply SAP's patch: the default version in the scanner was set to 1.0 because of DOM and SAX
 489         //so this patch is a workaround of the difference between StAX and DOM
 490         // SAPJVM: Return null if the XML version has not been declared (as specified in the JavaDoc).
 491 
 492         String version = fEntityScanner.getXMLVersion();
 493 
 494         return "1.0".equals(version) && !fEntityScanner.xmlVersionSetExplicitly ? null : version;
 495     }
 496 
 497     /**
 498      * @return
 499      */
 500     public boolean hasAttributes() {
 501         return fScanner.getAttributeIterator().getLength() > 0 ? true : false;
 502     }
 503 
 504     /**
 505      * this Funtion returns true if the current event has name
 506      */
 507     public boolean hasName() {
 508         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.END_ELEMENT) {
 509             return true;
 510         } else {
 511             return false;
 512         }
 513     }//hasName()
 514 
 515     /**
 516      * @throws XMLStreamException
 517      * @return
 518      */
 519     public boolean hasNext() throws XMLStreamException {
 520         //the scanner returns -1 when it detects a broken stream
 521         if (fEventType == -1) {
 522             return false;
 523         }
 524         //we can check in scanners if the scanner state is not set to
 525         //terminating, we still have more events.
 526         return fEventType != XMLEvent.END_DOCUMENT;
 527     }
 528 
 529     /**
 530      * @return
 531      */
 532     public boolean hasValue() {
 533         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.END_ELEMENT
 534                 || fEventType == XMLEvent.ENTITY_REFERENCE || fEventType == XMLEvent.PROCESSING_INSTRUCTION
 535                 || fEventType == XMLEvent.COMMENT || fEventType == XMLEvent.CHARACTERS) {
 536             return true;
 537         } else {
 538             return false;
 539         }
 540 
 541     }
 542 
 543     /**
 544      * @return
 545      */
 546     public boolean isEndElement() {
 547         return fEventType == XMLEvent.END_ELEMENT;
 548     }
 549 
 550     /**
 551      * @return
 552      */
 553     public boolean isStandalone() {
 554         return fScanner.isStandAlone();
 555     }
 556 
 557     /**
 558      * @return
 559      */
 560     public boolean isStartElement() {
 561         return fEventType == XMLEvent.START_ELEMENT;
 562     }
 563 
 564     /**
 565      * Returns true if the cursor points to a character data event that consists
 566      * of all whitespace Application calling this method needs to cache the
 567      * value and avoid calling this method again for the same event.
 568      *
 569      * @return
 570      */
 571     public boolean isWhiteSpace() {
 572         if (isCharacters() || (fEventType == XMLStreamConstants.CDATA)) {
 573             char[] ch = this.getTextCharacters();
 574             final int start = this.getTextStart();
 575             final int end = start + this.getTextLength();
 576             for (int i = start; i < end; i++) {
 577                 if (!XMLChar.isSpace(ch[i])) {
 578                     return false;
 579                 }
 580             }
 581             return true;
 582         }
 583         return false;
 584     }
 585 
 586     /**
 587      * @throws XMLStreamException
 588      * @return
 589      */
 590     public int next() throws XMLStreamException {
 591         if (!hasNext()) {
 592             if (fEventType != -1) {
 593                 throw new java.util.NoSuchElementException(
 594                         "END_DOCUMENT reached: no more elements on the stream.");
 595             } else {
 596                 throw new XMLStreamException(
 597                         "Error processing input source. The input stream is not complete.");
 598             }
 599         }
 600         try {
 601             fEventType = fScanner.next();
 602 
 603             if (versionStr == null) {
 604                 versionStr = getVersion();
 605             }
 606 
 607             if (fEventType == XMLStreamConstants.START_DOCUMENT
 608                     && versionStr != null
 609                     && versionStr.equals("1.1")) {
 610                 switchToXML11Scanner();
 611             }
 612 
 613             if (fEventType == XMLStreamConstants.CHARACTERS ||
 614                     fEventType == XMLStreamConstants.ENTITY_REFERENCE ||
 615                     fEventType == XMLStreamConstants.PROCESSING_INSTRUCTION ||
 616                     fEventType == XMLStreamConstants.COMMENT ||
 617                     fEventType == XMLStreamConstants.CDATA) {
 618                     fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
 619             }
 620 
 621             return fEventType;
 622         } catch (IOException ex) {
 623             // if this error occured trying to resolve the external DTD subset
 624             // and IS_VALIDATING == false, then this is not an XML error
 625             if (fScanner.fScannerState == XMLDocumentScannerImpl.SCANNER_STATE_DTD_EXTERNAL) {
 626                 Boolean isValidating = (Boolean) fPropertyManager.getProperty(
 627                         XMLInputFactory.IS_VALIDATING);
 628                 if (isValidating != null
 629                         && !isValidating.booleanValue()) {
 630                     // ignore the error, set scanner to known state
 631                     fEventType = XMLEvent.DTD;
 632                     fScanner.setScannerState(XMLDocumentScannerImpl.SCANNER_STATE_PROLOG);
 633                     fScanner.setDriver(fScanner.fPrologDriver);
 634                     if (fDTDDecl == null
 635                             || fDTDDecl.length() == 0) {
 636                         fDTDDecl = "<!-- "
 637                                 + "Exception scanning External DTD Subset.  "
 638                                 + "True contents of DTD cannot be determined.  "
 639                                 + "Processing will continue as XMLInputFactory.IS_VALIDATING == false."
 640                                 + " -->";
 641                     }
 642                     return XMLEvent.DTD;
 643                 }
 644             }
 645 
 646             // else real error
 647             throw new XMLStreamException(ex.getMessage(), getLocation(), ex);
 648         } catch (XNIException ex) {
 649             throw new XMLStreamException(
 650                     ex.getMessage(),
 651                     getLocation(),
 652                     ex.getException());
 653         }
 654     } //next()
 655 
 656     private void switchToXML11Scanner() throws IOException {
 657 
 658         int oldEntityDepth = fScanner.fEntityDepth;
 659         com.sun.org.apache.xerces.internal.xni.NamespaceContext oldNamespaceContext
 660                 = fScanner.fNamespaceContext;
 661 
 662         fScanner = new XML11NSDocumentScannerImpl();
 663 
 664         //get the new scanner state to old scanner's previous state
 665         fScanner.reset(fPropertyManager);
 666         fScanner.setPropertyManager(fPropertyManager);
 667         fEntityScanner = fEntityManager.getEntityScanner();
 668         fEntityScanner.registerListener(fScanner);
 669         fEntityManager.fCurrentEntity.mayReadChunks = true;
 670         fScanner.setScannerState(XMLEvent.START_DOCUMENT);
 671 
 672         fScanner.fEntityDepth = oldEntityDepth;
 673         fScanner.fNamespaceContext = oldNamespaceContext;
 674         fEventType = fScanner.next();
 675     }
 676 
 677     final static String getEventTypeString(int eventType) {
 678         switch (eventType) {
 679             case XMLEvent.START_ELEMENT:
 680                 return "START_ELEMENT";
 681             case XMLEvent.END_ELEMENT:
 682                 return "END_ELEMENT";
 683             case XMLEvent.PROCESSING_INSTRUCTION:
 684                 return "PROCESSING_INSTRUCTION";
 685             case XMLEvent.CHARACTERS:
 686                 return "CHARACTERS";
 687             case XMLEvent.COMMENT:
 688                 return "COMMENT";
 689             case XMLEvent.START_DOCUMENT:
 690                 return "START_DOCUMENT";
 691             case XMLEvent.END_DOCUMENT:
 692                 return "END_DOCUMENT";
 693             case XMLEvent.ENTITY_REFERENCE:
 694                 return "ENTITY_REFERENCE";
 695             case XMLEvent.ATTRIBUTE:
 696                 return "ATTRIBUTE";
 697             case XMLEvent.DTD:
 698                 return "DTD";
 699             case XMLEvent.CDATA:
 700                 return "CDATA";
 701             case XMLEvent.SPACE:
 702                 return "SPACE";
 703         }
 704         return "UNKNOWN_EVENT_TYPE, " + String.valueOf(eventType);
 705     }
 706 
 707     /**
 708      * Returns the count of attributes on this START_ELEMENT, this method is
 709      * only valid on a START_ELEMENT or ATTRIBUTE. This count excludes namespace
 710      * definitions. Attribute indices are zero-based.
 711      *
 712      * @return returns the number of attributes
 713      * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE
 714      */
 715     public int getAttributeCount() {
 716         //xxx: recognize SAX properties namespace, namespace-prefix to get XML Namespace declarations
 717         //does length includes namespace declarations ?
 718 
 719         //State should be either START_ELEMENT or ATTRIBUTE
 720         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.ATTRIBUTE) {
 721             return fScanner.getAttributeIterator().getLength();
 722         } else {
 723             throw new java.lang.IllegalStateException("Current state is not among the states "
 724                     + getEventTypeString(XMLEvent.START_ELEMENT) + " , "
 725                     + getEventTypeString(XMLEvent.ATTRIBUTE)
 726                     + "valid for getAttributeCount()");
 727         }
 728     }//getAttributeCount
 729 
 730     /**
 731      * Returns the localName of the attribute at the provided index
 732      *
 733      * @param index the position of the attribute
 734      * @return the localName of the attribute
 735      * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE
 736      */
 737     public QName getAttributeName(int index) {
 738         //State should be either START_ELEMENT or ATTRIBUTE
 739         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.ATTRIBUTE) {
 740             return convertXNIQNametoJavaxQName(fScanner.getAttributeIterator().getQualifiedName(index));
 741         } else {
 742             throw new java.lang.IllegalStateException("Current state is not among the states "
 743                     + getEventTypeString(XMLEvent.START_ELEMENT) + " , "
 744                     + getEventTypeString(XMLEvent.ATTRIBUTE)
 745                     + "valid for getAttributeName()");
 746         }
 747     }//getAttributeName
 748 
 749     /**
 750      * @param index
 751      * @return
 752      */
 753     public String getAttributeLocalName(int index) {
 754         //State should be either START_ELEMENT or ATTRIBUTE
 755         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.ATTRIBUTE) {
 756             return fScanner.getAttributeIterator().getLocalName(index);
 757         } else {
 758             throw new java.lang.IllegalStateException();
 759         }
 760     }//getAttributeName
 761 
 762     /**
 763      * Returns the namespace of the attribute at the provided index
 764      *
 765      * @param index the position of the attribute
 766      * @return the namespace URI (can be null)
 767      * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE
 768      */
 769     public String getAttributeNamespace(int index) {
 770         //State should be either START_ELEMENT or ATTRIBUTE
 771         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.ATTRIBUTE) {
 772             return fScanner.getAttributeIterator().getURI(index);
 773         } else {
 774             throw new java.lang.IllegalStateException("Current state is not among the states "
 775                     + getEventTypeString(XMLEvent.START_ELEMENT) + " , "
 776                     + getEventTypeString(XMLEvent.ATTRIBUTE)
 777                     + "valid for getAttributeNamespace()");
 778         }
 779 
 780     }//getAttributeNamespace
 781 
 782     /**
 783      * Returns the prefix of this attribute at the provided index
 784      *
 785      * @param index the position of the attribute
 786      * @return the prefix of the attribute
 787      * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE
 788      */
 789     public String getAttributePrefix(int index) {
 790         //State should be either START_ELEMENT or ATTRIBUTE
 791         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.ATTRIBUTE) {
 792             return fScanner.getAttributeIterator().getPrefix(index);
 793         } else {
 794             throw new java.lang.IllegalStateException("Current state is not among the states "
 795                     + getEventTypeString(XMLEvent.START_ELEMENT) + " , "
 796                     + getEventTypeString(XMLEvent.ATTRIBUTE)
 797                     + "valid for getAttributePrefix()");
 798         }
 799     }//getAttributePrefix
 800 
 801     /**
 802      * Returns the qname of the attribute at the provided index
 803      *
 804      * @param index the position of the attribute
 805      * @return the QName of the attribute
 806      * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE
 807      */
 808     public javax.xml.namespace.QName getAttributeQName(int index) {
 809         //State should be either START_ELEMENT or ATTRIBUTE
 810         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.ATTRIBUTE) {
 811             // create new object at runtime..
 812             String localName = fScanner.getAttributeIterator().getLocalName(index);
 813             String uri = fScanner.getAttributeIterator().getURI(index);
 814             return new javax.xml.namespace.QName(uri, localName);
 815         } else {
 816             throw new java.lang.IllegalStateException("Current state is not among the states "
 817                     + getEventTypeString(XMLEvent.START_ELEMENT) + " , "
 818                     + getEventTypeString(XMLEvent.ATTRIBUTE)
 819                     + "valid for getAttributeQName()");
 820         }
 821     }//getAttributeQName
 822 
 823     /**
 824      * Returns the XML type of the attribute at the provided index
 825      *
 826      * @param index the position of the attribute
 827      * @return the XML type of the attribute
 828      * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE
 829      */
 830     public String getAttributeType(int index) {
 831         //State should be either START_ELEMENT or ATTRIBUTE
 832         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.ATTRIBUTE) {
 833             return fScanner.getAttributeIterator().getType(index);
 834         } else {
 835             throw new java.lang.IllegalStateException("Current state is not among the states "
 836                     + getEventTypeString(XMLEvent.START_ELEMENT) + " , "
 837                     + getEventTypeString(XMLEvent.ATTRIBUTE)
 838                     + "valid for getAttributeType()");
 839         }
 840 
 841     }//getAttributeType
 842 
 843     /**
 844      * Returns the value of the attribute at the index
 845      *
 846      * @param index the position of the attribute
 847      * @return the attribute value
 848      * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE
 849      */
 850     public String getAttributeValue(int index) {
 851         //State should be either START_ELEMENT or ATTRIBUTE
 852         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.ATTRIBUTE) {
 853             return fScanner.getAttributeIterator().getValue(index);
 854         } else {
 855             throw new java.lang.IllegalStateException("Current state is not among the states "
 856                     + getEventTypeString(XMLEvent.START_ELEMENT) + " , "
 857                     + getEventTypeString(XMLEvent.ATTRIBUTE)
 858                     + "valid for getAttributeValue()");
 859         }
 860 
 861     }//getAttributeValue
 862 
 863     /**
 864      * @param namespaceURI
 865      * @param localName
 866      * @return
 867      */
 868     public String getAttributeValue(String namespaceURI, String localName) {
 869         //State should be either START_ELEMENT or ATTRIBUTE
 870         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.ATTRIBUTE) {
 871             XMLAttributesImpl attributes = fScanner.getAttributeIterator();
 872             if (namespaceURI == null) { //sjsxp issue 70
 873                 return attributes.getValue(attributes.getIndexByLocalName(localName));
 874             } else {
 875                 return fScanner.getAttributeIterator().getValue(
 876                         namespaceURI.length() == 0 ? null : namespaceURI, localName);
 877             }
 878 
 879         } else {
 880             throw new java.lang.IllegalStateException("Current state is not among the states "
 881                     + getEventTypeString(XMLEvent.START_ELEMENT) + " , "
 882                     + getEventTypeString(XMLEvent.ATTRIBUTE)
 883                     + "valid for getAttributeValue()");
 884         }
 885 
 886     }
 887 
 888     /**
 889      * Reads the content of a text-only element. Precondition: the current event
 890      * is START_ELEMENT. Postcondition: The current event is the corresponding
 891      * END_ELEMENT.
 892      *
 893      * @throws XMLStreamException if the current event is not a START_ELEMENT or
 894      * if a non text element is encountered
 895      */
 896     public String getElementText() throws XMLStreamException {
 897 
 898         if (getEventType() != XMLStreamConstants.START_ELEMENT) {
 899             throw new XMLStreamException(
 900                     "parser must be on START_ELEMENT to read next text", getLocation());
 901         }
 902         int eventType = next();
 903         StringBuilder content = new StringBuilder();
 904         while (eventType != XMLStreamConstants.END_ELEMENT) {
 905             if (eventType == XMLStreamConstants.CHARACTERS
 906                     || eventType == XMLStreamConstants.CDATA
 907                     || eventType == XMLStreamConstants.SPACE
 908                     || eventType == XMLStreamConstants.ENTITY_REFERENCE) {
 909                 content.append(getText());
 910             } else if (eventType == XMLStreamConstants.PROCESSING_INSTRUCTION
 911                     || eventType == XMLStreamConstants.COMMENT) {
 912                 // skipping
 913             } else if (eventType == XMLStreamConstants.END_DOCUMENT) {
 914                 throw new XMLStreamException(
 915                         "unexpected end of document when reading element text content");
 916             } else if (eventType == XMLStreamConstants.START_ELEMENT) {
 917                 throw new XMLStreamException("elementGetText() function expects text "
 918                         + "only elment but START_ELEMENT was encountered.", getLocation());
 919             } else {
 920                 throw new XMLStreamException(
 921                         "Unexpected event type " + eventType, getLocation());
 922             }
 923             eventType = next();
 924         }
 925         return content.toString();
 926     }
 927 
 928     /**
 929      * Return the current location of the processor. If the Location is unknown
 930      * the processor should return an implementation of Location that returns -1
 931      * for the location and null for the publicId and systemId. The location
 932      * information is only valid until next() is called.
 933      */
 934     public Location getLocation() {
 935         return new Location() {
 936             String _systemId = fEntityScanner.getExpandedSystemId();
 937             String _publicId = fEntityScanner.getPublicId();
 938             int _offset = fEntityScanner.getCharacterOffset();
 939             int _columnNumber = fEntityScanner.getColumnNumber();
 940             int _lineNumber = fEntityScanner.getLineNumber();
 941 
 942             public String getLocationURI() {
 943                 return _systemId;
 944             }
 945 
 946             public int getCharacterOffset() {
 947                 return _offset;
 948             }
 949 
 950             public int getColumnNumber() {
 951                 return _columnNumber;
 952             }
 953 
 954             public int getLineNumber() {
 955                 return _lineNumber;
 956             }
 957 
 958             public String getPublicId() {
 959                 return _publicId;
 960             }
 961 
 962             public String getSystemId() {
 963                 return _systemId;
 964             }
 965 
 966             public String toString() {
 967                 StringBuilder sbuffer = new StringBuilder();
 968                 sbuffer.append("Line number = " + getLineNumber());
 969                 sbuffer.append("\n");
 970                 sbuffer.append("Column number = " + getColumnNumber());
 971                 sbuffer.append("\n");
 972                 sbuffer.append("System Id = " + getSystemId());
 973                 sbuffer.append("\n");
 974                 sbuffer.append("Public Id = " + getPublicId());
 975                 sbuffer.append("\n");
 976                 sbuffer.append("Location Uri= " + getLocationURI());
 977                 sbuffer.append("\n");
 978                 sbuffer.append("CharacterOffset = " + getCharacterOffset());
 979                 sbuffer.append("\n");
 980                 return sbuffer.toString();
 981             }
 982         };
 983 
 984     }
 985 
 986     /**
 987      * Returns a QName for the current START_ELEMENT or END_ELEMENT event
 988      *
 989      * @return the QName for the current START_ELEMENT or END_ELEMENT event
 990      */
 991     public javax.xml.namespace.QName getName() {
 992         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.END_ELEMENT) {
 993             return convertXNIQNametoJavaxQName(fScanner.getElementQName());
 994         } else {
 995             throw new java.lang.IllegalStateException("Illegal to call getName() "
 996                     + "when event type is " + getEventTypeString(fEventType) + "."
 997                     + " Valid states are " + getEventTypeString(XMLEvent.START_ELEMENT) + ", "
 998                     + getEventTypeString(XMLEvent.END_ELEMENT));
 999         }
1000     }
1001 
1002     /**
1003      * Returns a read only namespace context for the current position. The
1004      * context is transient and only valid until a call to next() changes the
1005      * state of the reader.
1006      *
1007      * @return return a namespace context
1008      */
1009     public NamespaceContext getNamespaceContext() {
1010         return fNamespaceContextWrapper;
1011     }
1012 
1013     /**
1014      * Returns the count of namespaces declared on this START_ELEMENT or
1015      * END_ELEMENT, this method is only valid on a START_ELEMENT, END_ELEMENT or
1016      * NAMESPACE. On an END_ELEMENT the count is of the namespaces that are
1017      * about to go out of scope. This is the equivalent of the information
1018      * reported by SAX callback for an end element event.
1019      *
1020      * @return returns the number of namespace declarations on this specific
1021      * element
1022      * @throws IllegalStateException if this is not a START_ELEMENT, END_ELEMENT
1023      * or NAMESPACE
1024      */
1025     public int getNamespaceCount() {
1026         //namespaceContext is dynamic object.
1027         //REVISIT: check if it specifies all conditions mentioned in the javadoc
1028         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.END_ELEMENT
1029                 || fEventType == XMLEvent.NAMESPACE) {
1030             return fScanner.getNamespaceContext().getDeclaredPrefixCount();
1031         } else {
1032             throw new IllegalStateException("Current event state is " + getEventTypeString(fEventType)
1033                     + " is not among the states " + getEventTypeString(XMLEvent.START_ELEMENT)
1034                     + ", " + getEventTypeString(XMLEvent.END_ELEMENT) + ", "
1035                     + getEventTypeString(XMLEvent.NAMESPACE)
1036                     + " valid for getNamespaceCount().");
1037         }
1038     }
1039 
1040     /**
1041      * Returns the prefix for the namespace declared at the index. Returns null
1042      * if this is the default namespace declaration
1043      *
1044      * @param index the position of the namespace declaration
1045      * @return returns the namespace prefix
1046      * @throws IllegalStateException if this is not a START_ELEMENT, END_ELEMENT
1047      * or NAMESPACE
1048      */
1049     public String getNamespacePrefix(int index) {
1050         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.END_ELEMENT
1051                 || fEventType == XMLEvent.NAMESPACE) {
1052             //namespaceContext is dynamic object.
1053             String prefix = fScanner.getNamespaceContext().getDeclaredPrefixAt(index);
1054             return prefix.isEmpty() ? null : prefix;
1055         } else {
1056             throw new IllegalStateException("Current state " + getEventTypeString(fEventType)
1057                     + " is not among the states " + getEventTypeString(XMLEvent.START_ELEMENT)
1058                     + ", " + getEventTypeString(XMLEvent.END_ELEMENT) + ", "
1059                     + getEventTypeString(XMLEvent.NAMESPACE)
1060                     + " valid for getNamespacePrefix().");
1061         }
1062     }
1063 
1064     /**
1065      * Returns the uri for the namespace declared at the index.
1066      *
1067      * @param index the position of the namespace declaration
1068      * @return returns the namespace uri
1069      * @throws IllegalStateException if this is not a START_ELEMENT, END_ELEMENT
1070      * or NAMESPACE
1071      */
1072     public String getNamespaceURI(int index) {
1073         if (fEventType == XMLEvent.START_ELEMENT || fEventType == XMLEvent.END_ELEMENT
1074                 || fEventType == XMLEvent.NAMESPACE) {
1075             //namespaceContext is dynamic object.
1076             return fScanner.getNamespaceContext().getURI(fScanner.getNamespaceContext()
1077                     .getDeclaredPrefixAt(index));
1078         } else {
1079             throw new IllegalStateException("Current state " + getEventTypeString(fEventType)
1080                     + " is not among the states " + getEventTypeString(XMLEvent.START_ELEMENT)
1081                     + ", " + getEventTypeString(XMLEvent.END_ELEMENT) + ", "
1082                     + getEventTypeString(XMLEvent.NAMESPACE)
1083                     + " valid for getNamespaceURI().");
1084         }
1085 
1086     }
1087 
1088     /**
1089      * Get the value of a feature/property from the underlying implementation
1090      *
1091      * @param name The name of the property, may not be null
1092      * @return The value of the property
1093      * @throws IllegalArgumentException if name is null
1094      */
1095     public Object getProperty(java.lang.String name) throws java.lang.IllegalArgumentException {
1096         if (name == null) {
1097             throw new java.lang.IllegalArgumentException();
1098         }
1099         if (fPropertyManager != null) {
1100             if (name.equals(PropertyManager.STAX_NOTATIONS)) {
1101                 return getNotationDecls();
1102             } else if (name.equals(PropertyManager.STAX_ENTITIES)) {
1103                 return getEntityDecls();
1104             } else {
1105                 return fPropertyManager.getProperty(name);
1106             }
1107         }
1108         return null;
1109     }
1110 
1111     /**
1112      * Returns the current value of the parse event as a string, this returns
1113      * the string value of a CHARACTERS event, returns the value of a COMMENT,
1114      * the replacement value for an ENTITY_REFERENCE, or the String value of the
1115      * DTD
1116      *
1117      * @return the current text or null
1118      * @throws java.lang.IllegalStateException if this state is not a valid text
1119      * state.
1120      */
1121     public String getText() {
1122         if (fEventType == XMLEvent.CHARACTERS || fEventType == XMLEvent.COMMENT
1123                 || fEventType == XMLEvent.CDATA || fEventType == XMLEvent.SPACE) {
1124             //this requires creation of new string
1125             //fEventType == XMLEvent.ENTITY_REFERENCE
1126             return fScanner.getCharacterData().toString();
1127         } else if (fEventType == XMLEvent.ENTITY_REFERENCE) {
1128             String name = fScanner.getEntityName();
1129             if (name != null) {
1130                 if (fScanner.foundBuiltInRefs) {
1131                     return fScanner.getCharacterData().toString();
1132                 }
1133 
1134                 XMLEntityStorage entityStore = fEntityManager.getEntityStore();
1135                 Entity en = entityStore.getEntity(name);
1136                 if (en == null) {
1137                     return null;
1138                 }
1139                 if (en.isExternal()) {
1140                     return ((Entity.ExternalEntity) en).entityLocation.getExpandedSystemId();
1141                 } else {
1142                     return ((Entity.InternalEntity) en).text;
1143                 }
1144             } else {
1145                 return null;
1146             }
1147         } else if (fEventType == XMLEvent.DTD) {
1148             if (fDTDDecl != null) {
1149                 return fDTDDecl;
1150             }
1151             XMLStringBuffer tmpBuffer = fScanner.getDTDDecl();
1152             fDTDDecl = tmpBuffer.toString();
1153             return fDTDDecl;
1154         } else {
1155             throw new IllegalStateException("Current state " + getEventTypeString(fEventType)
1156                     + " is not among the states" + getEventTypeString(XMLEvent.CHARACTERS) + ", "
1157                     + getEventTypeString(XMLEvent.COMMENT) + ", "
1158                     + getEventTypeString(XMLEvent.CDATA) + ", "
1159                     + getEventTypeString(XMLEvent.SPACE) + ", "
1160                     + getEventTypeString(XMLEvent.ENTITY_REFERENCE) + ", "
1161                     + getEventTypeString(XMLEvent.DTD) + " valid for getText() ");
1162         }
1163     }//getText
1164 
1165     /**
1166      * Test if the current event is of the given type and if the namespace and
1167      * name match the current namespace and name of the current event. If the
1168      * namespaceURI is null it is not checked for equality, if the localName is
1169      * null it is not checked for equality.
1170      *
1171      * @param type the event type
1172      * @param namespaceURI the uri of the event, may be null
1173      * @param localName the localName of the event, may be null
1174      * @throws XMLStreamException if the required values are not matched.
1175      */
1176     public void require(int type, String namespaceURI, String localName) throws XMLStreamException {
1177         if (type != fEventType) {
1178             throw new XMLStreamException("Event type " + getEventTypeString(type) + " specified did "
1179                     + "not match with current parser event " + getEventTypeString(fEventType));
1180         }
1181         if (namespaceURI != null && !namespaceURI.equals(getNamespaceURI())) {
1182             throw new XMLStreamException("Namespace URI " + namespaceURI + " specified did not match "
1183                     + "with current namespace URI");
1184         }
1185         if (localName != null && !localName.equals(getLocalName())) {
1186             throw new XMLStreamException("LocalName " + localName + " specified did not match with "
1187                     + "current local name");
1188         }
1189         return;
1190     }
1191 
1192     /**
1193      * Gets the the text associated with a CHARACTERS, SPACE or CDATA event.
1194      * Text starting a "sourceStart" is copied into "destination" starting at
1195      * "targetStart". Up to "length" characters are copied. The number of
1196      * characters actually copied is returned.
1197      *
1198      * The "sourceStart" argument must be greater or equal to 0 and less than or
1199      * equal to the number of characters associated with the event. Usually, one
1200      * requests text starting at a "sourceStart" of 0. If the number of
1201      * characters actually copied is less than the "length", then there is no
1202      * more text. Otherwise, subsequent calls need to be made until all text has
1203      * been retrieved. For example:
1204      *
1205      * <code>
1206      * int length = 1024;
1207      * char[] myBuffer = new char[ length ];
1208      *
1209      * for ( int sourceStart = 0 ; ; sourceStart += length )
1210      * {
1211      *    int nCopied = stream.getTextCharacters( sourceStart, myBuffer, 0, length );
1212      *
1213      *   if (nCopied < length)
1214      *       break;
1215      * }
1216      * </code> XMLStreamException may be thrown if there are any XML errors in
1217      * the underlying source. The "targetStart" argument must be greater than or
1218      * equal to 0 and less than the length of "target", Length must be greater
1219      * than 0 and "targetStart + length" must be less than or equal to length of
1220      * "target".
1221      *
1222      * @param sourceStart the index of the first character in the source array
1223      * to copy
1224      * @param target the destination array
1225      * @param targetStart the start offset in the target array
1226      * @param length the number of characters to copy
1227      * @return the number of characters actually copied
1228      * @throws XMLStreamException if the underlying XML source is not
1229      * well-formed
1230      * @throws IndexOutOfBoundsException if targetStart < 0 or > than the length
1231      * of target
1232      * @throws IndexOutOfBoundwhile(isCharacters()) ;sException if length < 0 or targetStart + length
1233      * > length of target
1234      * @throws UnsupportedOperationException if this method is not supported
1235      * @throws NullPointerException is if target is null
1236      */
1237     public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length)
1238             throws XMLStreamException {
1239 
1240         if (target == null) {
1241             throw new NullPointerException("target char array can't be null");
1242         }
1243 
1244         if (targetStart < 0 || length < 0 || sourceStart < 0 || targetStart >= target.length
1245                 || (targetStart + length) > target.length) {
1246             throw new IndexOutOfBoundsException();
1247         }
1248 
1249         //getTextStart() + sourceStart should not be greater than the lenght of number of characters
1250         //present
1251         int copiedLength = 0;
1252         //int presentDataLen = getTextLength() - (getTextStart()+sourceStart);
1253         int available = getTextLength() - sourceStart;
1254         if (available < 0) {
1255             throw new IndexOutOfBoundsException("sourceStart is greater than"
1256                     + "number of characters associated with this event");
1257         }
1258         if (available < length) {
1259             copiedLength = available;
1260         } else {
1261             copiedLength = length;
1262         }
1263 
1264         System.arraycopy(getTextCharacters(), getTextStart() + sourceStart, target, targetStart, copiedLength);
1265         return copiedLength;
1266     }
1267 
1268     /**
1269      * Return true if the current event has text, false otherwise The following
1270      * events have text: CHARACTERS,DTD ,ENTITY_REFERENCE, COMMENT
1271      */
1272     public boolean hasText() {
1273         if (DEBUG) {
1274             pr("XMLReaderImpl#EVENT TYPE = " + fEventType);
1275         }
1276         if (fEventType == XMLEvent.CHARACTERS || fEventType == XMLEvent.COMMENT
1277                 || fEventType == XMLEvent.CDATA) {
1278             return fScanner.getCharacterData().length > 0;
1279         } else if (fEventType == XMLEvent.ENTITY_REFERENCE) {
1280             String name = fScanner.getEntityName();
1281             if (name != null) {
1282                 if (fScanner.foundBuiltInRefs) {
1283                     return true;
1284                 }
1285 
1286                 XMLEntityStorage entityStore = fEntityManager.getEntityStore();
1287                 Entity en = entityStore.getEntity(name);
1288                 if (en == null) {
1289                     return false;
1290                 }
1291                 if (en.isExternal()) {
1292                     return ((Entity.ExternalEntity) en).entityLocation.getExpandedSystemId() != null;
1293                 } else {
1294                     return ((Entity.InternalEntity) en).text != null;
1295                 }
1296             } else {
1297                 return false;
1298             }
1299         } else if (fEventType == XMLEvent.DTD) {
1300             return fScanner.fSeenDoctypeDecl;
1301         }
1302         return false;
1303     }
1304 
1305     /**
1306      * Returns a boolean which indicates if this attribute was created by
1307      * default
1308      *
1309      * @param index the position of the attribute
1310      * @return true if this is a default attribute
1311      * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE
1312      */
1313     public boolean isAttributeSpecified(int index) {
1314         //check that current state should be either START_ELEMENT or ATTRIBUTE
1315         if ((fEventType == XMLEvent.START_ELEMENT) || (fEventType == XMLEvent.ATTRIBUTE)) {
1316             return fScanner.getAttributeIterator().isSpecified(index);
1317         } else {
1318             throw new IllegalStateException("Current state is not among the states "
1319                     + getEventTypeString(XMLEvent.START_ELEMENT) + " , "
1320                     + getEventTypeString(XMLEvent.ATTRIBUTE)
1321                     + "valid for isAttributeSpecified()");
1322         }
1323     }
1324 
1325     /**
1326      * Returns true if the cursor points to a character data event
1327      *
1328      * @return true if the cursor points to character data, false otherwise
1329      */
1330     public boolean isCharacters() {
1331         return fEventType == XMLEvent.CHARACTERS;
1332     }
1333 
1334     /**
1335      * Skips any insignificant events (COMMENT and PROCESSING_INSTRUCTION) until
1336      * a START_ELEMENT or END_ELEMENT is reached. If other than space characters
1337      * are encountered, an exception is thrown. This method should be used when
1338      * processing element-only content because the parser is not able to
1339      * recognize ignorable whitespace if then DTD is missing or not interpreted.
1340      *
1341      * @return the event type of the element read
1342      * @throws XMLStreamException if the current event is not white space
1343      */
1344     public int nextTag() throws XMLStreamException {
1345 
1346         int eventType = next();
1347         while ((eventType == XMLStreamConstants.CHARACTERS && isWhiteSpace()) // skip whitespace
1348                 || (eventType == XMLStreamConstants.CDATA && isWhiteSpace())
1349                 // skip whitespace
1350                 || eventType == XMLStreamConstants.SPACE
1351                 || eventType == XMLStreamConstants.PROCESSING_INSTRUCTION
1352                 || eventType == XMLStreamConstants.COMMENT) {
1353             eventType = next();
1354         }
1355 
1356         if (eventType != XMLStreamConstants.START_ELEMENT && eventType != XMLStreamConstants.END_ELEMENT) {
1357             throw new XMLStreamException(
1358                     "found: " + getEventTypeString(eventType)
1359                     + ", expected " + getEventTypeString(XMLStreamConstants.START_ELEMENT)
1360                     + " or " + getEventTypeString(XMLStreamConstants.END_ELEMENT),
1361                     getLocation());
1362         }
1363 
1364         return eventType;
1365     }
1366 
1367     /**
1368      * Checks if standalone was set in the document
1369      *
1370      * @return true if standalone was set in the document, or false otherwise
1371      */
1372     public boolean standaloneSet() {
1373         //xxx: it requires if the standalone was set in the document ? This is different that if the document
1374         // is standalone
1375         return fScanner.standaloneSet();
1376     }
1377 
1378     /**
1379      * @param qname
1380      * @return
1381      */
1382     public javax.xml.namespace.QName convertXNIQNametoJavaxQName(
1383             com.sun.org.apache.xerces.internal.xni.QName qname) {
1384         if (qname == null) {
1385             return null;
1386         }
1387         //xxx: prefix definition ?
1388         if (qname.prefix == null) {
1389             return new javax.xml.namespace.QName(qname.uri, qname.localpart);
1390         } else {
1391             return new javax.xml.namespace.QName(qname.uri, qname.localpart, qname.prefix);
1392         }
1393     }
1394 
1395     /**
1396      * Return the uri for the given prefix. The uri returned depends on the
1397      * current state of the processor.
1398      *
1399      * <p>
1400      * <strong>NOTE:</strong>The 'xml' prefix is bound as defined in
1401      * <a href="http://www.w3.org/TR/REC-xml-names/#ns-using">Namespaces in
1402      * XML</a>
1403      * specification to "http://www.w3.org/XML/1998/namespace".
1404      *
1405      * <p>
1406      * <strong>NOTE:</strong> The 'xmlns' prefix must be resolved to following
1407      * namespace
1408      * <a href="http://www.w3.org/2000/xmlns/">http://www.w3.org/2000/xmlns/</a>
1409      *
1410      * @return the uri bound to the given prefix or null if it is not bound
1411      * @param prefix The prefix to lookup, may not be null
1412      * @throws IllegalStateException - if the prefix is null
1413      */
1414     public String getNamespaceURI(String prefix) {
1415         if (prefix == null) {
1416             throw new java.lang.IllegalArgumentException("prefix cannot be null.");
1417         }
1418 
1419         //first add the string to symbol table.. since internally identity comparisons are done.
1420         return fScanner.getNamespaceContext().getURI(fSymbolTable.addSymbol(prefix));
1421     }
1422 
1423     //xxx: this function is not being used.
1424     protected void setPropertyManager(PropertyManager propertyManager) {
1425         fPropertyManager = propertyManager;
1426         //REVISIT: we were supplying hashmap ealier
1427         fScanner.setProperty("stax-properties", propertyManager);
1428         fScanner.setPropertyManager(propertyManager);
1429     }
1430 
1431     /**
1432      * @return returns the reference to property manager.
1433      */
1434     protected PropertyManager getPropertyManager() {
1435         return fPropertyManager;
1436     }
1437 
1438     static void pr(String str) {
1439         System.out.println(str);
1440     }
1441 
1442     protected List<EntityDeclaration> getEntityDecls() {
1443         if (fEventType == XMLStreamConstants.DTD) {
1444             XMLEntityStorage entityStore = fEntityManager.getEntityStore();
1445             ArrayList<EntityDeclaration> list = null;
1446             Map<String, Entity> entities = entityStore.getEntities();
1447             if (entities.size() > 0) {
1448                 EntityDeclarationImpl decl = null;
1449                 list = new ArrayList<>(entities.size());
1450                 for (Map.Entry<String, Entity> entry : entities.entrySet()) {
1451                     String key = entry.getKey();
1452                     Entity en = entry.getValue();
1453                     decl = new EntityDeclarationImpl();
1454                     decl.setEntityName(key);
1455                     if (en.isExternal()) {
1456                         decl.setXMLResourceIdentifier(((Entity.ExternalEntity) en).entityLocation);
1457                         decl.setNotationName(((Entity.ExternalEntity) en).notation);
1458                     } else {
1459                         decl.setEntityReplacementText(((Entity.InternalEntity) en).text);
1460                     }
1461                     list.add(decl);
1462                 }
1463             }
1464             return list;
1465         }
1466         return null;
1467     }
1468 
1469     protected List<NotationDeclaration> getNotationDecls() {
1470         if (fEventType == XMLStreamConstants.DTD) {
1471             if (fScanner.fDTDScanner == null) {
1472                 return null;
1473             }
1474             DTDGrammar grammar = ((XMLDTDScannerImpl) (fScanner.fDTDScanner)).getGrammar();
1475             if (grammar == null) {
1476                 return null;
1477             }
1478             List<XMLNotationDecl> notations = grammar.getNotationDecls();
1479             ArrayList<NotationDeclaration> list = new ArrayList<>();
1480             for (XMLNotationDecl notation : notations) {
1481                 if (notation != null) {
1482                     list.add(new NotationDeclarationImpl(notation));
1483                 }
1484             }
1485             return list;
1486         }
1487         return null;
1488     }
1489 
1490 }//XMLReaderImpl