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.parsers;
  
  import org.xml.sax.SAXException;
  
  import java.util.Locale;
  import java.util.Stack;
  
  import mf.org.apache.xerces.dom.AttrImpl;
  import mf.org.apache.xerces.dom.CoreDocumentImpl;
  import mf.org.apache.xerces.dom.DOMErrorImpl;
  import mf.org.apache.xerces.dom.DOMMessageFormatter;
  import mf.org.apache.xerces.dom.DeferredDocumentImpl;
  import mf.org.apache.xerces.dom.DocumentImpl;
  import mf.org.apache.xerces.dom.DocumentTypeImpl;
  import mf.org.apache.xerces.dom.ElementDefinitionImpl;
  import mf.org.apache.xerces.dom.ElementImpl;
  import mf.org.apache.xerces.dom.ElementNSImpl;
  import mf.org.apache.xerces.dom.EntityImpl;
  import mf.org.apache.xerces.dom.EntityReferenceImpl;
  import mf.org.apache.xerces.dom.NodeImpl;
  import mf.org.apache.xerces.dom.NotationImpl;
  import mf.org.apache.xerces.dom.PSVIAttrNSImpl;
  import mf.org.apache.xerces.dom.PSVIDocumentImpl;
  import mf.org.apache.xerces.dom.PSVIElementNSImpl;
  import mf.org.apache.xerces.dom.TextImpl;
  import mf.org.apache.xerces.impl.Constants;
  import mf.org.apache.xerces.impl.dv.XSSimpleType;
  import mf.org.apache.xerces.util.DOMErrorHandlerWrapper;
  import mf.org.apache.xerces.xni.Augmentations;
  import mf.org.apache.xerces.xni.NamespaceContext;
  import mf.org.apache.xerces.xni.QName;
  import mf.org.apache.xerces.xni.XMLAttributes;
  import mf.org.apache.xerces.xni.XMLLocator;
  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.XMLParserConfiguration;
  import mf.org.apache.xerces.xs.AttributePSVI;
  import mf.org.apache.xerces.xs.ElementPSVI;
  import mf.org.apache.xerces.xs.XSTypeDefinition;
  import mf.org.w3c.dom.Attr;
  import mf.org.w3c.dom.CDATASection;
  import mf.org.w3c.dom.Comment;
  import mf.org.w3c.dom.DOMError;
  import mf.org.w3c.dom.Document;
  import mf.org.w3c.dom.DocumentType;
  import mf.org.w3c.dom.Element;
  import mf.org.w3c.dom.EntityReference;
  import mf.org.w3c.dom.NamedNodeMap;
  import mf.org.w3c.dom.Node;
  import mf.org.w3c.dom.NodeList;
  import mf.org.w3c.dom.ProcessingInstruction;
  import mf.org.w3c.dom.Text;
  import mf.org.w3c.dom.ls.LSParserFilter;
  import mf.org.w3c.dom.traversal.NodeFilter;
  
  /**
   * This is the base class of all DOM parsers. It implements the XNI
   * callback methods to create the DOM tree. After a successful parse of
   * an XML document, the DOM Document object can be queried using the
   * <code>getDocument</code> method. The actual pipeline is defined in
   * parser configuration.
   *
   * @author Arnaud Le Hors, IBM
   * @author Andy Clark, IBM
   * @author Elena Litani, IBM
   * @version $Id: AbstractDOMParser.java 782187 2009-06-06 04:21:12Z mrglavas $
   */
  public class AbstractDOMParser extends AbstractXMLDocumentParser {
  
      //
      // Constants
      //
  
      // feature ids
  
      /**
       * Feature id: namespace.
       */
      protected static final String NAMESPACES =
              Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
  
      /**
       * Feature id: create entity ref nodes.
       */
      protected static final String CREATE_ENTITY_REF_NODES =
              Constants.XERCES_FEATURE_PREFIX + Constants.CREATE_ENTITY_REF_NODES_FEATURE;
  
      /**
       * Feature id: include comments.
       */
      protected static final String INCLUDE_COMMENTS_FEATURE =
              Constants.XERCES_FEATURE_PREFIX + Constants.INCLUDE_COMMENTS_FEATURE;
  
      /**
       * Feature id: create cdata nodes.
       */
      protected static final String CREATE_CDATA_NODES_FEATURE =
              Constants.XERCES_FEATURE_PREFIX + Constants.CREATE_CDATA_NODES_FEATURE;
  
      /**
       * Feature id: include ignorable whitespace.
       */
      protected static final String INCLUDE_IGNORABLE_WHITESPACE =
              Constants.XERCES_FEATURE_PREFIX + Constants.INCLUDE_IGNORABLE_WHITESPACE;
  
      /**
       * Feature id: defer node expansion.
       */
      protected static final String DEFER_NODE_EXPANSION =
              Constants.XERCES_FEATURE_PREFIX + Constants.DEFER_NODE_EXPANSION_FEATURE;
      /**
       * Property id: document class name.
       */
      protected static final String DOCUMENT_CLASS_NAME =
              Constants.XERCES_PROPERTY_PREFIX + Constants.DOCUMENT_CLASS_NAME_PROPERTY;
  
      // property ids
      protected static final String CURRENT_ELEMENT_NODE =
              Constants.XERCES_PROPERTY_PREFIX + Constants.CURRENT_ELEMENT_NODE_PROPERTY;
      /**
       * Default document class name.
       */
      //MF
      protected static final String DEFAULT_DOCUMENT_CLASS_NAME =
              "mf.org.apache.xerces.dom.DocumentImpl";
  
      // protected static final String GRAMMAR_POOL =
      // Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY;
      protected static final String CORE_DOCUMENT_CLASS_NAME =
              "mf.org.apache.xerces.dom.CoreDocumentImpl";
  
      // other
      protected static final String PSVI_DOCUMENT_CLASS_NAME =
              "mf.org.apache.xerces.dom.PSVIDocumentImpl";
      /**
       * Recognized features.
       */
      private static final String[] RECOGNIZED_FEATURES = {
              NAMESPACES,
              CREATE_ENTITY_REF_NODES,
              INCLUDE_COMMENTS_FEATURE,
              CREATE_CDATA_NODES_FEATURE,
              INCLUDE_IGNORABLE_WHITESPACE,
              DEFER_NODE_EXPANSION
      };
      /**
       * Recognized properties.
       */
      private static final String[] RECOGNIZED_PROPERTIES = {
              DOCUMENT_CLASS_NAME,
              CURRENT_ELEMENT_NODE,
      };
      private static final boolean DEBUG_EVENTS = false;
  
      // debugging
      private static final boolean DEBUG_BASEURI = false;
      /**
       * Character buffer
       */
      protected final StringBuffer fStringBuffer = new StringBuffer(50);
  
      //
      // Data
      //
      /**
       * Base uri stack
       */
      protected final Stack fBaseURIStack = new Stack();
      /**
       * Attribute QName.
       */
      private final QName fAttrQName = new QName();
  
      // features
      /**
       * DOM L3 error handler
       */
      protected DOMErrorHandlerWrapper fErrorHandler = null;
      /**
       * True if inside DTD.
       */
      protected boolean fInDTD;
      /**
       * Create entity reference nodes.
       */
      protected boolean fCreateEntityRefNodes;
      /**
       * Include ignorable whitespace.
       */
      protected boolean fIncludeIgnorableWhitespace;
  
      // dom information
      /**
       * Include Comments.
       */
      protected boolean fIncludeComments;
      /**
       * Create cdata nodes.
       */
      protected boolean fCreateCDATANodes;
      /**
       * The document.
       */
      protected Document fDocument;
      /**
       * The default Xerces document implementation, if used.
       */
      protected CoreDocumentImpl fDocumentImpl;
      /**
       * Whether to store PSVI information in DOM tree.
       */
      protected boolean fStorePSVI;
      /**
       * The document class name to use.
       */
      protected String fDocumentClassName;
      /**
       * The document type node.
       */
      protected DocumentType fDocumentType;
      /**
       * Current node.
       */
      protected Node fCurrentNode;
      protected CDATASection fCurrentCDATASection;
      protected EntityImpl fCurrentEntityDecl;
  
      // internal subset
      protected int fDeferredEntityDecl;
  
      // deferred expansion data
      /**
       * Internal subset buffer.
       */
      protected StringBuffer fInternalSubset;
      protected boolean fDeferNodeExpansion;
      protected boolean fNamespaceAware;
      protected DeferredDocumentImpl fDeferredDocumentImpl;
      protected int fDocumentIndex;
      protected int fDocumentTypeIndex;
      protected int fCurrentNodeIndex;
  
      // state
      protected int fCurrentCDATASectionIndex;
      /**
       * True if inside DTD external subset.
       */
      protected boolean fInDTDExternalSubset;
      /**
       * Root element node.
       */
      protected Node fRoot;
      /**
       * True if inside CDATA section.
       */
      protected boolean fInCDATASection;
      /**
       * True if saw the first chunk of characters
       */
      protected boolean fFirstChunk = false;
  
      // data
      /**
       * LSParserFilter: specifies that element with given QNAME and all its children
       * must be rejected
       */
      protected boolean fFilterReject = false;
      /**
       * LSParserFilter: tracks the element depth within a rejected subtree.
       */
      protected int fRejectedElementDepth = 0;
  
      /**
       * LSParserFilter: store depth of skipped elements
       */
      protected Stack fSkippedElemStack = null;
  
      /**
       * LSParserFilter: true if inside entity reference
       */
      protected boolean fInEntityRef = false;
      protected LSParserFilter fDOMFilter = null;
      /**
       * Document locator.
       */
      private XMLLocator fLocator;
  
      // handlers
  
      /**
       * Default constructor.
       */
      protected AbstractDOMParser(XMLParserConfiguration config) {
  
          super(config);
  
  
          // add recognized features
          fConfiguration.addRecognizedFeatures(RECOGNIZED_FEATURES);
  
          // set default values
          fConfiguration.setFeature(CREATE_ENTITY_REF_NODES, true);
          fConfiguration.setFeature(INCLUDE_IGNORABLE_WHITESPACE, true);
          fConfiguration.setFeature(DEFER_NODE_EXPANSION, true);
          fConfiguration.setFeature(INCLUDE_COMMENTS_FEATURE, true);
          fConfiguration.setFeature(CREATE_CDATA_NODES_FEATURE, true);
  
          // add recognized properties
          fConfiguration.addRecognizedProperties(RECOGNIZED_PROPERTIES);
  
          // set default values
          fConfiguration.setProperty(DOCUMENT_CLASS_NAME,
                  DEFAULT_DOCUMENT_CLASS_NAME);
  
      } // <init>(XMLParserConfiguration)
  
      //
      // Constructors
      //
  
      /**
       * This method retreives the name of current document class.
       */
      protected String getDocumentClassName() {
          return fDocumentClassName;
      }
  
      /**
       * This method allows the programmer to decide which document
       * factory to use when constructing the DOM tree. However, doing
       * so will lose the functionality of the default factory. Also,
       * a document class other than the default will lose the ability
       * to defer node expansion on the DOM tree produced.
       *
       * @param documentClassName The fully qualified class name of the
       *                          document factory to use when constructing
       *                          the DOM tree.
       * @see #getDocumentClassName
       * @see #DEFAULT_DOCUMENT_CLASS_NAME
       */
      protected void setDocumentClassName(String documentClassName) {
  
          // normalize class name
          if (documentClassName == null) {
              documentClassName = DEFAULT_DOCUMENT_CLASS_NAME;
          }
  
          if (!documentClassName.equals(DEFAULT_DOCUMENT_CLASS_NAME) &&
                  !documentClassName.equals(PSVI_DOCUMENT_CLASS_NAME)) {
              // verify that this class exists and is of the right type
              try {
                  Class _class = ObjectFactory.findProviderClass(documentClassName,
                          ObjectFactory.findClassLoader(), true);
                  //if (!_class.isAssignableFrom(Document.class)) {
                  if (!Document.class.isAssignableFrom(_class)) {
                      throw new IllegalArgumentException(
                              DOMMessageFormatter.formatMessage(
                                      DOMMessageFormatter.DOM_DOMAIN,
                                      "InvalidDocumentClassName", new Object[]{documentClassName}));
                  }
              } catch (ClassNotFoundException e) {
                  throw new IllegalArgumentException(
                          DOMMessageFormatter.formatMessage(
                                  DOMMessageFormatter.DOM_DOMAIN,
                                  "MissingDocumentClassName", new Object[]{documentClassName}));
              }
          }
  
          // set document class name
          fDocumentClassName = documentClassName;
          if (!documentClassName.equals(DEFAULT_DOCUMENT_CLASS_NAME)) {
              fDeferNodeExpansion = false;
          }
  
      } // setDocumentClassName(String)
  
      /**
       * Returns the DOM document object.
       */
      public Document getDocument() {
          return fDocument;
      } // getDocument():Document
  
      //
      // Public methods
      //
  
      /**
       * Drops all references to the last DOM which was built by this parser.
       */
      public final void dropDocumentReferences() {
          fDocument = null;
          fDocumentImpl = null;
          fDeferredDocumentImpl = null;
          fDocumentType = null;
          fCurrentNode = null;
          fCurrentCDATASection = null;
          fCurrentEntityDecl = null;
          fRoot = null;
      } // dropDocumentReferences()
  
      /**
       * Resets the parser state.
       *
       * @throws SAXException Thrown on initialization error.
       */
      @Override
      public void reset() throws XNIException {
          super.reset();
  
  
          // get feature state
          fCreateEntityRefNodes =
                  fConfiguration.getFeature(CREATE_ENTITY_REF_NODES);
  
          fIncludeIgnorableWhitespace =
                  fConfiguration.getFeature(INCLUDE_IGNORABLE_WHITESPACE);
  
          fDeferNodeExpansion =
                  fConfiguration.getFeature(DEFER_NODE_EXPANSION);
  
          fNamespaceAware = fConfiguration.getFeature(NAMESPACES);
  
          fIncludeComments = fConfiguration.getFeature(INCLUDE_COMMENTS_FEATURE);
  
          fCreateCDATANodes = fConfiguration.getFeature(CREATE_CDATA_NODES_FEATURE);
  
          // get property
          setDocumentClassName((String)
                  fConfiguration.getProperty(DOCUMENT_CLASS_NAME));
  
          // reset dom information
          fDocument = null;
          fDocumentImpl = null;
          fStorePSVI = false;
          fDocumentType = null;
          fDocumentTypeIndex = -1;
          fDeferredDocumentImpl = null;
          fCurrentNode = null;
  
          // reset string buffer
          fStringBuffer.setLength(0);
  
          // reset state information
          fRoot = null;
          fInDTD = false;
          fInDTDExternalSubset = false;
          fInCDATASection = false;
          fFirstChunk = false;
          fCurrentCDATASection = null;
          fCurrentCDATASectionIndex = -1;
  
          fBaseURIStack.removeAllElements();
  
  
      } // reset()
  
      //
      // XMLDocumentParser methods
      //
  
      /**
       * Set the locale to use for messages.
       *
       * @param locale The locale object to use for localization of messages.
       */
      public void setLocale(Locale locale) {
          fConfiguration.setLocale(locale);
  
      } // setLocale(Locale)
  
      /**
       * This method notifies the start of a general entity.
       * <p/>
       * <strong>Note:</strong> This method is not called for entity references
       * appearing as part of attribute values.
       *
       * @param name       The name of the general 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).
       * @param augs       Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void startGeneralEntity(String name,
                                     XMLResourceIdentifier identifier,
                                     String encoding, Augmentations augs)
              throws XNIException {
          if (DEBUG_EVENTS) {
              System.out.println("==>startGeneralEntity (" + name + ")");
              if (DEBUG_BASEURI) {
                  System.out.println("   expandedSystemId( **baseURI): " + identifier.getExpandedSystemId());
                  System.out.println("   baseURI:" + identifier.getBaseSystemId());
              }
          }
  
          // Always create entity reference nodes to be able to recreate
          // entity as a part of doctype
          if (!fDeferNodeExpansion) {
              if (fFilterReject) {
                  return;
              }
              setCharacterData(true);
              EntityReference er = fDocument.createEntityReference(name);
              if (fDocumentImpl != null) {
                  // REVISIT: baseURI/actualEncoding
                  //         remove dependency on our implementation when DOM L3 is REC
                  //
  
                  EntityReferenceImpl erImpl = (EntityReferenceImpl) er;
  
                  // set base uri
                  erImpl.setBaseURI(identifier.getExpandedSystemId());
                  if (fDocumentType != null) {
                      // set actual encoding
                      NamedNodeMap entities = fDocumentType.getEntities();
                      fCurrentEntityDecl = (EntityImpl) entities.getNamedItem(name);
                      if (fCurrentEntityDecl != null) {
                          fCurrentEntityDecl.setInputEncoding(encoding);
                      }
  
                  }
                  // we don't need synchronization now, because entity ref will be
                  // expanded anyway. Synch only needed when user creates entityRef node
                  erImpl.needsSyncChildren(false);
              }
              fInEntityRef = true;
              fCurrentNode.appendChild(er);
              fCurrentNode = er;
          } else {
  
              int er =
                      fDeferredDocumentImpl.createDeferredEntityReference(name, identifier.getExpandedSystemId());
              if (fDocumentTypeIndex != -1) {
                  // find corresponding Entity decl
                  int node = fDeferredDocumentImpl.getLastChild(fDocumentTypeIndex, false);
                  while (node != -1) {
                      short nodeType = fDeferredDocumentImpl.getNodeType(node, false);
                      if (nodeType == Node.ENTITY_NODE) {
                          String nodeName =
                                  fDeferredDocumentImpl.getNodeName(node, false);
                          if (nodeName.equals(name)) {
                              fDeferredEntityDecl = node;
                              fDeferredDocumentImpl.setInputEncoding(node, encoding);
                              break;
                          }
                      }
                      node = fDeferredDocumentImpl.getRealPrevSibling(node, false);
                  }
              }
              fDeferredDocumentImpl.appendChild(fCurrentNodeIndex, er);
              fCurrentNodeIndex = er;
          }
  
      } // startGeneralEntity(String,XMLResourceIdentifier, Augmentations)
  
      //
      // XMLDocumentHandler methods
      //
  
      /**
       * Notifies of the presence of a TextDecl line in an entity. If present,
       * this method will be called immediately following the startEntity call.
       * <p/>
       * <strong>Note:</strong> This method will never be called for the
       * document entity; it is only called for external general entities
       * referenced in document content.
       * <p/>
       * <strong>Note:</strong> This method is not called for entity references
       * appearing as part of attribute values.
       *
       * @param version  The XML version, or null if not specified.
       * @param encoding The IANA encoding name of the entity.
       * @param augs     Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void textDecl(String version, String encoding, Augmentations augs) throws XNIException {
          if (fInDTD) {
              return;
          }
          if (!fDeferNodeExpansion) {
              if (fCurrentEntityDecl != null && !fFilterReject) {
                  fCurrentEntityDecl.setXmlEncoding(encoding);
                  if (version != null)
                      fCurrentEntityDecl.setXmlVersion(version);
              }
          } else {
              if (fDeferredEntityDecl != -1) {
                  fDeferredDocumentImpl.setEntityInfo(fDeferredEntityDecl, version, encoding);
              }
          }
      } // textDecl(String,String)
  
      /**
       * A comment.
       *
       * @param text The text in the comment.
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by application to signal an error.
       */
      @Override
      public void comment(XMLString text, Augmentations augs) throws XNIException {
          if (fInDTD) {
              if (fInternalSubset != null && !fInDTDExternalSubset) {
                  fInternalSubset.append("<!--");
                  if (text.length > 0) {
                      fInternalSubset.append(text.ch, text.offset, text.length);
                  }
                  fInternalSubset.append("-->");
              }
              return;
          }
          if (!fIncludeComments || fFilterReject) {
              return;
          }
          if (!fDeferNodeExpansion) {
              Comment comment = fDocument.createComment(text.toString());
  
              setCharacterData(false);
              fCurrentNode.appendChild(comment);
              if (fDOMFilter != null && !fInEntityRef &&
                      (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_COMMENT) != 0) {
                  short code = fDOMFilter.acceptNode(comment);
                  switch (code) {
                      case LSParserFilter.FILTER_INTERRUPT: {
                          throw Abort.INSTANCE;
                      }
                      case LSParserFilter.FILTER_REJECT: {
                          // REVISIT: the constant FILTER_REJECT should be changed when new
                          // DOM LS specs gets published
  
                          // fall through to SKIP since comment has no children.
                      }
                      case LSParserFilter.FILTER_SKIP: {
                          // REVISIT: the constant FILTER_SKIP should be changed when new
                          // DOM LS specs gets published
                          fCurrentNode.removeChild(comment);
                          // make sure we don't loose chars if next event is characters()
                          fFirstChunk = true;
                          return;
                      }
  
                      default: {
                          // accept node
                      }
                  }
              }
  
          } else {
              int comment =
                      fDeferredDocumentImpl.createDeferredComment(text.toString());
              fDeferredDocumentImpl.appendChild(fCurrentNodeIndex, comment);
          }
  
      } // comment(XMLString)
  
      /**
       * A processing instruction. Processing instructions consist of a
       * target name and, optionally, text data. The data is only meaningful
       * to the application.
       * <p/>
       * Typically, a processing instruction's data will contain a series
       * of pseudo-attributes. These pseudo-attributes follow the form of
       * element attributes but are <strong>not</strong> parsed or presented
       * to the application as anything other than text. The application is
       * responsible for parsing the data.
       *
       * @param target The target.
       * @param data   The data or null if none specified.
       * @param augs   Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void processingInstruction(String target, XMLString data, Augmentations augs)
              throws XNIException {
  
          if (fInDTD) {
              if (fInternalSubset != null && !fInDTDExternalSubset) {
                  fInternalSubset.append("<?");
                  fInternalSubset.append(target);
                  if (data.length > 0) {
                      fInternalSubset.append(' ').append(data.ch, data.offset, data.length);
                  }
                  fInternalSubset.append("?>");
              }
              return;
          }
  
          if (DEBUG_EVENTS) {
              System.out.println("==>processingInstruction (" + target + ")");
          }
          if (!fDeferNodeExpansion) {
              if (fFilterReject) {
                  return;
              }
              ProcessingInstruction pi =
                      fDocument.createProcessingInstruction(target, data.toString());
  
  
              setCharacterData(false);
              fCurrentNode.appendChild(pi);
              if (fDOMFilter != null && !fInEntityRef &&
                      (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_PROCESSING_INSTRUCTION) != 0) {
                  short code = fDOMFilter.acceptNode(pi);
                  switch (code) {
                      case LSParserFilter.FILTER_INTERRUPT: {
                          throw Abort.INSTANCE;
                      }
                      case LSParserFilter.FILTER_REJECT: {
                          // fall through to SKIP since PI has no children.
                      }
                      case LSParserFilter.FILTER_SKIP: {
                          fCurrentNode.removeChild(pi);
                          // fFirstChunk must be set to true so that data
                          // won't be lost in the case where the child before PI is
                          // a text node and the next event is characters.
                          fFirstChunk = true;
                          return;
                      }
                      default: {
                      }
                  }
              }
          } else {
              int pi = fDeferredDocumentImpl.
                      createDeferredProcessingInstruction(target, data.toString());
              fDeferredDocumentImpl.appendChild(fCurrentNodeIndex, pi);
          }
  
      } // processingInstruction(String,XMLString)
  
      /**
       * The start of the document.
       *
       * @param locator          The system identifier of the entity if the entity
       *                         is external, null otherwise.
       * @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).
       * @param namespaceContext The namespace context in effect at the
       *                         start of this document.
       *                         This object represents the current context.
       *                         Implementors of this class are responsible
       *                         for copying the namespace bindings from the
       *                         the current context (and its parent contexts)
       *                         if that information is important.
       * @param augs             Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void startDocument(XMLLocator locator, String encoding,
                                NamespaceContext namespaceContext, Augmentations augs)
              throws XNIException {
  
          fLocator = locator;
          if (!fDeferNodeExpansion) {
              if (fDocumentClassName.equals(DEFAULT_DOCUMENT_CLASS_NAME)) {
                  fDocument = new DocumentImpl();
                  fDocumentImpl = (CoreDocumentImpl) fDocument;
                  // REVISIT: when DOM Level 3 is REC rely on Document.support
                  //          instead of specific class
                  // set DOM error checking off
                  fDocumentImpl.setStrictErrorChecking(false);
                  // set actual encoding
                  fDocumentImpl.setInputEncoding(encoding);
                  // set documentURI
                  fDocumentImpl.setDocumentURI(locator.getExpandedSystemId());
              } else if (fDocumentClassName.equals(PSVI_DOCUMENT_CLASS_NAME)) {
                  fDocument = new PSVIDocumentImpl();
                  fDocumentImpl = (CoreDocumentImpl) fDocument;
                  fStorePSVI = true;
                  // REVISIT: when DOM Level 3 is REC rely on Document.support
                  //          instead of specific class
                  // set DOM error checking off
                  fDocumentImpl.setStrictErrorChecking(false);
                  // set actual encoding
                  fDocumentImpl.setInputEncoding(encoding);
                  // set documentURI
                  fDocumentImpl.setDocumentURI(locator.getExpandedSystemId());
              } else {
                  // use specified document class
                  try {
                      ClassLoader cl = ObjectFactory.findClassLoader();
                      Class documentClass = ObjectFactory.findProviderClass(fDocumentClassName,
                              cl, true);
                      fDocument = (Document) documentClass.newInstance();
  
                      // if subclass of our own class that's cool too
                      Class defaultDocClass =
                              ObjectFactory.findProviderClass(CORE_DOCUMENT_CLASS_NAME,
                                      cl, true);
                      if (defaultDocClass.isAssignableFrom(documentClass)) {
                          fDocumentImpl = (CoreDocumentImpl) fDocument;
  
                          Class psviDocClass = ObjectFactory.findProviderClass(PSVI_DOCUMENT_CLASS_NAME,
                                  cl, true);
                          if (psviDocClass.isAssignableFrom(documentClass)) {
                              fStorePSVI = true;
                          }
  
                          // REVISIT: when DOM Level 3 is REC rely on
                          //          Document.support instead of specific class
                          // set DOM error checking off
                          fDocumentImpl.setStrictErrorChecking(false);
                          // set actual encoding
                          fDocumentImpl.setInputEncoding(encoding);
                          // set documentURI
                          if (locator != null) {
                              fDocumentImpl.setDocumentURI(locator.getExpandedSystemId());
                          }
                      }
                  } catch (ClassNotFoundException e) {
                      // won't happen we already checked that earlier
                  } catch (Exception e) {
                      throw new RuntimeException(
                              DOMMessageFormatter.formatMessage(
                                      DOMMessageFormatter.DOM_DOMAIN,
                                      "CannotCreateDocumentClass",
                                      new Object[]{fDocumentClassName}));
                  }
              }
              fCurrentNode = fDocument;
          } else {
              fDeferredDocumentImpl = new DeferredDocumentImpl(fNamespaceAware);
              fDocument = fDeferredDocumentImpl;
              fDocumentIndex = fDeferredDocumentImpl.createDeferredDocument();
              // REVISIT: strict error checking is not implemented in deferred dom.
              //          Document.support instead of specific class
  
              // set actual encoding
              fDeferredDocumentImpl.setInputEncoding(encoding);
              // set documentURI
              fDeferredDocumentImpl.setDocumentURI(locator.getExpandedSystemId());
              fCurrentNodeIndex = fDocumentIndex;
  
          }
  
      } // startDocument(String,String)
  
      /**
       * Notifies of the presence of an XMLDecl line in the document. If
       * present, this method will be called immediately following the
       * startDocument call.
       *
       * @param version    The XML version.
       * @param encoding   The IANA encoding name of the document, or null if
       *                   not specified.
       * @param standalone The standalone value, or null if not specified.
       * @param augs       Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void xmlDecl(String version, String encoding, String standalone,
                          Augmentations augs)
              throws XNIException {
          if (!fDeferNodeExpansion) {
              // REVISIT: when DOM Level 3 is REC rely on Document.support
              //          instead of specific class
              if (fDocumentImpl != null) {
                  if (version != null)
                      fDocumentImpl.setXmlVersion(version);
                  fDocumentImpl.setXmlEncoding(encoding);
                  fDocumentImpl.setXmlStandalone("yes".equals(standalone));
              }
          } else {
              if (version != null)
                  fDeferredDocumentImpl.setXmlVersion(version);
              fDeferredDocumentImpl.setXmlEncoding(encoding);
              fDeferredDocumentImpl.setXmlStandalone("yes".equals(standalone));
          }
      } // xmlDecl(String,String,String)
  
      /**
       * Notifies of the presence of the DOCTYPE line in the document.
       *
       * @param rootElement The name of the root element.
       * @param publicId    The public identifier if an external DTD or null
       *                    if the external DTD is specified using SYSTEM.
       * @param systemId    The system identifier if an external DTD, null
       *                    otherwise.
       * @param augs        Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void doctypeDecl(String rootElement,
                              String publicId, String systemId, Augmentations augs)
              throws XNIException {
  
          if (!fDeferNodeExpansion) {
              if (fDocumentImpl != null) {
                  fDocumentType = fDocumentImpl.createDocumentType(
                          rootElement, publicId, systemId);
                  fCurrentNode.appendChild(fDocumentType);
              }
          } else {
              fDocumentTypeIndex = fDeferredDocumentImpl.
                      createDeferredDocumentType(rootElement, publicId, systemId);
              fDeferredDocumentImpl.appendChild(fCurrentNodeIndex, fDocumentTypeIndex);
          }
  
      } // doctypeDecl(String,String,String)
  
      /**
       * The start of an element. If the document specifies the start element
       * by using an empty tag, then the startElement method will immediately
       * be followed by the endElement method, with no intervening methods.
       *
       * @param element    The name of the element.
       * @param attributes The element attributes.
       * @param augs       Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void startElement(QName element, XMLAttributes attributes, Augmentations augs)
              throws XNIException {
          if (DEBUG_EVENTS) {
              System.out.println("==>startElement (" + element.rawname + ")");
          }
          if (!fDeferNodeExpansion) {
              if (fFilterReject) {
                  ++fRejectedElementDepth;
                  return;
              }
              Element el = createElementNode(element);
              int attrCount = attributes.getLength();
              boolean seenSchemaDefault = false;
              for (int i = 0; i < attrCount; i++) {
                  attributes.getName(i, fAttrQName);
                  Attr attr = createAttrNode(fAttrQName);
  
                  String attrValue = attributes.getValue(i);
  
                  AttributePSVI attrPSVI = (AttributePSVI) attributes.getAugmentations(i).getItem(Constants.ATTRIBUTE_PSVI);
                  if (fStorePSVI && attrPSVI != null) {
                      ((PSVIAttrNSImpl) attr).setPSVI(attrPSVI);
                  }
  
                  attr.setValue(attrValue);
                  boolean specified = attributes.isSpecified(i);
                  // Take special care of schema defaulted attributes. Calling the
                  // non-namespace aware setAttributeNode() method could overwrite
                  // another attribute with the same local name.
                  if (!specified && (seenSchemaDefault || (fAttrQName.uri != null &&
                          fAttrQName.uri != NamespaceContext.XMLNS_URI && fAttrQName.prefix == null))) {
                      el.setAttributeNodeNS(attr);
                      seenSchemaDefault = true;
                  } else {
                      el.setAttributeNode(attr);
                  }
                  // NOTE: The specified value MUST be set after you set
                  //       the node value because that turns the "specified"
                  //       flag to "true" which may overwrite a "false"
                  //       value from the attribute list. -Ac
                  if (fDocumentImpl != null) {
                      AttrImpl attrImpl = (AttrImpl) attr;
                      Object type = null;
                      boolean id = false;
  
                      // REVISIT: currently it is possible that someone turns off
                      // namespaces and turns on xml schema validation
                      // To avoid classcast exception in AttrImpl check for namespaces
                      // however the correct solution should probably disallow setting
                      // namespaces to false when schema processing is turned on.
                      if (attrPSVI != null && fNamespaceAware) {
                          // XML Schema
                          type = attrPSVI.getMemberTypeDefinition();
                          if (type == null) {
                              type = attrPSVI.getTypeDefinition();
                              if (type != null) {
                                  id = ((XSSimpleType) type).isIDType();
                                  attrImpl.setType(type);
                              }
                          } else {
                              id = ((XSSimpleType) type).isIDType();
                              attrImpl.setType(type);
                          }
                      } else {
                          // DTD
                          boolean isDeclared = Boolean.TRUE.equals(attributes.getAugmentations(i).getItem(Constants.ATTRIBUTE_DECLARED));
                          // For DOM Level 3 TypeInfo, the type name must
                          // be null if this attribute has not been declared
                          // in the DTD.
                          if (isDeclared) {
                              type = attributes.getType(i);
                              id = "ID".equals(type);
                          }
                          attrImpl.setType(type);
                      }
  
                      if (id) {
                          ((ElementImpl) el).setIdAttributeNode(attr, true);
                      }
  
                      attrImpl.setSpecified(specified);
                      // REVISIT: Handle entities in attribute value.
                  }
              }
              setCharacterData(false);
  
              if (augs != null) {
                  ElementPSVI elementPSVI = (ElementPSVI) augs.getItem(Constants.ELEMENT_PSVI);
                  if (elementPSVI != null && fNamespaceAware) {
                      XSTypeDefinition type = elementPSVI.getMemberTypeDefinition();
                      if (type == null) {
                          type = elementPSVI.getTypeDefinition();
                      }
                      ((ElementNSImpl) el).setType(type);
                  }
              }
  
  
              // filter nodes
              if (fDOMFilter != null && !fInEntityRef) {
                  if (fRoot == null) {
                      // fill value of the root element
                      fRoot = el;
                  } else {
                      short code = fDOMFilter.startElement(el);
                      switch (code) {
                          case LSParserFilter.FILTER_INTERRUPT: {
                              throw Abort.INSTANCE;
                          }
                          case LSParserFilter.FILTER_REJECT: {
                              fFilterReject = true;
                              fRejectedElementDepth = 0;
                              return;
                          }
                          case LSParserFilter.FILTER_SKIP: {
                              // make sure that if any char data is available
                              // the fFirstChunk is true, so that if the next event
                              // is characters(), and the last node is text, we will copy
                              // the value already in the text node to fStringBuffer
                              // (not to lose it).
                              fFirstChunk = true;
                              fSkippedElemStack.push(Boolean.TRUE);
                              return;
                          }
                          default: {
                              if (!fSkippedElemStack.isEmpty()) {
                                  fSkippedElemStack.push(Boolean.FALSE);
                              }
                          }
                      }
                  }
              }
              fCurrentNode.appendChild(el);
              fCurrentNode = el;
          } else {
              int el = fDeferredDocumentImpl.createDeferredElement(fNamespaceAware ?
                      element.uri : null, element.rawname);
              Object type = null;
              int attrCount = attributes.getLength();
              // Need to loop in reverse order so that the attributes
              // are processed in document order when the DOM is expanded.
              for (int i = attrCount - 1; i >= 0; --i) {
  
                  // set type information
                  AttributePSVI attrPSVI = (AttributePSVI) attributes.getAugmentations(i).getItem(Constants.ATTRIBUTE_PSVI);
                  boolean id = false;
  
                  // REVISIT: currently it is possible that someone turns off
                  // namespaces and turns on xml schema validation
                  // To avoid classcast exception in AttrImpl check for namespaces
                  // however the correct solution should probably disallow setting
                  // namespaces to false when schema processing is turned on.
                  if (attrPSVI != null && fNamespaceAware) {
                      // XML Schema
                      type = attrPSVI.getMemberTypeDefinition();
                      if (type == null) {
                          type = attrPSVI.getTypeDefinition();
                          if (type != null) {
                              id = ((XSSimpleType) type).isIDType();
                          }
                      } else {
                          id = ((XSSimpleType) type).isIDType();
                      }
                  } else {
                      // DTD
                      boolean isDeclared = Boolean.TRUE.equals(attributes.getAugmentations(i).getItem(Constants.ATTRIBUTE_DECLARED));
                      // For DOM Level 3 TypeInfo, the type name must
                      // be null if this attribute has not been declared
                      // in the DTD.
                      if (isDeclared) {
                          type = attributes.getType(i);
                          id = "ID".equals(type);
                      }
                  }
  
                  // create attribute
                  fDeferredDocumentImpl.setDeferredAttribute(
                          el,
                          attributes.getQName(i),
                          attributes.getURI(i),
                          attributes.getValue(i),
                          attributes.isSpecified(i),
                          id,
                          type);
              }
  
              fDeferredDocumentImpl.appendChild(fCurrentNodeIndex, el);
              fCurrentNodeIndex = el;
          }
      } // startElement(QName,XMLAttributes)
  
      /**
       * An empty element.
       *
       * @param element    The name of the element.
       * @param attributes The element attributes.
       * @param augs       Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs)
              throws XNIException {
  
          startElement(element, attributes, augs);
          endElement(element, augs);
  
      } // emptyElement(QName,XMLAttributes)
  
      /**
       * Character content.
       *
       * @param text The content.
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void characters(XMLString text, Augmentations augs) throws XNIException {
  
          if (DEBUG_EVENTS) {
              System.out.println("==>characters(): " + text.toString());
          }
  
          if (!fDeferNodeExpansion) {
  
              if (fFilterReject) {
                  return;
              }
              if (fInCDATASection && fCreateCDATANodes) {
                  if (fCurrentCDATASection == null) {
                      fCurrentCDATASection =
                              fDocument.createCDATASection(text.toString());
                      fCurrentNode.appendChild(fCurrentCDATASection);
                      fCurrentNode = fCurrentCDATASection;
                  } else {
                      fCurrentCDATASection.appendData(text.toString());
                  }
              } else if (!fInDTD) {
                  // if type is union (XML Schema) it is possible that we receive
                  // character call with empty data
                  if (text.length == 0) {
                      return;
                  }
  
                  Node child = fCurrentNode.getLastChild();
                  if (child != null && child.getNodeType() == Node.TEXT_NODE) {
                      // collect all the data into the string buffer.
                      if (fFirstChunk) {
                          if (fDocumentImpl != null) {
                              fStringBuffer.append(((TextImpl) child).removeData());
                          } else {
                              fStringBuffer.append(((Text) child).getData());
                              child.setNodeValue(null);
                          }
                          fFirstChunk = false;
                      }
                      if (text.length > 0) {
                          fStringBuffer.append(text.ch, text.offset, text.length);
                      }
                  } else {
                      fFirstChunk = true;
                      Text textNode = fDocument.createTextNode(text.toString());
                      fCurrentNode.appendChild(textNode);
                  }
  
              }
          } else {
              // The Text and CDATASection normalization is taken care of within
              // the DOM in the deferred case.
              if (fInCDATASection && fCreateCDATANodes) {
                  if (fCurrentCDATASectionIndex == -1) {
                      int cs = fDeferredDocumentImpl.
                              createDeferredCDATASection(text.toString());
  
                      fDeferredDocumentImpl.appendChild(fCurrentNodeIndex, cs);
                      fCurrentCDATASectionIndex = cs;
                      fCurrentNodeIndex = cs;
                  } else {
                      int txt = fDeferredDocumentImpl.
                              createDeferredTextNode(text.toString(), false);
                      fDeferredDocumentImpl.appendChild(fCurrentNodeIndex, txt);
                  }
              } else if (!fInDTD) {
                  // if type is union (XML Schema) it is possible that we receive
                  // character call with empty data
                  if (text.length == 0) {
                      return;
                  }
  
                  String value = text.toString();
                  int txt = fDeferredDocumentImpl.
                          createDeferredTextNode(value, false);
                  fDeferredDocumentImpl.appendChild(fCurrentNodeIndex, txt);
  
              }
          }
      } // characters(XMLString)
  
      /**
       * Ignorable whitespace. For this method to be called, the document
       * source must have some way of determining that the text containing
       * only whitespace characters should be considered ignorable. For
       * example, the validator can determine if a length of whitespace
       * characters in the document are ignorable based on the element
       * content model.
       *
       * @param text The ignorable whitespace.
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException {
  
          if (!fIncludeIgnorableWhitespace || fFilterReject) {
              return;
          }
          if (!fDeferNodeExpansion) {
              Node child = fCurrentNode.getLastChild();
              if (child != null && child.getNodeType() == Node.TEXT_NODE) {
                  Text textNode = (Text) child;
                  textNode.appendData(text.toString());
              } else {
                  Text textNode = fDocument.createTextNode(text.toString());
                  if (fDocumentImpl != null) {
                      TextImpl textNodeImpl = (TextImpl) textNode;
                      textNodeImpl.setIgnorableWhitespace(true);
                  }
                  fCurrentNode.appendChild(textNode);
              }
          } else {
              // The Text normalization is taken care of within the DOM in the
              // deferred case.
              int txt = fDeferredDocumentImpl.
                      createDeferredTextNode(text.toString(), true);
              fDeferredDocumentImpl.appendChild(fCurrentNodeIndex, txt);
          }
  
      } // ignorableWhitespace(XMLString)
  
      /**
       * The end of an element.
       *
       * @param element The name of the element.
       * @param augs    Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endElement(QName element, Augmentations augs) throws XNIException {
          if (DEBUG_EVENTS) {
              System.out.println("==>endElement (" + element.rawname + ")");
          }
          if (!fDeferNodeExpansion) {
  
              // REVISIT: Should this happen after we call the filter?
              if (augs != null && fDocumentImpl != null && (fNamespaceAware || fStorePSVI)) {
                  ElementPSVI elementPSVI = (ElementPSVI) augs.getItem(Constants.ELEMENT_PSVI);
                  if (elementPSVI != null) {
                      // Updating TypeInfo. If the declared type is a union the
                      // [member type definition] will only be available at the
                      // end of an element.
                      if (fNamespaceAware) {
                          XSTypeDefinition type = elementPSVI.getMemberTypeDefinition();
                          if (type == null) {
                              type = elementPSVI.getTypeDefinition();
                          }
                          ((ElementNSImpl) fCurrentNode).setType(type);
                      }
                      if (fStorePSVI) {
                          ((PSVIElementNSImpl) fCurrentNode).setPSVI(elementPSVI);
                      }
                  }
              }
  
              if (fDOMFilter != null) {
                  if (fFilterReject) {
                      if (fRejectedElementDepth-- == 0) {
                          fFilterReject = false;
                      }
                      return;
                  }
                  if (!fSkippedElemStack.isEmpty()) {
                      if (fSkippedElemStack.pop() == Boolean.TRUE) {
                          return;
                      }
                  }
                  setCharacterData(false);
                  if ((fCurrentNode != fRoot) && !fInEntityRef && (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_ELEMENT) != 0) {
                      short code = fDOMFilter.acceptNode(fCurrentNode);
                      switch (code) {
                          case LSParserFilter.FILTER_INTERRUPT: {
                              throw Abort.INSTANCE;
                          }
                          case LSParserFilter.FILTER_REJECT: {
                              Node parent = fCurrentNode.getParentNode();
                              parent.removeChild(fCurrentNode);
                              fCurrentNode = parent;
                              return;
                          }
                          case LSParserFilter.FILTER_SKIP: {
                              // make sure that if any char data is available
                              // the fFirstChunk is true, so that if the next event
                              // is characters(), and the last node is text, we will copy
                              // the value already in the text node to fStringBuffer
                              // (not to lose it).
                              fFirstChunk = true;
  
                              // replace children
                              Node parent = fCurrentNode.getParentNode();
                              NodeList ls = fCurrentNode.getChildNodes();
                              int length = ls.getLength();
  
                              for (int i = 0; i < length; i++) {
                                  parent.appendChild(ls.item(0));
                              }
                              parent.removeChild(fCurrentNode);
                              fCurrentNode = parent;
  
                              return;
                          }
  
                          default: {
                          }
                      }
                  }
                  fCurrentNode = fCurrentNode.getParentNode();
  
              } // end-if DOMFilter
              else {
                  setCharacterData(false);
                  fCurrentNode = fCurrentNode.getParentNode();
              }
  
          } else {
              if (augs != null) {
                  ElementPSVI elementPSVI = (ElementPSVI) augs.getItem(Constants.ELEMENT_PSVI);
                  if (elementPSVI != null) {
                      // Setting TypeInfo. If the declared type is a union the
                      // [member type definition] will only be available at the
                      // end of an element.
                      XSTypeDefinition type = elementPSVI.getMemberTypeDefinition();
                      if (type == null) {
                          type = elementPSVI.getTypeDefinition();
                      }
                      fDeferredDocumentImpl.setTypeInfo(fCurrentNodeIndex, type);
                  }
              }
              fCurrentNodeIndex =
                      fDeferredDocumentImpl.getParentNode(fCurrentNodeIndex, false);
          }
  
  
      } // endElement(QName)
  
      /**
       * The start of a CDATA section.
       *
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void startCDATA(Augmentations augs) throws XNIException {
  
          fInCDATASection = true;
          if (!fDeferNodeExpansion) {
              if (fFilterReject) {
                  return;
              }
              if (fCreateCDATANodes) {
                  setCharacterData(false);
              }
          }
      } // startCDATA()
  
      /**
       * The end of a CDATA section.
       *
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endCDATA(Augmentations augs) throws XNIException {
  
          fInCDATASection = false;
          if (!fDeferNodeExpansion) {
  
              if (fFilterReject) {
                  return;
              }
  
              if (fCurrentCDATASection != null) {
  
                  if (fDOMFilter != null && !fInEntityRef &&
                          (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_CDATA_SECTION) != 0) {
                      short code = fDOMFilter.acceptNode(fCurrentCDATASection);
                      switch (code) {
                          case LSParserFilter.FILTER_INTERRUPT: {
                              throw Abort.INSTANCE;
                          }
                          case LSParserFilter.FILTER_REJECT: {
                              // fall through to SKIP since CDATA section has no children.
                          }
                          case LSParserFilter.FILTER_SKIP: {
                              Node parent = fCurrentNode.getParentNode();
                              parent.removeChild(fCurrentCDATASection);
                              fCurrentNode = parent;
                              return;
                          }
  
                          default: {
                              // accept node
                          }
                      }
                  }
  
                  fCurrentNode = fCurrentNode.getParentNode();
                  fCurrentCDATASection = null;
              }
          } else {
              if (fCurrentCDATASectionIndex != -1) {
                  fCurrentNodeIndex =
                          fDeferredDocumentImpl.getParentNode(fCurrentNodeIndex, false);
                  fCurrentCDATASectionIndex = -1;
              }
          }
  
      } // endCDATA()
  
      /**
       * The end of the document.
       *
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endDocument(Augmentations augs) throws XNIException {
  
          if (!fDeferNodeExpansion) {
              // REVISIT: when DOM Level 3 is REC rely on Document.support
              //          instead of specific class
              // set the actual encoding and set DOM error checking back on
              if (fDocumentImpl != null) {
                  if (fLocator != null) {
                      fDocumentImpl.setInputEncoding(fLocator.getEncoding());
                  }
                  fDocumentImpl.setStrictErrorChecking(true);
              }
              fCurrentNode = null;
          } else {
              // set the actual encoding
              if (fLocator != null) {
                  fDeferredDocumentImpl.setInputEncoding(fLocator.getEncoding());
              }
              fCurrentNodeIndex = -1;
          }
  
      } // endDocument()
  
      /**
       * This method notifies the end of a general entity.
       * <p/>
       * <strong>Note:</strong> This method is not called for entity references
       * appearing as part of attribute values.
       *
       * @param name The name of the entity.
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endGeneralEntity(String name, Augmentations augs) throws XNIException {
          if (DEBUG_EVENTS) {
              System.out.println("==>endGeneralEntity: (" + name + ")");
          }
          if (!fDeferNodeExpansion) {
  
              if (fFilterReject) {
                  return;
              }
              setCharacterData(true);
  
              if (fDocumentType != null) {
                  // get current entity declaration
                  NamedNodeMap entities = fDocumentType.getEntities();
                  fCurrentEntityDecl = (EntityImpl) entities.getNamedItem(name);
                  if (fCurrentEntityDecl != null) {
                      if (fCurrentEntityDecl != null && fCurrentEntityDecl.getFirstChild() == null) {
                          fCurrentEntityDecl.setReadOnly(false, true);
                          Node child = fCurrentNode.getFirstChild();
                          while (child != null) {
                              Node copy = child.cloneNode(true);
                              fCurrentEntityDecl.appendChild(copy);
                              child = child.getNextSibling();
                          }
                          fCurrentEntityDecl.setReadOnly(true, true);
  
                          //entities.setNamedItem(fCurrentEntityDecl);
                      }
                      fCurrentEntityDecl = null;
                  }
  
              }
              fInEntityRef = false;
              boolean removeEntityRef = false;
              if (fCreateEntityRefNodes) {
                  if (fDocumentImpl != null) {
                      // Make entity ref node read only
                      ((NodeImpl) fCurrentNode).setReadOnly(true, true);
                  }
  
                  if (fDOMFilter != null &&
                          (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_ENTITY_REFERENCE) != 0) {
                      short code = fDOMFilter.acceptNode(fCurrentNode);
                      switch (code) {
                          case LSParserFilter.FILTER_INTERRUPT: {
                              throw Abort.INSTANCE;
                          }
                          case LSParserFilter.FILTER_REJECT: {
                              Node parent = fCurrentNode.getParentNode();
                              parent.removeChild(fCurrentNode);
                              fCurrentNode = parent;
                              return;
  
                          }
                          case LSParserFilter.FILTER_SKIP: {
                              // make sure we don't loose chars if next event is characters()
                              fFirstChunk = true;
                              removeEntityRef = true;
                              break;
                          }
  
                          default: {
                              fCurrentNode = fCurrentNode.getParentNode();
                          }
                      }
                  } else {
                      fCurrentNode = fCurrentNode.getParentNode();
                  }
              }
  
              if (!fCreateEntityRefNodes || removeEntityRef) {
                  // move entity reference children to the list of
                  // siblings of its parent and remove entity reference
                  NodeList children = fCurrentNode.getChildNodes();
                  Node parent = fCurrentNode.getParentNode();
                  int length = children.getLength();
                  if (length > 0) {
  
                      // get previous sibling of the entity reference
                      Node node = fCurrentNode.getPreviousSibling();
                      // normalize text nodes
                      Node child = children.item(0);
                      if (node != null && node.getNodeType() == Node.TEXT_NODE &&
                              child.getNodeType() == Node.TEXT_NODE) {
                          ((Text) node).appendData(child.getNodeValue());
                          fCurrentNode.removeChild(child);
  
                      } else {
                          node = parent.insertBefore(child, fCurrentNode);
                          handleBaseURI(node);
                      }
  
                      for (int i = 1; i < length; i++) {
                          node = parent.insertBefore(children.item(0), fCurrentNode);
                          handleBaseURI(node);
                      }
                  } // length > 0
                  parent.removeChild(fCurrentNode);
                  fCurrentNode = parent;
              }
          } else {
  
              if (fDocumentTypeIndex != -1) {
                  // find corresponding Entity decl
                  int node = fDeferredDocumentImpl.getLastChild(fDocumentTypeIndex, false);
                  while (node != -1) {
                      short nodeType = fDeferredDocumentImpl.getNodeType(node, false);
                      if (nodeType == Node.ENTITY_NODE) {
                          String nodeName =
                                  fDeferredDocumentImpl.getNodeName(node, false);
                          if (nodeName.equals(name)) {
                              fDeferredEntityDecl = node;
                              break;
                          }
                      }
                      node = fDeferredDocumentImpl.getRealPrevSibling(node, false);
                  }
              }
  
              if (fDeferredEntityDecl != -1 &&
                      fDeferredDocumentImpl.getLastChild(fDeferredEntityDecl, false) == -1) {
                  // entity definition exists and it does not have any children
                  int prevIndex = -1;
                  int childIndex = fDeferredDocumentImpl.getLastChild(fCurrentNodeIndex, false);
                  while (childIndex != -1) {
                      int cloneIndex = fDeferredDocumentImpl.cloneNode(childIndex, true);
                      fDeferredDocumentImpl.insertBefore(fDeferredEntityDecl, cloneIndex, prevIndex);
                      prevIndex = cloneIndex;
                      childIndex = fDeferredDocumentImpl.getRealPrevSibling(childIndex, false);
                  }
              }
              if (fCreateEntityRefNodes) {
                  fCurrentNodeIndex =
                          fDeferredDocumentImpl.getParentNode(fCurrentNodeIndex,
                                  false);
              } else { //!fCreateEntityRefNodes
                  // move children of entity ref before the entity ref.
                  // remove entity ref.
  
                  // holds a child of entity ref
                  int childIndex = fDeferredDocumentImpl.getLastChild(fCurrentNodeIndex, false);
                  int parentIndex =
                          fDeferredDocumentImpl.getParentNode(fCurrentNodeIndex,
                                  false);
  
                  int prevIndex = fCurrentNodeIndex;
                  int lastChild = childIndex;
                  int sibling = -1;
                  while (childIndex != -1) {
                      handleBaseURI(childIndex);
                      sibling = fDeferredDocumentImpl.getRealPrevSibling(childIndex, false);
                      fDeferredDocumentImpl.insertBefore(parentIndex, childIndex, prevIndex);
                      prevIndex = childIndex;
                      childIndex = sibling;
                  }
                  if (lastChild != -1)
                      fDeferredDocumentImpl.setAsLastChild(parentIndex, lastChild);
                  else {
                      sibling = fDeferredDocumentImpl.getRealPrevSibling(prevIndex, false);
                      fDeferredDocumentImpl.setAsLastChild(parentIndex, sibling);
                  }
                  fCurrentNodeIndex = parentIndex;
              }
              fDeferredEntityDecl = -1;
          }
  
  
      } // endGeneralEntity(String, Augmentations)
  
      /**
       * Record baseURI information for the Element (by adding xml:base attribute)
       * or for the ProcessingInstruction (by setting a baseURI field)
       * Non deferred DOM.
       *
       * @param node
       */
      protected final void handleBaseURI(Node node) {
          if (fDocumentImpl != null) {
              // REVISIT: remove dependency on our implementation when
              //          DOM L3 becomes REC
  
              String baseURI = null;
              short nodeType = node.getNodeType();
  
              if (nodeType == Node.ELEMENT_NODE) {
                  // if an element already has xml:base attribute
                  // do nothing
                  if (fNamespaceAware) {
                      if (((Element) node).getAttributeNodeNS("http://www.w3.org/XML/1998/namespace", "base") != null) {
                          return;
                      }
                  } else if (((Element) node).getAttributeNode("xml:base") != null) {
                      return;
                  }
                  // retrive the baseURI from the entity reference
                  baseURI = fCurrentNode.getBaseURI();
                  if (baseURI != null && !baseURI.equals(fDocumentImpl.getDocumentURI())) {
                      if (fNamespaceAware) {
                          ((Element) node).setAttributeNS("http://www.w3.org/XML/1998/namespace", "xml:base", baseURI);
                      } else {
                          ((Element) node).setAttribute("xml:base", baseURI);
                      }
                  }
              } else if (nodeType == Node.PROCESSING_INSTRUCTION_NODE) {
  
                  baseURI = fCurrentNode.getBaseURI();
                  if (baseURI != null && fErrorHandler != null) {
                      DOMErrorImpl error = new DOMErrorImpl();
                      error.fType = "pi-base-uri-not-preserved";
                      error.fRelatedData = baseURI;
                      error.fSeverity = DOMError.SEVERITY_WARNING;
                      fErrorHandler.getErrorHandler().handleError(error);
                  }
              }
          }
      }
  
      /**
       * Record baseURI information for the Element (by adding xml:base attribute)
       * or for the ProcessingInstruction (by setting a baseURI field)
       * Deferred DOM.
       *
       * @param node
       */
      protected final void handleBaseURI(int node) {
          short nodeType = fDeferredDocumentImpl.getNodeType(node, false);
  
          if (nodeType == Node.ELEMENT_NODE) {
              String baseURI = fDeferredDocumentImpl.getNodeValueString(fCurrentNodeIndex, false);
              if (baseURI == null) {
                  baseURI = fDeferredDocumentImpl.getDeferredEntityBaseURI(fDeferredEntityDecl);
              }
              if (baseURI != null && !baseURI.equals(fDeferredDocumentImpl.getDocumentURI())) {
                  fDeferredDocumentImpl.setDeferredAttribute(node,
                          "xml:base",
                          "http://www.w3.org/XML/1998/namespace",
                          baseURI,
                          true);
              }
          } else if (nodeType == Node.PROCESSING_INSTRUCTION_NODE) {
  
  
              // retrieve baseURI from the entity reference
              String baseURI = fDeferredDocumentImpl.getNodeValueString(fCurrentNodeIndex, false);
  
              if (baseURI == null) {
                  // try baseURI of the entity declaration
                  baseURI = fDeferredDocumentImpl.getDeferredEntityBaseURI(fDeferredEntityDecl);
              }
  
              if (baseURI != null && fErrorHandler != null) {
                  DOMErrorImpl error = new DOMErrorImpl();
                  error.fType = "pi-base-uri-not-preserved";
                  error.fRelatedData = baseURI;
                  error.fSeverity = DOMError.SEVERITY_WARNING;
                  fErrorHandler.getErrorHandler().handleError(error);
              }
          }
      }
  
      /**
       * The start of the DTD.
       *
       * @param locator The document locator, or null if the document
       *                location cannot be reported during the parsing of
       *                the document DTD. However, it is <em>strongly</em>
       *                recommended that a locator be supplied that can
       *                at least report the base system identifier of the
       *                DTD.
       * @param augs    Additional information that may include infoset
       *                augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void startDTD(XMLLocator locator, Augmentations augs) throws XNIException {
          if (DEBUG_EVENTS) {
              System.out.println("==>startDTD");
              if (DEBUG_BASEURI) {
                  System.out.println("   expandedSystemId: " + locator.getExpandedSystemId());
                  System.out.println("   baseURI:" + locator.getBaseSystemId());
              }
          }
  
          fInDTD = true;
          if (locator != null) {
              fBaseURIStack.push(locator.getBaseSystemId());
          }
          if (fDeferNodeExpansion || fDocumentImpl != null) {
              fInternalSubset = new StringBuffer(1024);
          }
      } // startDTD(XMLLocator)
  
  
      //
      // XMLDTDHandler methods
      //
  
      /**
       * The end of the DTD.
       *
       * @param augs Additional information that may include infoset
       *             augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endDTD(Augmentations augs) throws XNIException {
          if (DEBUG_EVENTS) {
              System.out.println("==>endDTD()");
          }
          fInDTD = false;
          if (!fBaseURIStack.isEmpty()) {
              fBaseURIStack.pop();
          }
          String internalSubset = fInternalSubset != null && fInternalSubset.length() > 0
                  ? fInternalSubset.toString() : null;
          if (fDeferNodeExpansion) {
              if (internalSubset != null) {
                  fDeferredDocumentImpl.setInternalSubset(fDocumentTypeIndex, internalSubset);
              }
          } else if (fDocumentImpl != null) {
              if (internalSubset != null) {
                  ((DocumentTypeImpl) fDocumentType).setInternalSubset(internalSubset);
              }
          }
      } // endDTD()
  
      /**
       * The start of a conditional section.
       *
       * @param type The type of the conditional section. This value will
       *             either be CONDITIONAL_INCLUDE or CONDITIONAL_IGNORE.
       * @param augs Additional information that may include infoset
       *             augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       * @see #CONDITIONAL_INCLUDE
       * @see #CONDITIONAL_IGNORE
       */
      @Override
      public void startConditional(short type, Augmentations augs) throws XNIException {
      } // startConditional(short)
  
      /**
       * The end of a conditional section.
       *
       * @param augs Additional information that may include infoset
       *             augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endConditional(Augmentations augs) throws XNIException {
      } // endConditional()
  
      /**
       * The start of the DTD external subset.
       *
       * @param augs Additional information that may include infoset
       *             augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void startExternalSubset(XMLResourceIdentifier identifier,
                                      Augmentations augs) throws XNIException {
          if (DEBUG_EVENTS) {
              System.out.println("==>startExternalSubset");
              if (DEBUG_BASEURI) {
                  System.out.println("   expandedSystemId: " + identifier.getExpandedSystemId());
                  System.out.println("   baseURI:" + identifier.getBaseSystemId());
              }
          }
          fBaseURIStack.push(identifier.getBaseSystemId());
          fInDTDExternalSubset = true;
      } // startExternalSubset(Augmentations)
  
      /**
       * The end of the DTD external subset.
       *
       * @param augs Additional information that may include infoset
       *             augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endExternalSubset(Augmentations augs) throws XNIException {
          fInDTDExternalSubset = false;
          fBaseURIStack.pop();
      } // endExternalSubset(Augmentations)
  
      /**
       * An internal entity declaration.
       *
       * @param name              The name of the entity. Parameter entity names start with
       *                          '%', whereas the name of a general entity is just the
       *                          entity name.
       * @param text              The value of the entity.
       * @param nonNormalizedText The non-normalized value of the entity. This
       *                          value contains the same sequence of characters that was in
       *                          the internal entity declaration, without any entity
       *                          references expanded.
       * @param augs              Additional information that may include infoset
       *                          augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void internalEntityDecl(String name, XMLString text,
                                     XMLString nonNormalizedText,
                                     Augmentations augs) throws XNIException {
  
          if (DEBUG_EVENTS) {
              System.out.println("==>internalEntityDecl: " + name);
              if (DEBUG_BASEURI) {
                  System.out.println("   baseURI:" + fBaseURIStack.peek());
              }
          }
          // internal subset string
          if (fInternalSubset != null && !fInDTDExternalSubset) {
              fInternalSubset.append("<!ENTITY ");
              if (name.startsWith("%")) {
                  fInternalSubset.append("% ");
                  fInternalSubset.append(name.substring(1));
              } else {
                  fInternalSubset.append(name);
              }
              fInternalSubset.append(' ');
              String value = nonNormalizedText.toString();
              boolean singleQuote = value.indexOf('\'') == -1;
              fInternalSubset.append(singleQuote ? '\'' : '"');
              fInternalSubset.append(value);
              fInternalSubset.append(singleQuote ? '\'' : '"');
              fInternalSubset.append(">\n");
          }
  
          // NOTE: We only know how to create these nodes for the Xerces
          //       DOM implementation because DOM Level 2 does not specify
          //       that functionality. -Ac
  
          // create full node
          // don't add parameter entities!
          if (name.startsWith("%"))
              return;
          if (fDocumentType != null) {
              NamedNodeMap entities = fDocumentType.getEntities();
              EntityImpl entity = (EntityImpl) entities.getNamedItem(name);
              if (entity == null) {
                  entity = (EntityImpl) fDocumentImpl.createEntity(name);
                  entity.setBaseURI((String) fBaseURIStack.peek());
                  entities.setNamedItem(entity);
              }
          }
  
          // create deferred node
          if (fDocumentTypeIndex != -1) {
              boolean found = false;
              int node = fDeferredDocumentImpl.getLastChild(fDocumentTypeIndex, false);
              while (node != -1) {
                  short nodeType = fDeferredDocumentImpl.getNodeType(node, false);
                  if (nodeType == Node.ENTITY_NODE) {
                      String nodeName = fDeferredDocumentImpl.getNodeName(node, false);
                      if (nodeName.equals(name)) {
                          found = true;
                          break;
                      }
                  }
                  node = fDeferredDocumentImpl.getRealPrevSibling(node, false);
              }
              if (!found) {
                  int entityIndex =
                          fDeferredDocumentImpl.createDeferredEntity(name, null, null, null, (String) fBaseURIStack.peek());
                  fDeferredDocumentImpl.appendChild(fDocumentTypeIndex, entityIndex);
              }
          }
  
      } // internalEntityDecl(String,XMLString,XMLString)
  
      /**
       * An external entity declaration.
       *
       * @param name       The name of the entity. Parameter entity names start
       *                   with '%', whereas the name of a general entity is just
       *                   the entity name.
       * @param identifier An object containing all location information
       *                   pertinent to this notation.
       * @param augs       Additional information that may include infoset
       *                   augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void externalEntityDecl(String name, XMLResourceIdentifier identifier,
                                     Augmentations augs) throws XNIException {
  
  
          if (DEBUG_EVENTS) {
              System.out.println("==>externalEntityDecl: " + name);
              if (DEBUG_BASEURI) {
                  System.out.println("   expandedSystemId:" + identifier.getExpandedSystemId());
                  System.out.println("   baseURI:" + identifier.getBaseSystemId());
              }
          }
          // internal subset string
          String publicId = identifier.getPublicId();
          String literalSystemId = identifier.getLiteralSystemId();
          if (fInternalSubset != null && !fInDTDExternalSubset) {
              fInternalSubset.append("<!ENTITY ");
              if (name.startsWith("%")) {
                  fInternalSubset.append("% ");
                  fInternalSubset.append(name.substring(1));
              } else {
                  fInternalSubset.append(name);
              }
              fInternalSubset.append(' ');
              if (publicId != null) {
                  fInternalSubset.append("PUBLIC '");
                  fInternalSubset.append(publicId);
                  fInternalSubset.append("' '");
              } else {
                  fInternalSubset.append("SYSTEM '");
              }
              fInternalSubset.append(literalSystemId);
              fInternalSubset.append("'>\n");
          }
  
          // NOTE: We only know how to create these nodes for the Xerces
          //       DOM implementation because DOM Level 2 does not specify
          //       that functionality. -Ac
  
          // create full node
          // don't add parameter entities!
          if (name.startsWith("%"))
              return;
          if (fDocumentType != null) {
              NamedNodeMap entities = fDocumentType.getEntities();
              EntityImpl entity = (EntityImpl) entities.getNamedItem(name);
              if (entity == null) {
                  entity = (EntityImpl) fDocumentImpl.createEntity(name);
                  entity.setPublicId(publicId);
                  entity.setSystemId(literalSystemId);
                  entity.setBaseURI(identifier.getBaseSystemId());
                  entities.setNamedItem(entity);
              }
          }
  
          // create deferred node
          if (fDocumentTypeIndex != -1) {
              boolean found = false;
              int nodeIndex = fDeferredDocumentImpl.getLastChild(fDocumentTypeIndex, false);
              while (nodeIndex != -1) {
                  short nodeType = fDeferredDocumentImpl.getNodeType(nodeIndex, false);
                  if (nodeType == Node.ENTITY_NODE) {
                      String nodeName = fDeferredDocumentImpl.getNodeName(nodeIndex, false);
                      if (nodeName.equals(name)) {
                          found = true;
                          break;
                      }
                  }
                  nodeIndex = fDeferredDocumentImpl.getRealPrevSibling(nodeIndex, false);
              }
              if (!found) {
                  int entityIndex = fDeferredDocumentImpl.createDeferredEntity(
                          name, publicId, literalSystemId, null, identifier.getBaseSystemId());
                  fDeferredDocumentImpl.appendChild(fDocumentTypeIndex, entityIndex);
              }
          }
  
      } // externalEntityDecl(String,XMLResourceIdentifier, Augmentations)
  
      /**
       * This method notifies of the start of a parameter entity. The parameter
       * entity name start with a '%' character.
       *
       * @param name       The name of the parameter 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 parameter entities).
       * @param augs       Additional information that may include infoset
       *                   augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void startParameterEntity(String name,
                                       XMLResourceIdentifier identifier,
                                       String encoding,
                                       Augmentations augs) throws XNIException {
          if (DEBUG_EVENTS) {
              System.out.println("==>startParameterEntity: " + name);
              if (DEBUG_BASEURI) {
                  System.out.println("   expandedSystemId: " + identifier.getExpandedSystemId());
                  System.out.println("   baseURI:" + identifier.getBaseSystemId());
              }
          }
          if (augs != null && fInternalSubset != null &&
                  !fInDTDExternalSubset &&
                  Boolean.TRUE.equals(augs.getItem(Constants.ENTITY_SKIPPED))) {
              fInternalSubset.append(name).append(";\n");
          }
          fBaseURIStack.push(identifier.getExpandedSystemId());
      }
  
      /**
       * This method notifies the end of a parameter entity. Parameter entity
       * names begin with a '%' character.
       *
       * @param name The name of the parameter entity.
       * @param augs Additional information that may include infoset
       *             augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endParameterEntity(String name, Augmentations augs) throws XNIException {
  
          if (DEBUG_EVENTS) {
              System.out.println("==>endParameterEntity: " + name);
          }
          fBaseURIStack.pop();
      }
  
      /**
       * An unparsed entity declaration.
       *
       * @param name       The name of the entity.
       * @param identifier An object containing all location information
       *                   pertinent to this entity.
       * @param notation   The name of the notation.
       * @param augs       Additional information that may include infoset
       *                   augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void unparsedEntityDecl(String name, XMLResourceIdentifier identifier,
                                     String notation, Augmentations augs)
              throws XNIException {
  
          if (DEBUG_EVENTS) {
              System.out.println("==>unparsedEntityDecl: " + name);
              if (DEBUG_BASEURI) {
                  System.out.println("   expandedSystemId:" + identifier.getExpandedSystemId());
                  System.out.println("   baseURI:" + identifier.getBaseSystemId());
              }
          }
          // internal subset string
          String publicId = identifier.getPublicId();
          String literalSystemId = identifier.getLiteralSystemId();
          if (fInternalSubset != null && !fInDTDExternalSubset) {
              fInternalSubset.append("<!ENTITY ");
              fInternalSubset.append(name);
              fInternalSubset.append(' ');
              if (publicId != null) {
                  fInternalSubset.append("PUBLIC '");
                  fInternalSubset.append(publicId);
                  if (literalSystemId != null) {
                      fInternalSubset.append("' '");
                      fInternalSubset.append(literalSystemId);
                  }
              } else {
                  fInternalSubset.append("SYSTEM '");
                  fInternalSubset.append(literalSystemId);
              }
              fInternalSubset.append("' NDATA ");
              fInternalSubset.append(notation);
              fInternalSubset.append(">\n");
          }
  
          // NOTE: We only know how to create these nodes for the Xerces
          //       DOM implementation because DOM Level 2 does not specify
          //       that functionality. -Ac
  
          // create full node
          if (fDocumentType != null) {
              NamedNodeMap entities = fDocumentType.getEntities();
              EntityImpl entity = (EntityImpl) entities.getNamedItem(name);
              if (entity == null) {
                  entity = (EntityImpl) fDocumentImpl.createEntity(name);
                  entity.setPublicId(publicId);
                  entity.setSystemId(literalSystemId);
                  entity.setNotationName(notation);
                  entity.setBaseURI(identifier.getBaseSystemId());
                  entities.setNamedItem(entity);
              }
          }
  
          // create deferred node
          if (fDocumentTypeIndex != -1) {
              boolean found = false;
              int nodeIndex = fDeferredDocumentImpl.getLastChild(fDocumentTypeIndex, false);
              while (nodeIndex != -1) {
                  short nodeType = fDeferredDocumentImpl.getNodeType(nodeIndex, false);
                  if (nodeType == Node.ENTITY_NODE) {
                      String nodeName = fDeferredDocumentImpl.getNodeName(nodeIndex, false);
                      if (nodeName.equals(name)) {
                          found = true;
                          break;
                      }
                  }
                  nodeIndex = fDeferredDocumentImpl.getRealPrevSibling(nodeIndex, false);
              }
              if (!found) {
                  int entityIndex = fDeferredDocumentImpl.createDeferredEntity(
                          name, publicId, literalSystemId, notation, identifier.getBaseSystemId());
                  fDeferredDocumentImpl.appendChild(fDocumentTypeIndex, entityIndex);
              }
          }
  
      } // unparsedEntityDecl(String,XMLResourceIdentifier, String, Augmentations)
  
      /**
       * A notation declaration
       *
       * @param name       The name of the notation.
       * @param identifier An object containing all location information
       *                   pertinent to this notation.
       * @param augs       Additional information that may include infoset
       *                   augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void notationDecl(String name, XMLResourceIdentifier identifier,
                               Augmentations augs) throws XNIException {
  
          // internal subset string
          String publicId = identifier.getPublicId();
          String literalSystemId = identifier.getLiteralSystemId();
          if (fInternalSubset != null && !fInDTDExternalSubset) {
              fInternalSubset.append("<!NOTATION ");
              fInternalSubset.append(name);
              if (publicId != null) {
                  fInternalSubset.append(" PUBLIC '");
                  fInternalSubset.append(publicId);
                  if (literalSystemId != null) {
                      fInternalSubset.append("' '");
                      fInternalSubset.append(literalSystemId);
                  }
              } else {
                  fInternalSubset.append(" SYSTEM '");
                  fInternalSubset.append(literalSystemId);
              }
              fInternalSubset.append("'>\n");
          }
  
          // NOTE: We only know how to create these nodes for the Xerces
          //       DOM implementation because DOM Level 2 does not specify
          //       that functionality. -Ac
  
          // create full node
          if (fDocumentImpl != null && fDocumentType != null) {
              NamedNodeMap notations = fDocumentType.getNotations();
              if (notations.getNamedItem(name) == null) {
                  NotationImpl notation = (NotationImpl) fDocumentImpl.createNotation(name);
                  notation.setPublicId(publicId);
                  notation.setSystemId(literalSystemId);
                  notation.setBaseURI(identifier.getBaseSystemId());
                  notations.setNamedItem(notation);
              }
          }
  
          // create deferred node
          if (fDocumentTypeIndex != -1) {
              boolean found = false;
              int nodeIndex = fDeferredDocumentImpl.getLastChild(fDocumentTypeIndex, false);
              while (nodeIndex != -1) {
                  short nodeType = fDeferredDocumentImpl.getNodeType(nodeIndex, false);
                  if (nodeType == Node.NOTATION_NODE) {
                      String nodeName = fDeferredDocumentImpl.getNodeName(nodeIndex, false);
                      if (nodeName.equals(name)) {
                          found = true;
                          break;
                      }
                  }
                  nodeIndex = fDeferredDocumentImpl.getPrevSibling(nodeIndex, false);
              }
              if (!found) {
                  int notationIndex = fDeferredDocumentImpl.createDeferredNotation(
                          name, publicId, literalSystemId, identifier.getBaseSystemId());
                  fDeferredDocumentImpl.appendChild(fDocumentTypeIndex, notationIndex);
              }
          }
  
      } // notationDecl(String,XMLResourceIdentifier, Augmentations)
  
      /**
       * Characters within an IGNORE conditional section.
       *
       * @param text The ignored text.
       * @param augs Additional information that may include infoset
       *             augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void ignoredCharacters(XMLString text, Augmentations augs) throws XNIException {
      } // ignoredCharacters(XMLString, Augmentations)
  
      /**
       * An element declaration.
       *
       * @param name         The name of the element.
       * @param contentModel The element content model.
       * @param augs         Additional information that may include infoset
       *                     augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void elementDecl(String name, String contentModel, Augmentations augs)
              throws XNIException {
  
          // internal subset string
          if (fInternalSubset != null && !fInDTDExternalSubset) {
              fInternalSubset.append("<!ELEMENT ");
              fInternalSubset.append(name);
              fInternalSubset.append(' ');
              fInternalSubset.append(contentModel);
              fInternalSubset.append(">\n");
          }
  
      } // elementDecl(String,String)
  
      /**
       * An attribute declaration.
       *
       * @param elementName               The name of the element that this attribute
       *                                  is associated with.
       * @param attributeName             The name of the attribute.
       * @param type                      The attribute type. This value will be one of
       *                                  the following: "CDATA", "ENTITY", "ENTITIES",
       *                                  "ENUMERATION", "ID", "IDREF", "IDREFS",
       *                                  "NMTOKEN", "NMTOKENS", or "NOTATION".
       * @param enumeration               If the type has the value "ENUMERATION" or
       *                                  "NOTATION", this array holds the allowed attribute
       *                                  values; otherwise, this array is null.
       * @param defaultType               The attribute default type. This value will be
       *                                  one of the following: "#FIXED", "#IMPLIED",
       *                                  "#REQUIRED", or null.
       * @param defaultValue              The attribute default value, or null if no
       *                                  default value is specified.
       * @param nonNormalizedDefaultValue The attribute default value with no normalization
       *                                  performed, or null if no default value is specified.
       * @param augs                      Additional information that may include infoset
       *                                  augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void attributeDecl(String elementName, String attributeName,
                                String type, String[] enumeration,
                                String defaultType, XMLString defaultValue,
                                XMLString nonNormalizedDefaultValue, Augmentations augs) throws XNIException {
  
          // internal subset string
          if (fInternalSubset != null && !fInDTDExternalSubset) {
              fInternalSubset.append("<!ATTLIST ");
              fInternalSubset.append(elementName);
              fInternalSubset.append(' ');
              fInternalSubset.append(attributeName);
              fInternalSubset.append(' ');
              if (type.equals("ENUMERATION")) {
                  fInternalSubset.append('(');
                  for (int i = 0; i < enumeration.length; i++) {
                      if (i > 0) {
                          fInternalSubset.append('|');
                      }
                      fInternalSubset.append(enumeration[i]);
                  }
                  fInternalSubset.append(')');
              } else {
                  fInternalSubset.append(type);
              }
              if (defaultType != null) {
                  fInternalSubset.append(' ');
                  fInternalSubset.append(defaultType);
              }
              if (defaultValue != null) {
                  fInternalSubset.append(" '");
                  for (int i = 0; i < defaultValue.length; i++) {
                      char c = defaultValue.ch[defaultValue.offset + i];
                      if (c == '\'') {
                          fInternalSubset.append("&apos;");
                      } else {
                          fInternalSubset.append(c);
                      }
                  }
                  fInternalSubset.append('\'');
              }
              fInternalSubset.append(">\n");
          }
          // REVISIT: This code applies to the support of domx/grammar-access
          // feature in Xerces 1
  
          // deferred expansion
          if (fDeferredDocumentImpl != null) {
  
              // get the default value
              if (defaultValue != null) {
  
                  // get element definition
                  int elementDefIndex = fDeferredDocumentImpl.lookupElementDefinition(elementName);
  
                  // create element definition if not already there
                  if (elementDefIndex == -1) {
                      elementDefIndex = fDeferredDocumentImpl.createDeferredElementDefinition(elementName);
                      fDeferredDocumentImpl.appendChild(fDocumentTypeIndex, elementDefIndex);
                  }
                  // add default attribute
                  boolean nsEnabled = fNamespaceAware;
                  String namespaceURI = null;
                  if (nsEnabled) {
                      // DOM Level 2 wants all namespace declaration attributes
                      // to be bound to "http://www.w3.org/2000/xmlns/"
                      // So as long as the XML parser doesn't do it, it needs to
                      // done here.
                      if (attributeName.startsWith("xmlns:") ||
                              attributeName.equals("xmlns")) {
                          namespaceURI = NamespaceContext.XMLNS_URI;
                      } else if (attributeName.startsWith("xml:")) {
                          namespaceURI = NamespaceContext.XML_URI;
                      }
                  }
                  int attrIndex = fDeferredDocumentImpl.createDeferredAttribute(
                          attributeName, namespaceURI, defaultValue.toString(), false);
                  if ("ID".equals(type)) {
                      fDeferredDocumentImpl.setIdAttribute(attrIndex);
                  }
                  // REVISIT: set ID type correctly
                  fDeferredDocumentImpl.appendChild(elementDefIndex, attrIndex);
              }
  
          } // if deferred
  
          // full expansion
          else if (fDocumentImpl != null) {
  
              // get the default value
              if (defaultValue != null) {
  
                  // get element definition node
                  NamedNodeMap elements = ((DocumentTypeImpl) fDocumentType).getElements();
                  ElementDefinitionImpl elementDef = (ElementDefinitionImpl) elements.getNamedItem(elementName);
                  if (elementDef == null) {
                      elementDef = fDocumentImpl.createElementDefinition(elementName);
                      ((DocumentTypeImpl) fDocumentType).getElements().setNamedItem(elementDef);
                  }
  
                  // REVISIT: Check for uniqueness of element name? -Ac
  
                  // create attribute and set properties
                  boolean nsEnabled = fNamespaceAware;
                  AttrImpl attr;
                  if (nsEnabled) {
                      String namespaceURI = null;
                      // DOM Level 2 wants all namespace declaration attributes
                      // to be bound to "http://www.w3.org/2000/xmlns/"
                      // So as long as the XML parser doesn't do it, it needs to
                      // done here.
                      if (attributeName.startsWith("xmlns:") ||
                              attributeName.equals("xmlns")) {
                          namespaceURI = NamespaceContext.XMLNS_URI;
                      } else if (attributeName.startsWith("xml:")) {
                          namespaceURI = NamespaceContext.XML_URI;
                      }
                      attr = (AttrImpl) fDocumentImpl.createAttributeNS(namespaceURI,
                              attributeName);
                  } else {
                      attr = (AttrImpl) fDocumentImpl.createAttribute(attributeName);
                  }
                  attr.setValue(defaultValue.toString());
                  attr.setSpecified(false);
                  attr.setIdAttribute("ID".equals(type));
  
                  // add default attribute to element definition
                  if (nsEnabled) {
                      elementDef.getAttributes().setNamedItemNS(attr);
                  } else {
                      elementDef.getAttributes().setNamedItem(attr);
                  }
              }
  
          } // if NOT defer-node-expansion
  
      } // attributeDecl(String,String,String,String[],String,XMLString, XMLString, Augmentations)
  
      /**
       * The start of an attribute list.
       *
       * @param elementName The name of the element that this attribute
       *                    list is associated with.
       * @param augs        Additional information that may include infoset
       *                    augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void startAttlist(String elementName, Augmentations augs) throws XNIException {
      } // startAttlist(String)
  
      /**
       * The end of an attribute list.
       *
       * @param augs Additional information that may include infoset
       *             augmentations.
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endAttlist(Augmentations augs) throws XNIException {
      } // endAttlist()
  
      // method to create an element node.
      // subclasses can override this method to create element nodes in other ways.
      protected Element createElementNode(QName element) {
          Element el = null;
  
          if (fNamespaceAware) {
              // if we are using xerces DOM implementation, call our
              // own constructor to reuse the strings we have here.
              if (fDocumentImpl != null) {
                  el = fDocumentImpl.createElementNS(element.uri, element.rawname,
                          element.localpart);
              } else {
                  el = fDocument.createElementNS(element.uri, element.rawname);
              }
          } else {
              el = fDocument.createElement(element.rawname);
          }
  
          return el;
      }
  
      // method to create an attribute node.
      // subclasses can override this method to create attribute nodes in other ways.
      protected Attr createAttrNode(QName attrQName) {
          Attr attr = null;
  
          if (fNamespaceAware) {
              if (fDocumentImpl != null) {
                  // if we are using xerces DOM implementation, call our
                  // own constructor to reuse the strings we have here.
                  attr = fDocumentImpl.createAttributeNS(attrQName.uri,
                          attrQName.rawname,
                          attrQName.localpart);
              } else {
                  attr = fDocument.createAttributeNS(attrQName.uri,
                          attrQName.rawname);
              }
          } else {
              attr = fDocument.createAttribute(attrQName.rawname);
          }
  
          return attr;
      }
  
      /*
       * When the first characters() call is received, the data is stored in
       * a new Text node. If right after the first characters() we receive another chunk of data,
       * the data from the Text node, following the new characters are appended
       * to the fStringBuffer and the text node data is set to empty.
       *
       * This function is called when the state is changed and the
       * data must be appended to the current node.
       *
       * Note: if DOMFilter is set, you must make sure that if Node is skipped,
       * or removed fFistChunk must be set to true, otherwise some data can be lost.
       *
       */
      protected void setCharacterData(boolean sawChars) {
  
          // handle character data
          fFirstChunk = sawChars;
  
  
          // if we have data in the buffer we must have created
          // a text node already.
  
          Node child = fCurrentNode.getLastChild();
          if (child != null) {
              if (fStringBuffer.length() > 0) {
                  // REVISIT: should this check be performed?
                  if (child.getNodeType() == Node.TEXT_NODE) {
                      if (fDocumentImpl != null) {
                          ((TextImpl) child).replaceData(fStringBuffer.toString());
                      } else {
                          ((Text) child).setData(fStringBuffer.toString());
                      }
                  }
                  // reset string buffer
                  fStringBuffer.setLength(0);
              }
  
              if (fDOMFilter != null && !fInEntityRef) {
                  if ((child.getNodeType() == Node.TEXT_NODE) &&
                          ((fDOMFilter.getWhatToShow() & NodeFilter.SHOW_TEXT) != 0)) {
                      short code = fDOMFilter.acceptNode(child);
                      switch (code) {
                          case LSParserFilter.FILTER_INTERRUPT: {
                              throw Abort.INSTANCE;
                          }
                          case LSParserFilter.FILTER_REJECT: {
                              // fall through to SKIP since Comment has no children.
                          }
                          case LSParserFilter.FILTER_SKIP: {
                              fCurrentNode.removeChild(child);
                              return;
                          }
                          default: {
                              // accept node -- do nothing
                          }
                      }
                  }
              }   // end-if fDOMFilter !=null
  
          } // end-if child !=null
      }
  
      /**
       * @see mf.org.w3c.dom.ls.LSParser#abort()
       */
      public void abort() {
          throw Abort.INSTANCE;
      }
  
      /**
       * If the user stops the process, this exception will be thrown.
       */
      static final class Abort extends RuntimeException {
          static final Abort INSTANCE = new Abort();
          private static final long serialVersionUID = 1687848994976808490L;
  
          private Abort() {
          }
  
          @Override
          public Throwable fillInStackTrace() {
              return this;
          }
      }
  
  
  } // class AbstractDOMParser
  