content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   * 
   *      http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package mf.org.apache.xerces.impl;
  
  import java.io.CharConversionException;
  import java.io.EOFException;
  import java.io.IOException;
  
  import mf.org.apache.xerces.impl.dtd.XMLDTDDescription;
  import mf.org.apache.xerces.impl.io.MalformedByteSequenceException;
  import mf.org.apache.xerces.impl.msg.XMLMessageFormatter;
  import mf.org.apache.xerces.impl.validation.ValidationManager;
  import mf.org.apache.xerces.util.NamespaceSupport;
  import mf.org.apache.xerces.util.XMLChar;
  import mf.org.apache.xerces.util.XMLStringBuffer;
  import mf.org.apache.xerces.xni.Augmentations;
  import mf.org.apache.xerces.xni.NamespaceContext;
  import mf.org.apache.xerces.xni.XMLResourceIdentifier;
  import mf.org.apache.xerces.xni.XMLString;
  import mf.org.apache.xerces.xni.XNIException;
  import mf.org.apache.xerces.xni.parser.XMLComponentManager;
  import mf.org.apache.xerces.xni.parser.XMLConfigurationException;
  import mf.org.apache.xerces.xni.parser.XMLDTDScanner;
  import mf.org.apache.xerces.xni.parser.XMLInputSource;
  
  
  /**
   * This class is responsible for scanning XML document structure
   * and content. The scanner acts as the source for the document
   * information which is communicated to the document handler.
   * <p/>
   * This component requires the following features and properties from the
   * component manager that uses it:
   * <ul>
   * <li>http://xml.org/sax/features/namespaces</li>
   * <li>http://xml.org/sax/features/validation</li>
   * <li>http://apache.org/xml/features/nonvalidating/load-external-dtd</li>
   * <li>http://apache.org/xml/features/scanner/notify-char-refs</li>
   * <li>http://apache.org/xml/features/scanner/notify-builtin-refs</li>
   * <li>http://apache.org/xml/properties/internal/symbol-table</li>
   * <li>http://apache.org/xml/properties/internal/error-reporter</li>
   * <li>http://apache.org/xml/properties/internal/entity-manager</li>
   * <li>http://apache.org/xml/properties/internal/dtd-scanner</li>
   * </ul>
   *
   * @author Glenn Marcy, IBM
   * @author Andy Clark, IBM
   * @author Arnaud  Le Hors, IBM
   * @author Eric Ye, IBM
   * @version $Id: XMLDocumentScannerImpl.java 572055 2007-09-02 17:55:43Z mrglavas $
   * @xerces.internal
   */
  public class XMLDocumentScannerImpl
          extends XMLDocumentFragmentScannerImpl {
  
      //
      // Constants
      //
  
      // scanner states
  
      /**
       * Scanner state: XML declaration.
       */
      protected static final int SCANNER_STATE_XML_DECL = 0;
  
      /**
       * Scanner state: prolog.
       */
      protected static final int SCANNER_STATE_PROLOG = 5;
  
      /**
       * Scanner state: trailing misc.
       */
      protected static final int SCANNER_STATE_TRAILING_MISC = 12;
  
      /**
       * Scanner state: DTD internal declarations.
       */
      protected static final int SCANNER_STATE_DTD_INTERNAL_DECLS = 17;
  
      /**
       * Scanner state: open DTD external subset.
       */
      protected static final int SCANNER_STATE_DTD_EXTERNAL = 18;
  
      /**
       * Scanner state: DTD external declarations.
       */
      protected static final int SCANNER_STATE_DTD_EXTERNAL_DECLS = 19;
  
      // feature identifiers
  
      /**
       * Feature identifier: load external DTD.
       */
      protected static final String LOAD_EXTERNAL_DTD =
              Constants.XERCES_FEATURE_PREFIX + Constants.LOAD_EXTERNAL_DTD_FEATURE;
  
      /**
       * Feature identifier: load external DTD.
       */
      protected static final String DISALLOW_DOCTYPE_DECL_FEATURE =
              Constants.XERCES_FEATURE_PREFIX + Constants.DISALLOW_DOCTYPE_DECL_FEATURE;
  
      // property identifiers
  
      /**
       * Property identifier: DTD scanner.
       */
      protected static final String DTD_SCANNER =
              Constants.XERCES_PROPERTY_PREFIX + Constants.DTD_SCANNER_PROPERTY;
  
      /**
       * property identifier:  ValidationManager
       */
      protected static final String VALIDATION_MANAGER =
              Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY;
  
      /**
       * property identifier:  NamespaceContext
       */
      protected static final String NAMESPACE_CONTEXT =
              Constants.XERCES_PROPERTY_PREFIX + Constants.NAMESPACE_CONTEXT_PROPERTY;
  
  
      // recognized features and properties
  
      /**
       * Recognized features.
       */
      private static final String[] RECOGNIZED_FEATURES = {
              LOAD_EXTERNAL_DTD,
              DISALLOW_DOCTYPE_DECL_FEATURE,
      };
  
      /**
       * Feature defaults.
       */
      private static final Boolean[] FEATURE_DEFAULTS = {
              Boolean.TRUE,
              Boolean.FALSE,
      };
  
      /**
       * Recognized properties.
       */
      private static final String[] RECOGNIZED_PROPERTIES = {
              DTD_SCANNER,
              VALIDATION_MANAGER,
              NAMESPACE_CONTEXT,
      };
  
      /**
       * Property defaults.
       */
      private static final Object[] PROPERTY_DEFAULTS = {
              null,
              null,
              null,
      };
  
      //
      // Data
      //
  
      // properties
      /**
       * XML declaration dispatcher.
       */
      protected final Dispatcher fXMLDeclDispatcher = new XMLDeclDispatcher();
      /**
       * Prolog dispatcher.
       */
      protected final Dispatcher fPrologDispatcher = new PrologDispatcher();
  
      // protected data
      /**
       * DTD dispatcher.
       */
      protected final Dispatcher fDTDDispatcher = new DTDDispatcher();
  
      // other info
      /**
       * Trailing miscellaneous section dispatcher.
       */
      protected final Dispatcher fTrailingMiscDispatcher = new TrailingMiscDispatcher();
      /**
       * Array of 3 strings.
       */
      private final String[] fStrings = new String[3];
      /**
       * String.
       */
      private final XMLString fString = new XMLString();
      /**
       * String buffer.
       */
      private final XMLStringBuffer fStringBuffer = new XMLStringBuffer();
  
      // features
      /**
       * A DTD Description.
       */
      private final XMLDTDDescription fDTDDescription = new XMLDTDDescription(null, null, null, null, null);
      /**
       * DTD scanner.
       */
      protected XMLDTDScanner fDTDScanner;
  
      // state
      /**
       * Validation manager .
       */
      protected ValidationManager fValidationManager;
  
      // dispatchers
      /**
       * Scanning DTD.
       */
      protected boolean fScanningDTD;
      /**
       * Doctype name.
       */
      protected String fDoctypeName;
      /**
       * Doctype declaration public identifier.
       */
      protected String fDoctypePublicId;
      /**
       * Doctype declaration system identifier.
       */
      protected String fDoctypeSystemId;
  
      // temporary variables
      /**
       * Namespace support.
       */
      protected NamespaceContext fNamespaceContext = new NamespaceSupport();
      /**
       * Load external DTD.
       */
      protected boolean fLoadExternalDTD = true;
      /**
       * Disallow doctype declaration.
       */
      protected boolean fDisallowDoctype = false;
      /**
       * Seen doctype declaration.
       */
      protected boolean fSeenDoctypeDecl;
      /**
       * External subset source.
       */
      private XMLInputSource fExternalSubsetSource = null;
  
      //
      // Constructors
      //
  
      /**
       * Default constructor.
       */
      public XMLDocumentScannerImpl() {
      } // <init>()
  
      //
      // XMLDocumentScanner methods
      //
  
      /**
       * Sets the input source.
       *
       * @param inputSource The input source.
       * @throws IOException Thrown on i/o error.
       */
      @Override
      public void setInputSource(XMLInputSource inputSource) throws IOException {
          fEntityManager.setEntityHandler(this);
          fEntityManager.startDocumentEntity(inputSource);
          //fDocumentSystemId = fEntityManager.expandSystemId(inputSource.getSystemId());
      } // setInputSource(XMLInputSource)
  
      //
      // XMLComponent methods
      //
  
      /**
       * Resets the component. The component can query the component manager
       * about any features and properties that affect the operation of the
       * component.
       *
       * @param componentManager The component manager.
       * @throws SAXException Thrown by component on initialization error.
       *                      For example, if a feature or property is
       *                      required for the operation of the component, the
       *                      component manager may throw a
       *                      SAXNotRecognizedException or a
       *                      SAXNotSupportedException.
       */
      @Override
      public void reset(XMLComponentManager componentManager)
              throws XMLConfigurationException {
  
          super.reset(componentManager);
  
          // other settings
          fDoctypeName = null;
          fDoctypePublicId = null;
          fDoctypeSystemId = null;
          fSeenDoctypeDecl = false;
          fScanningDTD = false;
          fExternalSubsetSource = null;
  
          if (!fParserSettings) {
              // parser settings have not been changed
              fNamespaceContext.reset();
              // setup dispatcher
              setScannerState(SCANNER_STATE_XML_DECL);
              setDispatcher(fXMLDeclDispatcher);
              return;
          }
  
          // xerces features
          try {
              fLoadExternalDTD = componentManager.getFeature(LOAD_EXTERNAL_DTD);
          } catch (XMLConfigurationException e) {
              fLoadExternalDTD = true;
          }
          try {
              fDisallowDoctype = componentManager.getFeature(DISALLOW_DOCTYPE_DECL_FEATURE);
          } catch (XMLConfigurationException e) {
              fDisallowDoctype = false;
          }
  
          // xerces properties
          fDTDScanner = (XMLDTDScanner) componentManager.getProperty(DTD_SCANNER);
          try {
              fValidationManager = (ValidationManager) componentManager.getProperty(VALIDATION_MANAGER);
          } catch (XMLConfigurationException e) {
              fValidationManager = null;
          }
  
          try {
              fNamespaceContext = (NamespaceContext) componentManager.getProperty(NAMESPACE_CONTEXT);
          } catch (XMLConfigurationException e) {
          }
          if (fNamespaceContext == null) {
              fNamespaceContext = new NamespaceSupport();
          }
          fNamespaceContext.reset();
  
          // setup dispatcher
          setScannerState(SCANNER_STATE_XML_DECL);
          setDispatcher(fXMLDeclDispatcher);
  
      } // reset(XMLComponentManager)
  
      /**
       * Returns a list of feature identifiers that are recognized by
       * this component. This method may return null if no features
       * are recognized by this component.
       */
      @Override
      public String[] getRecognizedFeatures() {
          String[] featureIds = super.getRecognizedFeatures();
          int length = featureIds != null ? featureIds.length : 0;
          String[] combinedFeatureIds = new String[length + RECOGNIZED_FEATURES.length];
          if (featureIds != null) {
              System.arraycopy(featureIds, 0, combinedFeatureIds, 0, featureIds.length);
          }
          System.arraycopy(RECOGNIZED_FEATURES, 0, combinedFeatureIds, length, RECOGNIZED_FEATURES.length);
          return combinedFeatureIds;
      } // getRecognizedFeatures():String[]
  
      /**
       * Sets the state of a feature. This method is called by the component
       * manager any time after reset when a feature changes state.
       * <p/>
       * <strong>Note:</strong> Components should silently ignore features
       * that do not affect the operation of the component.
       *
       * @param featureId The feature identifier.
       * @param state     The state of the feature.
       * @throws SAXNotRecognizedException The component should not throw
       *                                   this exception.
       * @throws SAXNotSupportedException  The component should not throw
       *                                   this exception.
       */
      @Override
      public void setFeature(String featureId, boolean state)
              throws XMLConfigurationException {
  
          super.setFeature(featureId, state);
  
          // Xerces properties
          if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
              final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
  
              if (suffixLength == Constants.LOAD_EXTERNAL_DTD_FEATURE.length() &&
                      featureId.endsWith(Constants.LOAD_EXTERNAL_DTD_FEATURE)) {
                  fLoadExternalDTD = state;
                  return;
              } else if (suffixLength == Constants.DISALLOW_DOCTYPE_DECL_FEATURE.length() &&
                      featureId.endsWith(Constants.DISALLOW_DOCTYPE_DECL_FEATURE)) {
                  fDisallowDoctype = state;
                  return;
              }
          }
  
      } // setFeature(String,boolean)
  
      /**
       * Returns a list of property identifiers that are recognized by
       * this component. This method may return null if no properties
       * are recognized by this component.
       */
      @Override
      public String[] getRecognizedProperties() {
          String[] propertyIds = super.getRecognizedProperties();
          int length = propertyIds != null ? propertyIds.length : 0;
          String[] combinedPropertyIds = new String[length + RECOGNIZED_PROPERTIES.length];
          if (propertyIds != null) {
              System.arraycopy(propertyIds, 0, combinedPropertyIds, 0, propertyIds.length);
          }
          System.arraycopy(RECOGNIZED_PROPERTIES, 0, combinedPropertyIds, length, RECOGNIZED_PROPERTIES.length);
          return combinedPropertyIds;
      } // getRecognizedProperties():String[]
  
      /**
       * Sets the value of a property. This method is called by the component
       * manager any time after reset when a property changes value.
       * <p/>
       * <strong>Note:</strong> Components should silently ignore properties
       * that do not affect the operation of the component.
       *
       * @param propertyId The property identifier.
       * @param value      The value of the property.
       * @throws SAXNotRecognizedException The component should not throw
       *                                   this exception.
       * @throws SAXNotSupportedException  The component should not throw
       *                                   this exception.
       */
      @Override
      public void setProperty(String propertyId, Object value)
              throws XMLConfigurationException {
  
          super.setProperty(propertyId, value);
  
          // Xerces properties
          if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
              final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length();
  
              if (suffixLength == Constants.DTD_SCANNER_PROPERTY.length() &&
                      propertyId.endsWith(Constants.DTD_SCANNER_PROPERTY)) {
                  fDTDScanner = (XMLDTDScanner) value;
              }
              if (suffixLength == Constants.NAMESPACE_CONTEXT_PROPERTY.length() &&
                      propertyId.endsWith(Constants.NAMESPACE_CONTEXT_PROPERTY)) {
                  if (value != null) {
                      fNamespaceContext = (NamespaceContext) value;
                  }
              }
  
              return;
          }
  
      } // setProperty(String,Object)
  
      /**
       * Returns the default state for a feature, or null if this
       * component does not want to report a default value for this
       * feature.
       *
       * @param featureId The feature identifier.
       * @since Xerces 2.2.0
       */
      @Override
      public Boolean getFeatureDefault(String featureId) {
  
          for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
              if (RECOGNIZED_FEATURES[i].equals(featureId)) {
                  return FEATURE_DEFAULTS[i];
              }
          }
          return super.getFeatureDefault(featureId);
      } // getFeatureDefault(String):Boolean
  
      /**
       * Returns the default state for a property, or null if this
       * component does not want to report a default value for this
       * property.
       *
       * @param propertyId The property identifier.
       * @since Xerces 2.2.0
       */
      @Override
      public Object getPropertyDefault(String propertyId) {
          for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
              if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
                  return PROPERTY_DEFAULTS[i];
              }
          }
          return super.getPropertyDefault(propertyId);
      } // getPropertyDefault(String):Object
  
      //
      // XMLEntityHandler methods
      //
  
      /**
       * This method notifies of the start of an entity. The DTD has the
       * pseudo-name of "[dtd]" parameter entity names start with '%'; and
       * general entities are just specified by their name.
       *
       * @param name       The name of the entity.
       * @param identifier The resource identifier.
       * @param encoding   The auto-detected IANA encoding name of the entity
       *                   stream. This value will be null in those situations
       *                   where the entity encoding is not auto-detected (e.g.
       *                   internal entities or a document entity that is
       *                   parsed from a java.io.Reader).
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void startEntity(String name,
                              XMLResourceIdentifier identifier,
                              String encoding, Augmentations augs) throws XNIException {
  
          super.startEntity(name, identifier, encoding, augs);
  
          // prepare to look for a TextDecl if external general entity
          if (!name.equals("[xml]") && fEntityScanner.isExternal()) {
              setScannerState(SCANNER_STATE_TEXT_DECL);
          }
  
          // call handler
          if (fDocumentHandler != null && name.equals("[xml]")) {
              fDocumentHandler.startDocument(fEntityScanner, encoding, fNamespaceContext, null);
          }
  
      } // startEntity(String,identifier,String)
  
      /**
       * This method notifies the end of an entity. The DTD has the pseudo-name
       * of "[dtd]" parameter entity names start with '%'; and general entities
       * are just specified by their name.
       *
       * @param name The name of the entity.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endEntity(String name, Augmentations augs) throws XNIException {
  
          super.endEntity(name, augs);
  
          // call handler
          if (fDocumentHandler != null && name.equals("[xml]")) {
              fDocumentHandler.endDocument(null);
          }
  
      } // endEntity(String)
  
      //
      // Protected methods
      //
  
      // dispatcher factory methods
  
      /**
       * Creates a content dispatcher.
       */
      @Override
      protected Dispatcher createContentDispatcher() {
          return new ContentDispatcher();
      } // createContentDispatcher():Dispatcher
  
      // scanning methods
  
      /**
       * Scans a doctype declaration.
       */
      protected boolean scanDoctypeDecl() throws IOException, XNIException {
  
          // spaces
          if (!fEntityScanner.skipSpaces()) {
              reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ROOT_ELEMENT_TYPE_IN_DOCTYPEDECL",
                      null);
          }
  
          // root element name
          fDoctypeName = fEntityScanner.scanName();
          if (fDoctypeName == null) {
              reportFatalError("MSG_ROOT_ELEMENT_TYPE_REQUIRED", null);
          }
  
          // external id
          if (fEntityScanner.skipSpaces()) {
              scanExternalID(fStrings, false);
              fDoctypeSystemId = fStrings[0];
              fDoctypePublicId = fStrings[1];
              fEntityScanner.skipSpaces();
          }
  
          fHasExternalDTD = fDoctypeSystemId != null;
  
          // Attempt to locate an external subset with an external subset resolver.
          if (!fHasExternalDTD && fExternalSubsetResolver != null) {
              fDTDDescription.setValues(null, null, fEntityManager.getCurrentResourceIdentifier().getExpandedSystemId(), null);
              fDTDDescription.setRootName(fDoctypeName);
              fExternalSubsetSource = fExternalSubsetResolver.getExternalSubset(fDTDDescription);
              fHasExternalDTD = fExternalSubsetSource != null;
          }
  
          // call handler
          if (fDocumentHandler != null) {
              // NOTE: I don't like calling the doctypeDecl callback until
              //       end of the *full* doctype line (including internal
              //       subset) is parsed correctly but SAX2 requires that
              //       it knows the root element name and public and system
              //       identifier for the startDTD call. -Ac
              if (fExternalSubsetSource == null) {
                  fDocumentHandler.doctypeDecl(fDoctypeName, fDoctypePublicId, fDoctypeSystemId, null);
              } else {
                  fDocumentHandler.doctypeDecl(fDoctypeName, fExternalSubsetSource.getPublicId(), fExternalSubsetSource.getSystemId(), null);
              }
          }
  
          // is there an internal subset?
          boolean internalSubset = true;
          if (!fEntityScanner.skipChar('[')) {
              internalSubset = false;
              fEntityScanner.skipSpaces();
              if (!fEntityScanner.skipChar('>')) {
                  reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName});
              }
              fMarkupDepth--;
          }
  
          return internalSubset;
  
      } // scanDoctypeDecl():boolean
  
      //
      // Private methods
      //
  
      /**
       * Returns the scanner state name.
       */
      @Override
      protected String getScannerStateName(int state) {
  
          switch (state) {
              case SCANNER_STATE_XML_DECL:
                  return "SCANNER_STATE_XML_DECL";
              case SCANNER_STATE_PROLOG:
                  return "SCANNER_STATE_PROLOG";
              case SCANNER_STATE_TRAILING_MISC:
                  return "SCANNER_STATE_TRAILING_MISC";
              case SCANNER_STATE_DTD_INTERNAL_DECLS:
                  return "SCANNER_STATE_DTD_INTERNAL_DECLS";
              case SCANNER_STATE_DTD_EXTERNAL:
                  return "SCANNER_STATE_DTD_EXTERNAL";
              case SCANNER_STATE_DTD_EXTERNAL_DECLS:
                  return "SCANNER_STATE_DTD_EXTERNAL_DECLS";
          }
          return super.getScannerStateName(state);
  
      } // getScannerStateName(int):String
  
      //
      // Classes
      //
  
      /**
       * Dispatcher to handle XMLDecl scanning.
       *
       * @author Andy Clark, IBM
       */
      protected final class XMLDeclDispatcher
              implements Dispatcher {
  
          //
          // Dispatcher methods
          //
  
          /**
           * Dispatch an XML "event".
           *
           * @param complete True if this dispatcher is intended to scan
           *                 and dispatch as much as possible.
           * @return True if there is more to dispatch either from this
           * or a another dispatcher.
           * @throws IOException  Thrown on i/o error.
           * @throws XNIException Thrown on parse error.
           */
          @Override
          public boolean dispatch(boolean complete)
                  throws IOException, XNIException {
  
              // next dispatcher is prolog regardless of whether there
              // is an XMLDecl in this document
              setScannerState(SCANNER_STATE_PROLOG);
              setDispatcher(fPrologDispatcher);
  
              // scan XMLDecl
              try {
                  if (fEntityScanner.skipString("<?xml")) {
                      fMarkupDepth++;
                      // NOTE: special case where document starts with a PI
                      //       whose name starts with "xml" (e.g. "xmlfoo")
                      if (XMLChar.isName(fEntityScanner.peekChar())) {
                          fStringBuffer.clear();
                          fStringBuffer.append("xml");
                          if (fNamespaces) {
                              while (XMLChar.isNCName(fEntityScanner.peekChar())) {
                                  fStringBuffer.append((char) fEntityScanner.scanChar());
                              }
                          } else {
                              while (XMLChar.isName(fEntityScanner.peekChar())) {
                                  fStringBuffer.append((char) fEntityScanner.scanChar());
                              }
                          }
                          String target = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
                          scanPIData(target, fString);
                      }
  
                      // standard XML declaration
                      else {
                          scanXMLDeclOrTextDecl(false);
                      }
                  }
                  fEntityManager.fCurrentEntity.mayReadChunks = true;
  
                  // if no XMLDecl, then scan piece of prolog
                  return true;
              }
              // encoding errors
              catch (MalformedByteSequenceException e) {
                  fErrorReporter.reportError(e.getDomain(), e.getKey(),
                          e.getArguments(), XMLErrorReporter.SEVERITY_FATAL_ERROR, e);
                  return false;
              } catch (CharConversionException e) {
                  fErrorReporter.reportError(
                          XMLMessageFormatter.XML_DOMAIN,
                          "CharConversionFailure",
                          null,
                          XMLErrorReporter.SEVERITY_FATAL_ERROR, e);
                  return false;
              }
              // premature end of file
              catch (EOFException e) {
                  reportFatalError("PrematureEOF", null);
                  return false;
                  //throw e;
              }
  
  
          } // dispatch(boolean):boolean
  
      } // class XMLDeclDispatcher
  
      /**
       * Dispatcher to handle prolog scanning.
       *
       * @author Andy Clark, IBM
       */
      protected final class PrologDispatcher
              implements Dispatcher {
  
          //
          // Dispatcher methods
          //
  
          /**
           * Dispatch an XML "event".
           *
           * @param complete True if this dispatcher is intended to scan
           *                 and dispatch as much as possible.
           * @return True if there is more to dispatch either from this
           * or a another dispatcher.
           * @throws IOException  Thrown on i/o error.
           * @throws XNIException Thrown on parse error.
           */
          @Override
          public boolean dispatch(boolean complete)
                  throws IOException, XNIException {
  
              try {
                  boolean again;
                  do {
                      again = false;
                      switch (fScannerState) {
                          case SCANNER_STATE_PROLOG: {
                              fEntityScanner.skipSpaces();
                              if (fEntityScanner.skipChar('<')) {
                                  setScannerState(SCANNER_STATE_START_OF_MARKUP);
                                  again = true;
                              } else if (fEntityScanner.skipChar('&')) {
                                  setScannerState(SCANNER_STATE_REFERENCE);
                                  again = true;
                              } else {
                                  setScannerState(SCANNER_STATE_CONTENT);
                                  again = true;
                              }
                              break;
                          }
                          case SCANNER_STATE_START_OF_MARKUP: {
                              fMarkupDepth++;
                              if (fEntityScanner.skipChar('!')) {
                                  if (fEntityScanner.skipChar('-')) {
                                      if (!fEntityScanner.skipChar('-')) {
                                          reportFatalError("InvalidCommentStart",
                                                  null);
                                      }
                                      setScannerState(SCANNER_STATE_COMMENT);
                                      again = true;
                                  } else if (fEntityScanner.skipString("DOCTYPE")) {
                                      setScannerState(SCANNER_STATE_DOCTYPE);
                                      again = true;
                                  } else {
                                      reportFatalError("MarkupNotRecognizedInProlog",
                                              null);
                                  }
                              } else if (isValidNameStartChar(fEntityScanner.peekChar())) {
                                  setScannerState(SCANNER_STATE_ROOT_ELEMENT);
                                  setDispatcher(fContentDispatcher);
                                  return true;
                              } else if (fEntityScanner.skipChar('?')) {
                                  setScannerState(SCANNER_STATE_PI);
                                  again = true;
                              } else if (isValidNameStartHighSurrogate(fEntityScanner.peekChar())) {
                                  setScannerState(SCANNER_STATE_ROOT_ELEMENT);
                                  setDispatcher(fContentDispatcher);
                                  return true;
                              } else {
                                  reportFatalError("MarkupNotRecognizedInProlog",
                                          null);
                              }
                              break;
                          }
                          case SCANNER_STATE_COMMENT: {
                              scanComment();
                              setScannerState(SCANNER_STATE_PROLOG);
                              break;
                          }
                          case SCANNER_STATE_PI: {
                              scanPI();
                              setScannerState(SCANNER_STATE_PROLOG);
                              break;
                          }
                          case SCANNER_STATE_DOCTYPE: {
                              if (fDisallowDoctype) {
                                  reportFatalError("DoctypeNotAllowed", null);
                              }
                              if (fSeenDoctypeDecl) {
                                  reportFatalError("AlreadySeenDoctype", null);
                              }
                              fSeenDoctypeDecl = true;
  
                              // scanDoctypeDecl() sends XNI doctypeDecl event that 
                              // in SAX is converted to startDTD() event.
                              if (scanDoctypeDecl()) {
                                  setScannerState(SCANNER_STATE_DTD_INTERNAL_DECLS);
                                  setDispatcher(fDTDDispatcher);
                                  return true;
                              }
  
                              // handle external subset
                              if (fDoctypeSystemId != null) {
                                  fIsEntityDeclaredVC = !fStandalone;
                                  if (((fValidation || fLoadExternalDTD)
                                          && (fValidationManager == null || !fValidationManager.isCachedDTD()))) {
                                      setScannerState(SCANNER_STATE_DTD_EXTERNAL);
                                      setDispatcher(fDTDDispatcher);
                                      return true;
                                  }
                              } else if (fExternalSubsetSource != null) {
                                  fIsEntityDeclaredVC = !fStandalone;
                                  if (((fValidation || fLoadExternalDTD)
                                          && (fValidationManager == null || !fValidationManager.isCachedDTD()))) {
                                      // This handles the case of a DOCTYPE that had neither an internal subset or an external subset.
                                      fDTDScanner.setInputSource(fExternalSubsetSource);
                                      fExternalSubsetSource = null;
                                      setScannerState(SCANNER_STATE_DTD_EXTERNAL_DECLS);
                                      setDispatcher(fDTDDispatcher);
                                      return true;
                                  }
                              }
  
                              // Send endDTD() call if: 
                              // a) systemId is null or if an external subset resolver could not locate an external subset.
                              // b) "load-external-dtd" and validation are false
                              // c) DTD grammar is cached
  
                              // in XNI this results in 3 events:  doctypeDecl, startDTD, endDTD
                              // in SAX this results in 2 events: startDTD, endDTD
                              fDTDScanner.setInputSource(null);
                              setScannerState(SCANNER_STATE_PROLOG);
                              break;
                          }
                          case SCANNER_STATE_CONTENT: {
                              reportFatalError("ContentIllegalInProlog", null);
                              fEntityScanner.scanChar();
                          }
                          case SCANNER_STATE_REFERENCE: {
                              reportFatalError("ReferenceIllegalInProlog", null);
                          }
                      }
                  } while (complete || again);
  
                  if (complete) {
                      if (fEntityScanner.scanChar() != '<') {
                          reportFatalError("RootElementRequired", null);
                      }
                      setScannerState(SCANNER_STATE_ROOT_ELEMENT);
                      setDispatcher(fContentDispatcher);
                  }
              }
              // encoding errors
              catch (MalformedByteSequenceException e) {
                  fErrorReporter.reportError(e.getDomain(), e.getKey(),
                          e.getArguments(), XMLErrorReporter.SEVERITY_FATAL_ERROR, e);
                  return false;
              } catch (CharConversionException e) {
                  fErrorReporter.reportError(
                          XMLMessageFormatter.XML_DOMAIN,
                          "CharConversionFailure",
                          null,
                          XMLErrorReporter.SEVERITY_FATAL_ERROR, e);
                  return false;
              }
              // premature end of file
              catch (EOFException e) {
                  reportFatalError("PrematureEOF", null);
                  return false;
                  //throw e;
              }
  
              return true;
  
          } // dispatch(boolean):boolean
  
      } // class PrologDispatcher
  
      /**
       * Dispatcher to handle the internal and external DTD subsets.
       *
       * @author Andy Clark, IBM
       */
      protected final class DTDDispatcher
              implements Dispatcher {
  
          //
          // Dispatcher methods
          //
  
          /**
           * Dispatch an XML "event".
           *
           * @param complete True if this dispatcher is intended to scan
           *                 and dispatch as much as possible.
           * @return True if there is more to dispatch either from this
           * or a another dispatcher.
           * @throws IOException  Thrown on i/o error.
           * @throws XNIException Thrown on parse error.
           */
          @Override
          public boolean dispatch(boolean complete)
                  throws IOException, XNIException {
              fEntityManager.setEntityHandler(null);
              try {
                  boolean again;
                  do {
                      again = false;
                      switch (fScannerState) {
                          case SCANNER_STATE_DTD_INTERNAL_DECLS: {
                              // REVISIT: Should there be a feature for
                              //          the "complete" parameter?
                              boolean completeDTD = true;
                              boolean readExternalSubset = (fValidation || fLoadExternalDTD) && (fValidationManager == null || !fValidationManager.isCachedDTD());
                              boolean moreToScan = fDTDScanner.scanDTDInternalSubset(completeDTD, fStandalone, fHasExternalDTD && readExternalSubset);
                              if (!moreToScan) {
                                  // end doctype declaration
                                  if (!fEntityScanner.skipChar(']')) {
                                      reportFatalError("EXPECTED_SQUARE_BRACKET_TO_CLOSE_INTERNAL_SUBSET",
                                              null);
                                  }
                                  fEntityScanner.skipSpaces();
                                  if (!fEntityScanner.skipChar('>')) {
                                      reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName});
                                  }
                                  fMarkupDepth--;
  
                                  // scan external subset next
                                  if (fDoctypeSystemId != null) {
                                      fIsEntityDeclaredVC = !fStandalone;
                                      if (readExternalSubset) {
                                          setScannerState(SCANNER_STATE_DTD_EXTERNAL);
                                          break;
                                      }
                                  } else if (fExternalSubsetSource != null) {
                                      fIsEntityDeclaredVC = !fStandalone;
                                      if (readExternalSubset) {
                                          // This handles the case of a DOCTYPE that only had an internal subset.
                                          fDTDScanner.setInputSource(fExternalSubsetSource);
                                          fExternalSubsetSource = null;
                                          setScannerState(SCANNER_STATE_DTD_EXTERNAL_DECLS);
                                          break;
                                      }
                                  }
                                  // This document only has an internal subset. If it contains parameter entity
                                  // references and standalone="no" then [Entity Declared] is a validity constraint.
                                  else {
                                      fIsEntityDeclaredVC = fEntityManager.hasPEReferences() && !fStandalone;
                                  }
  
                                  // break out of this dispatcher.
                                  setScannerState(SCANNER_STATE_PROLOG);
                                  setDispatcher(fPrologDispatcher);
                                  fEntityManager.setEntityHandler(XMLDocumentScannerImpl.this);
                                  return true;
                              }
                              break;
                          }
                          case SCANNER_STATE_DTD_EXTERNAL: {
                              fDTDDescription.setValues(fDoctypePublicId, fDoctypeSystemId, null, null);
                              fDTDDescription.setRootName(fDoctypeName);
                              XMLInputSource xmlInputSource =
                                      fEntityManager.resolveEntity(fDTDDescription);
                              fDTDScanner.setInputSource(xmlInputSource);
                              setScannerState(SCANNER_STATE_DTD_EXTERNAL_DECLS);
                              again = true;
                              break;
                          }
                          case SCANNER_STATE_DTD_EXTERNAL_DECLS: {
                              // REVISIT: Should there be a feature for
                              //          the "complete" parameter?
                              boolean completeDTD = true;
                              boolean moreToScan = fDTDScanner.scanDTDExternalSubset(completeDTD);
                              if (!moreToScan) {
                                  setScannerState(SCANNER_STATE_PROLOG);
                                  setDispatcher(fPrologDispatcher);
                                  fEntityManager.setEntityHandler(XMLDocumentScannerImpl.this);
                                  return true;
                              }
                              break;
                          }
                          default: {
                              throw new XNIException("DTDDispatcher#dispatch: scanner state=" + fScannerState + " (" + getScannerStateName(fScannerState) + ')');
                          }
                      }
                  } while (complete || again);
              }
              // encoding errors
              catch (MalformedByteSequenceException e) {
                  fErrorReporter.reportError(e.getDomain(), e.getKey(),
                          e.getArguments(), XMLErrorReporter.SEVERITY_FATAL_ERROR, e);
                  return false;
              } catch (CharConversionException e) {
                  fErrorReporter.reportError(
                          XMLMessageFormatter.XML_DOMAIN,
                          "CharConversionFailure",
                          null,
                          XMLErrorReporter.SEVERITY_FATAL_ERROR, e);
                  return false;
              }
              // premature end of file
              catch (EOFException e) {
                  reportFatalError("PrematureEOF", null);
                  return false;
                  //throw e;
              }
  
              // cleanup
              finally {
                  fEntityManager.setEntityHandler(XMLDocumentScannerImpl.this);
              }
  
              return true;
  
          } // dispatch(boolean):boolean
  
      } // class DTDDispatcher
  
      /**
       * Dispatcher to handle content scanning.
       *
       * @author Andy Clark, IBM
       * @author Eric Ye, IBM
       */
      protected class ContentDispatcher
              extends FragmentContentDispatcher {
  
          //
          // Protected methods
          //
  
          // hooks
  
          // NOTE: These hook methods are added so that the full document
          //       scanner can share the majority of code with this class.
  
          /**
           * Scan for DOCTYPE hook. This method is a hook for subclasses
           * to add code to handle scanning for a the "DOCTYPE" string
           * after the string "<!" has been scanned.
           *
           * @return True if the "DOCTYPE" was scanned; false if "DOCTYPE"
           * was not scanned.
           */
          @Override
          protected boolean scanForDoctypeHook()
                  throws IOException, XNIException {
  
              if (fEntityScanner.skipString("DOCTYPE")) {
                  setScannerState(SCANNER_STATE_DOCTYPE);
                  return true;
              }
              return false;
  
          } // scanForDoctypeHook():boolean
  
          /**
           * Element depth iz zero. This methos is a hook for subclasses
           * to add code to handle when the element depth hits zero. When
           * scanning a document fragment, an element depth of zero is
           * normal. However, when scanning a full XML document, the
           * scanner must handle the trailing miscellanous section of
           * the document after the end of the document's root element.
           *
           * @return True if the caller should stop and return true which
           * allows the scanner to switch to a new scanning
           * dispatcher. A return value of false indicates that
           * the content dispatcher should continue as normal.
           */
          @Override
          protected boolean elementDepthIsZeroHook()
                  throws IOException, XNIException {
  
              setScannerState(SCANNER_STATE_TRAILING_MISC);
              setDispatcher(fTrailingMiscDispatcher);
              return true;
  
          } // elementDepthIsZeroHook():boolean
  
          /**
           * Scan for root element hook. This method is a hook for
           * subclasses to add code that handles scanning for the root
           * element. When scanning a document fragment, there is no
           * "root" element. However, when scanning a full XML document,
           * the scanner must handle the root element specially.
           *
           * @return True if the caller should stop and return true which
           * allows the scanner to switch to a new scanning
           * dispatcher. A return value of false indicates that
           * the content dispatcher should continue as normal.
           */
          @Override
          protected boolean scanRootElementHook()
                  throws IOException, XNIException {
  
              if (fExternalSubsetResolver != null && !fSeenDoctypeDecl
                      && !fDisallowDoctype && (fValidation || fLoadExternalDTD)) {
                  scanStartElementName();
                  resolveExternalSubsetAndRead();
                  if (scanStartElementAfterName()) {
                      setScannerState(SCANNER_STATE_TRAILING_MISC);
                      setDispatcher(fTrailingMiscDispatcher);
                      return true;
                  }
              } else if (scanStartElement()) {
                  setScannerState(SCANNER_STATE_TRAILING_MISC);
                  setDispatcher(fTrailingMiscDispatcher);
                  return true;
              }
              return false;
  
          } // scanRootElementHook():boolean
  
          /**
           * End of file hook. This method is a hook for subclasses to
           * add code that handles the end of file. The end of file in
           * a document fragment is OK if the markup depth is zero.
           * However, when scanning a full XML document, an end of file
           * is always premature.
           */
          @Override
          protected void endOfFileHook(EOFException e)
                  throws IOException, XNIException {
  
              reportFatalError("PrematureEOF", null);
              // in case continue-after-fatal-error set, should not do this...
              //throw e;
  
          } // endOfFileHook()
  
          /**
           * <p>Attempt to locate an external subset for a document that does not otherwise
           * have one. If an external subset is located, then it is scanned.</p>
           */
          protected void resolveExternalSubsetAndRead()
                  throws IOException, XNIException {
  
              fDTDDescription.setValues(null, null, fEntityManager.getCurrentResourceIdentifier().getExpandedSystemId(), null);
              fDTDDescription.setRootName(fElementQName.rawname);
              XMLInputSource src = fExternalSubsetResolver.getExternalSubset(fDTDDescription);
  
              if (src != null) {
                  fDoctypeName = fElementQName.rawname;
                  fDoctypePublicId = src.getPublicId();
                  fDoctypeSystemId = src.getSystemId();
                  // call document handler
                  if (fDocumentHandler != null) {
                      // This inserts a doctypeDecl event into the stream though no 
                      // DOCTYPE existed in the instance document.
                      fDocumentHandler.doctypeDecl(fDoctypeName, fDoctypePublicId, fDoctypeSystemId, null);
                  }
                  try {
                      if (fValidationManager == null || !fValidationManager.isCachedDTD()) {
                          fDTDScanner.setInputSource(src);
                          while (fDTDScanner.scanDTDExternalSubset(true)) ;
                      } else {
                          // This sends startDTD and endDTD calls down the pipeline.
                          fDTDScanner.setInputSource(null);
                      }
                  } finally {
                      fEntityManager.setEntityHandler(XMLDocumentScannerImpl.this);
                  }
              }
          } // resolveExternalSubsetAndRead()
  
      } // class ContentDispatcher
  
      /**
       * Dispatcher to handle trailing miscellaneous section scanning.
       *
       * @author Andy Clark, IBM
       * @author Eric Ye, IBM
       */
      protected final class TrailingMiscDispatcher
              implements Dispatcher {
  
          //
          // Dispatcher methods
          //
  
          /**
           * Dispatch an XML "event".
           *
           * @param complete True if this dispatcher is intended to scan
           *                 and dispatch as much as possible.
           * @return True if there is more to dispatch either from this
           * or a another dispatcher.
           * @throws IOException  Thrown on i/o error.
           * @throws XNIException Thrown on parse error.
           */
          @Override
          public boolean dispatch(boolean complete)
                  throws IOException, XNIException {
  
              try {
                  boolean again;
                  do {
                      again = false;
                      switch (fScannerState) {
                          case SCANNER_STATE_TRAILING_MISC: {
                              fEntityScanner.skipSpaces();
                              if (fEntityScanner.skipChar('<')) {
                                  setScannerState(SCANNER_STATE_START_OF_MARKUP);
                                  again = true;
                              } else {
                                  setScannerState(SCANNER_STATE_CONTENT);
                                  again = true;
                              }
                              break;
                          }
                          case SCANNER_STATE_START_OF_MARKUP: {
                              fMarkupDepth++;
                              if (fEntityScanner.skipChar('?')) {
                                  setScannerState(SCANNER_STATE_PI);
                                  again = true;
                              } else if (fEntityScanner.skipChar('!')) {
                                  setScannerState(SCANNER_STATE_COMMENT);
                                  again = true;
                              } else if (fEntityScanner.skipChar('/')) {
                                  reportFatalError("MarkupNotRecognizedInMisc",
                                          null);
                                  again = true;
                              } else if (isValidNameStartChar(fEntityScanner.peekChar())) {
                                  reportFatalError("MarkupNotRecognizedInMisc",
                                          null);
                                  scanStartElement();
                                  setScannerState(SCANNER_STATE_CONTENT);
                              } else if (isValidNameStartHighSurrogate(fEntityScanner.peekChar())) {
                                  reportFatalError("MarkupNotRecognizedInMisc",
                                          null);
                                  scanStartElement();
                                  setScannerState(SCANNER_STATE_CONTENT);
                              } else {
                                  reportFatalError("MarkupNotRecognizedInMisc",
                                          null);
                              }
                              break;
                          }
                          case SCANNER_STATE_PI: {
                              scanPI();
                              setScannerState(SCANNER_STATE_TRAILING_MISC);
                              break;
                          }
                          case SCANNER_STATE_COMMENT: {
                              if (!fEntityScanner.skipString("--")) {
                                  reportFatalError("InvalidCommentStart", null);
                              }
                              scanComment();
                              setScannerState(SCANNER_STATE_TRAILING_MISC);
                              break;
                          }
                          case SCANNER_STATE_CONTENT: {
                              int ch = fEntityScanner.peekChar();
                              if (ch == -1) {
                                  setScannerState(SCANNER_STATE_TERMINATED);
                                  return false;
                              }
                              reportFatalError("ContentIllegalInTrailingMisc",
                                      null);
                              fEntityScanner.scanChar();
                              setScannerState(SCANNER_STATE_TRAILING_MISC);
                              break;
                          }
                          case SCANNER_STATE_REFERENCE: {
                              reportFatalError("ReferenceIllegalInTrailingMisc",
                                      null);
                              setScannerState(SCANNER_STATE_TRAILING_MISC);
                              break;
                          }
                          case SCANNER_STATE_TERMINATED: {
                              return false;
                          }
                      }
                  } while (complete || again);
              }
              // encoding errors
              catch (MalformedByteSequenceException e) {
                  fErrorReporter.reportError(e.getDomain(), e.getKey(),
                          e.getArguments(), XMLErrorReporter.SEVERITY_FATAL_ERROR, e);
                  return false;
              } catch (CharConversionException e) {
                  fErrorReporter.reportError(
                          XMLMessageFormatter.XML_DOMAIN,
                          "CharConversionFailure",
                          null,
                          XMLErrorReporter.SEVERITY_FATAL_ERROR, e);
                  return false;
              } catch (EOFException e) {
                  // NOTE: This is the only place we're allowed to reach
                  //       the real end of the document stream. Unless the
                  //       end of file was reached prematurely.
                  if (fMarkupDepth != 0) {
                      reportFatalError("PrematureEOF", null);
                      return false;
                      //throw e;
                  }
  
                  setScannerState(SCANNER_STATE_TERMINATED);
                  return false;
              }
  
              return true;
  
          } // dispatch(boolean):boolean
  
      } // class TrailingMiscDispatcher
  
  } // class XMLDocumentScannerImpl
  