Package org.apache.xerces.validators.common

Source Code of org.apache.xerces.validators.common.XMLValidator$AttValidatorNMTOKENS

/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 The Apache Software Foundation.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment: 
*       "This product includes software developed by the
*        Apache Software Foundation (http://www.apache.org/)."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
*    not be used to endorse or promote products derived from this
*    software without prior written permission. For written
*    permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
*    nor may "Apache" appear in their name, without prior written
*    permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.apache.org.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

package org.apache.xerces.validators.common;

import org.apache.xerces.framework.XMLAttrList;
import org.apache.xerces.framework.XMLContentSpec;
import org.apache.xerces.framework.XMLDocumentHandler;
import org.apache.xerces.framework.XMLDocumentScanner;
import org.apache.xerces.framework.XMLErrorReporter;
import org.apache.xerces.readers.DefaultEntityHandler;
import org.apache.xerces.readers.XMLEntityHandler;
import org.apache.xerces.utils.ChunkyCharArray;
import org.apache.xerces.utils.NamespacesScope;
import org.apache.xerces.utils.StringPool;
import org.apache.xerces.utils.XMLCharacterProperties;
import org.apache.xerces.utils.XMLMessages;
import org.apache.xerces.utils.ImplementationMessages;

import org.w3c.dom.Document;

import org.xml.sax.InputSource;
import org.xml.sax.EntityResolver;
import org.xml.sax.Locator;
import org.xml.sax.helpers.LocatorImpl;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;

import org.apache.xerces.validators.dtd.DTDImporter;
import org.apache.xerces.validators.schema.SchemaImporter;
import org.apache.xerces.validators.schema.SchemaMessageProvider;
import org.apache.xerces.validators.schema.DatatypeContentModel;
import org.apache.xerces.validators.datatype.InvalidDatatypeValueException;

public final class XMLValidator
    implements DefaultEntityHandler.EventHandler,
               XMLEntityHandler.CharDataHandler,
               XMLDocumentScanner.EventHandler,
               NamespacesScope.NamespacesHandler
{
  // **** DEBUG ****
  static XMLValidator schemaValidator = null;
    static boolean DEBUG = false;
  // **** DEBUG ****


    //
    // Debugging options
    //
    private static final boolean PRINT_EXCEPTION_STACK_TRACE = false;
    private static final boolean DEBUG_PRINT_ATTRIBUTES = false;
    private static final boolean DEBUG_PRINT_CONTENT = false;
    //
    // Package access for use by AttributeValidator classes.
    //
    StringPool fStringPool = null;
    boolean fValidating = false;
    boolean fInElementContent = false;
    int fStandaloneReader = -1;
    //
    //
    //
    private XMLErrorReporter fErrorReporter = null;
    private DefaultEntityHandler fEntityHandler = null;
    private boolean fValidationEnabled = false;
    private boolean fDynamicValidation = false;
    private boolean fValidationEnabledByDynamic = false;
    private boolean fDynamicDisabledByValidation = false;
    private boolean fWarningOnDuplicateAttDef = false;
    private boolean fWarningOnUndeclaredElements = false;
    private int[] fElementTypeStack = new int[8];
    private int[] fElementEntityStack = new int[8];
    private int[] fElementIndexStack = new int[8];
    private int[] fContentSpecTypeStack = new int[8];
    private int[] fElementChildCount = new int[8];
    private int[][] fElementChildren = new int[8][];
    private int fElementDepth = -1;
    private boolean fNamespacesEnabled = false;
    private NamespacesScope fNamespacesScope = null;
    private int fNamespacesPrefix = -1;
    private int fRootElementType = -1;
    private int fAttrListHandle = -1;
    private int fElementDeclCount = 0;
    private int fCurrentElementType = -1;
    private int fCurrentElementEntity = -1;
    private int fCurrentElementIndex = -1;
    private int fCurrentContentSpecType = -1;
    private boolean fSeenDoctypeDecl = false;
    private int fEMPTYSymbol = -1;
    private int fANYSymbol = -1;
    private int fMIXEDSymbol = -1;
    private int fCHILDRENSymbol = -1;
    private int fCDATASymbol = -1;
    private int fIDSymbol = -1;
    private int fIDREFSymbol = -1;
    private int fIDREFSSymbol = -1;
    private int fENTITYSymbol = -1;
    private int fENTITIESSymbol = -1;
    private int fNMTOKENSymbol = -1;
    private int fNMTOKENSSymbol = -1;
    private int fNOTATIONSymbol = -1;
    private int fENUMERATIONSymbol = -1;
    private int fREQUIREDSymbol = -1;
    private int fFIXEDSymbol = -1;
    private int fDATATYPESymbol = -1;
    private int fEpsilonIndex = -1;
    private boolean fScanningDTD = false;
    private DTDImporter fDTDImporter = null;
    private SchemaImporter fSchemaImporter = null;
    private XMLDocumentScanner fDocumentScanner = null;
    private boolean fCalledStartDocument = false;
    private XMLDocumentHandler fDocumentHandler = null;
    private XMLDocumentHandler.DTDHandler fDTDHandler = null;
    private boolean fSeenRootElement = false;
    private XMLAttrList fAttrList = null;
    private int fXMLLang = -1;
    private LocatorImpl fAttrNameLocator = null;
    private boolean fCheckedForSchema = false;
    private Document fSchemaDocument = null;
    private boolean fDeclsAreExternal = false;
    private StringPool.CharArrayRange fCurrentElementCharArrayRange = null;
    private char[] fCharRefData = null;
    private boolean fSendCharDataAsCharArray = false;
    private boolean fBufferDatatype = false;
    private StringBuffer fDatatypeBuffer = new StringBuffer();
    //
    //
    //
    public XMLValidator(StringPool stringPool,
                        XMLErrorReporter errorReporter,
                        DefaultEntityHandler entityHandler,
                        XMLDocumentScanner documentScanner)
    {
        fStringPool = stringPool;
        fErrorReporter = errorReporter;
        fEntityHandler = entityHandler;
        fDocumentScanner = documentScanner;
        fAttrList = new XMLAttrList(fStringPool);
        entityHandler.setEventHandler(this);
        entityHandler.setCharDataHandler(this);
        fDocumentScanner.setEventHandler(this);
        init();

    // **** DEBUG ****
    if ( schemaValidator == null ) {
      // only set for the first one!!
      schemaValidator = this;
    }
    // **** DEBUG ****

    }


  //****DEBUG****
  private String nameOf (int stringPoolIndex)
  {
    return fStringPool.toString(stringPoolIndex);
  }

  private String param (String name, int stringPoolIndex)
  {
    return name + "=\"" + fStringPool.toString(stringPoolIndex) + "\" ";
  }


  private void print(String message)
  {
    if ( this == schemaValidator ) {
      System.err.println(message);
    }
  }

  //****DEBUG****
 

    /**
     * Set char data processing preference and handlers.
     */
    public void initHandlers(boolean sendCharDataAsCharArray,
                             XMLDocumentHandler docHandler,
                             XMLDocumentHandler.DTDHandler dtdHandler)
    {
    //****DEBUG****
    if (DEBUG) print("(GEN) XMLValidator.initHandlers\n");
    //****DEBUG****

        fSendCharDataAsCharArray = sendCharDataAsCharArray;
        fEntityHandler.setSendCharDataAsCharArray(fSendCharDataAsCharArray);
        fDocumentHandler = docHandler;
        fDTDHandler = dtdHandler;
    }
    //
    //
    //
    public Document getSchemaDocument() {
        return fSchemaDocument;
    }
    //
    //
    //
    public void resetOrCopy(StringPool stringPool) throws Exception {
        fAttrList = new XMLAttrList(stringPool);
        resetCommon(stringPool);
    }
    public void reset(StringPool stringPool) throws Exception {
        fAttrList.reset(stringPool);
        resetCommon(stringPool);
    }
    //
    // Turning on validation/dynamic turns on validation if it is off, and this
    // is remembered.  Turning off validation DISABLES validation/dynamic if it
    // is on.  Turning off validation/dynamic DOES NOT turn off validation if it
    // was explicitly turned on, only if it was turned on BECAUSE OF the call to
    // turn validation/dynamic on.  Turning on validation will REENABLE and turn
    // validation/dynamic back on if it was disabled by a call that turned off
    // validation while validation/dynamic was enabled.
    //
    public void setValidationEnabled(boolean flag) throws Exception {
        fValidationEnabled = flag;
        fValidationEnabledByDynamic = false;
        if (fValidationEnabled) {
            if (fDynamicDisabledByValidation) {
                fDynamicValidation = true;
                fDynamicDisabledByValidation = false;
            }
        } else if (fDynamicValidation) {
            fDynamicValidation = false;
            fDynamicDisabledByValidation = true;
        }
        fValidating = fValidationEnabled;
    }
    public boolean getValidationEnabled() {
        return fValidationEnabled;
    }
    public void setDynamicValidationEnabled(boolean flag) throws Exception {
        fDynamicValidation = flag;
        fDynamicDisabledByValidation = false;
        if (!fDynamicValidation) {
            if (fValidationEnabledByDynamic) {
                fValidationEnabled = false;
                fValidationEnabledByDynamic = false;
            }
        } else if (!fValidationEnabled) {
            fValidationEnabled = true;
            fValidationEnabledByDynamic = true;
        }
        fValidating = fValidationEnabled;
    }
    public boolean getDynamicValidationEnabled() {
        return fDynamicValidation;
    }
    public void setNamespacesEnabled(boolean flag) {
        fNamespacesEnabled = flag;
    }
    public boolean getNamespacesEnabled() {
        return fNamespacesEnabled;
    }
    public void setWarningOnDuplicateAttDef(boolean flag) {
        fWarningOnDuplicateAttDef = flag;
    }
    public boolean getWarningOnDuplicateAttDef() {
        return fWarningOnDuplicateAttDef;
    }
    public void setWarningOnUndeclaredElements(boolean flag) {
        fWarningOnUndeclaredElements = flag;
    }
    public boolean getWarningOnUndeclaredElements() {
        return fWarningOnUndeclaredElements;
    }

    //
    // DefaultEntityHandler.EventHandler interface
    //
    //    public void startEntityReference(int entityName, int entityType, int entityContext) throws Exception;
    //    public void endEntityReference(int entityName, int entityType, int entityContext) throws Exception;
    //    public void sendEndOfInputNotifications(int entityName, boolean moreToFollow) throws Exception;
    //    public void sendReaderChangeNotifications(XMLEntityHandler.EntityReader reader, int readerId) throws Exception;
    //    public boolean externalEntityStandaloneCheck();
    //    public boolean getValidating();
    //
    public void startEntityReference(int entityName, int entityType, int entityContext) throws Exception {
        fDocumentHandler.startEntityReference(entityName, entityType, entityContext);
    }
    public void endEntityReference(int entityName, int entityType, int entityContext) throws Exception {
        fDocumentHandler.endEntityReference(entityName, entityType, entityContext);
    }
    public void sendEndOfInputNotifications(int entityName, boolean moreToFollow) throws Exception {
        fDocumentScanner.endOfInput(entityName, moreToFollow);
        if (fScanningDTD) {
            fDTDImporter.sendEndOfInputNotifications(entityName, moreToFollow);
        }
    }
    public void sendReaderChangeNotifications(XMLEntityHandler.EntityReader reader, int readerId) throws Exception {
        fDocumentScanner.readerChange(reader, readerId);
        if (fScanningDTD) {
            fDTDImporter.sendReaderChangeNotifications(reader, readerId);
        }
    }
    public boolean externalEntityStandaloneCheck() {
        return (fStandaloneReader != -1 && fValidating);
    }
    public boolean getValidating() {
        return fValidating;
    }

    //
    // XMLEntityHandler.CharDataHandler interface
    //
    //    public void processCharacters(char[] chars, int offset, int length) throws Exception;
    //    public void processCharacters(int stringHandle) throws Exception;
    //    public void processWhitespace(char[] chars, int offset, int length) throws Exception;
    //    public void processWhitespace(int stringHandle) throws Exception;
    //
    public void processCharacters(char[] chars, int offset, int length) throws Exception {
        if (fValidating) {
            if (fInElementContent || fCurrentContentSpecType == fEMPTYSymbol)
                charDataInContent();
            if (fBufferDatatype)
                fDatatypeBuffer.append(chars, offset, length);
        }
        fDocumentHandler.characters(chars, offset, length);
    }
    public void processCharacters(int data) throws Exception {
        if (fValidating) {
            if (fInElementContent || fCurrentContentSpecType == fEMPTYSymbol)
                charDataInContent();
            if (fBufferDatatype)
                fDatatypeBuffer.append(fStringPool.toString(data));
        }
        fDocumentHandler.characters(data);
    }
    public void processWhitespace(char[] chars, int offset, int length) throws Exception {
        if (fInElementContent) {
            if (fStandaloneReader != -1 && fValidating && getElementDeclIsExternal(fCurrentElementIndex)) {
                reportRecoverableXMLError(XMLMessages.MSG_WHITE_SPACE_IN_ELEMENT_CONTENT_WHEN_STANDALONE,
                                          XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION);
            }
            fDocumentHandler.ignorableWhitespace(chars, offset, length);
        } else {
            if (fCurrentContentSpecType == fEMPTYSymbol)
                charDataInContent();
            fDocumentHandler.characters(chars, offset, length);
        }
    }
    public void processWhitespace(int data) throws Exception {
        if (fInElementContent) {
            if (fStandaloneReader != -1 && fValidating && getElementDeclIsExternal(fCurrentElementIndex)) {
                reportRecoverableXMLError(XMLMessages.MSG_WHITE_SPACE_IN_ELEMENT_CONTENT_WHEN_STANDALONE,
                                          XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION);
            }
            fDocumentHandler.ignorableWhitespace(data);
        } else {
            if (fCurrentContentSpecType == fEMPTYSymbol)
                charDataInContent();
            fDocumentHandler.characters(data);
        }
    }

    //
    // XMLDocumentScanner.EventHandler interface
    //
    //    public int scanElementType(XMLEntityHandler.EntityReader entityReader, char fastchar) throws Exception;
    //    public boolean scanExpectedElementType(XMLEntityHandler.EntityReader entityReader, char fastchar) throws Exception;
    //    public int scanAttributeName(XMLEntityHandler.EntityReader entityReader, int elementType) throws Exception;
    //    public void callStartDocument() throws Exception;
    //    public void callEndDocument() throws Exception;
    //    public void callXMLDecl(int version, int encoding, int standalone) throws Exception;
    //    public void callTextDecl(int version, int encoding) throws Exception;
    //    public void callStartElement(int elementType) throws Exception;
    //    public void callEndElement(int readerId) throws Exception;
    //    public boolean validVersionNum(String version) throws Exception;
    //    public boolean validEncName(String encoding) throws Exception;
    //    public void callStartCDATA() throws Exception;
    //    public void callEndCDATA() throws Exception;
    //    public void callCharacters(int ch) throws Exception;
    //    public void callProcessingInstruction(int piTarget, int piData) throws Exception;
    //    public void callComment(int data) throws Exception;
    //    public void scanDoctypeDecl(boolean standalone) throws Exception;
    //    public int scanAttValue(int elementType, int attrName) throws Exception;
    //


  //
  // scanElementName
  //

    public int scanElementType(XMLEntityHandler.EntityReader entityReader, char fastchar) throws Exception {

    int elementType;

        if (!fNamespacesEnabled) {
            elementType = entityReader.scanName(fastchar);
        } else {

      elementType = entityReader.scanQName(fastchar);
      if (entityReader.lookingAtChar(':', false)) {
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                       XMLMessages.XML_DOMAIN,
                       XMLMessages.MSG_TWO_COLONS_IN_QNAME,
                       XMLMessages.P5_INVALID_CHARACTER,
                       null,
                       XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
        entityReader.skipPastNmtoken(' ');
      }

    }

    //****DEBUG****
        if (DEBUG) {
            String nsFlag = "";
            if ( fNamespacesEnabled ) {
                nsFlag = "NameSpacesEnabled";
            }

            print("(SCN) XMLValidator.scanElementType: " + param("elementType",elementType) + nsFlag + "\n");
        }
    //****DEBUG****

        return elementType;
    }

    public boolean scanExpectedElementType(XMLEntityHandler.EntityReader entityReader, char fastchar) throws Exception {

    //****DEBUG****
    if (DEBUG) print("(SCN) XMLValidator.scanExpectedElementType ... \n");
    //****DEBUG****

        if (fCurrentElementCharArrayRange == null)
            fCurrentElementCharArrayRange = fStringPool.createCharArrayRange();
        fStringPool.getCharArrayRange(fCurrentElementType, fCurrentElementCharArrayRange);
        return entityReader.scanExpectedName(fastchar, fCurrentElementCharArrayRange);
    }

 
  //
  // scanAttributeName
  //

    public int scanAttributeName(XMLEntityHandler.EntityReader entityReader, int elementType) throws Exception {

    int attrName;

        if (!fSeenRootElement) {
            fSeenRootElement = true;
            rootElementSpecified(elementType);
            fStringPool.resetShuffleCount();
        }

        if (!fNamespacesEnabled) {
            attrName = entityReader.scanName('=');
        } else {
      attrName = entityReader.scanQName('=');
      if (entityReader.lookingAtChar(':', false)) {
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                       XMLMessages.XML_DOMAIN,
                       XMLMessages.MSG_TWO_COLONS_IN_QNAME,
                       XMLMessages.P5_INVALID_CHARACTER,
                       null,
                       XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
        entityReader.skipPastNmtoken(' ');
      }
    }

    //****DEBUG****
    if (DEBUG) print("(SCN) XMLValidator.scanAttributeName: " + param("elementType",elementType) + param("attrName",attrName) + "\n");
    //****DEBUG****

        return attrName;
    }


  //
  // callStartDocument
  //

    public void callStartDocument() throws Exception {

    //****DEBUG****
    if (DEBUG) print("\n(VAL) XMLValidator.callStartDocument\n");
    //****DEBUG****

        if (!fCalledStartDocument) {
            fDocumentHandler.startDocument();
            fCalledStartDocument = true;
        }
    }


  //
  // callEndDocument
  //

    public void callEndDocument() throws Exception {

    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.callEndDocument\n\n");
    //****DEBUG****

        if (fCalledStartDocument)
            fDocumentHandler.endDocument();
    }

    public void callXMLDecl(int version, int encoding, int standalone) throws Exception {
        fDocumentHandler.xmlDecl(version, encoding, standalone);
    }

    public void callTextDecl(int version, int encoding) throws Exception {
        fDocumentHandler.textDecl(version, encoding);
    }

    public void callStartElement(int elementType) throws Exception {
        //
        // Check after all specified attrs are scanned
        // (1) report error for REQUIRED attrs that are missing (V_TAGc)
        // (2) add default attrs (FIXED and NOT_FIXED)
        //

    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.callStartElement: " + param("elementType",elementType) + "\n");
    //****DEBUG****

        if (!fSeenRootElement) {
            fSeenRootElement = true;
            rootElementSpecified(elementType);
            fStringPool.resetShuffleCount();
        }
        fCheckedForSchema = true;
        validateElementAndAttributes(elementType, fAttrList);
        fDocumentHandler.startElement(elementType, fAttrList, fAttrListHandle);
        fAttrListHandle = -1;
        if (fElementDepth >= 0) {
            int[] children = fElementChildren[fElementDepth];
            int childCount = fElementChildCount[fElementDepth];
            try {
                children[childCount] = elementType;
            } catch (NullPointerException ex) {
                children = fElementChildren[fElementDepth] = new int[256];
                childCount = 0; // should really assert this...
                children[childCount] = elementType;
            } catch (ArrayIndexOutOfBoundsException ex) {
                int[] newChildren = new int[childCount * 2];
                System.arraycopy(children, 0, newChildren, 0, childCount);
                children = fElementChildren[fElementDepth] = newChildren;
                children[childCount] = elementType;
            }
            fElementChildCount[fElementDepth] = ++childCount;
        }
        fElementDepth++;
        if (fElementDepth == fElementTypeStack.length) {
            int[] newStack = new int[fElementDepth * 2];
            System.arraycopy(fElementTypeStack, 0, newStack, 0, fElementDepth);
            fElementTypeStack = newStack;
            newStack = new int[fElementDepth * 2];
            System.arraycopy(fElementEntityStack, 0, newStack, 0, fElementDepth);
            fElementEntityStack = newStack;
            newStack = new int[fElementDepth * 2];
            System.arraycopy(fElementIndexStack, 0, newStack, 0, fElementDepth);
            fElementIndexStack = newStack;
            newStack = new int[fElementDepth * 2];
            System.arraycopy(fContentSpecTypeStack, 0, newStack, 0, fElementDepth);
            fContentSpecTypeStack = newStack;
            newStack = new int[fElementDepth * 2];
            System.arraycopy(fElementChildCount, 0, newStack, 0, fElementDepth);
            fElementChildCount = newStack;
            int[][] newContentStack = new int[fElementDepth * 2][];
            System.arraycopy(fElementChildren, 0, newContentStack, 0, fElementDepth);
            fElementChildren = newContentStack;
        }
        fCurrentElementType = elementType;
        fCurrentElementEntity = fEntityHandler.getReaderId();
        fElementTypeStack[fElementDepth] = fCurrentElementType;
        fElementEntityStack[fElementDepth] = fCurrentElementEntity;
        fElementIndexStack[fElementDepth] = fCurrentElementIndex;
        fContentSpecTypeStack[fElementDepth] = fCurrentContentSpecType;
        fElementChildCount[fElementDepth] = 0;
    }


  //
  // callEndElement
  //

    public void callEndElement(int readerId) throws Exception {

    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.callEndElement: " + param("readerId",readerId) + "\n");
    //****DEBUG****

        int elementType = fCurrentElementType;
        if (fCurrentElementEntity != readerId) {
            fErrorReporter.reportError(fErrorReporter.getLocator(),
                                       XMLMessages.XML_DOMAIN,
                                       XMLMessages.MSG_ELEMENT_ENTITY_MISMATCH,
                                       XMLMessages.P78_NOT_WELLFORMED,
                                       new Object[] { fStringPool.toString(elementType) },
                                       XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
        }
        fDocumentHandler.endElement(elementType);
        if (fValidating) {
            int elementIndex = fCurrentElementIndex;
            if (elementIndex != -1 && fCurrentContentSpecType != -1) {
                int childCount = peekChildCount();
                int result = checkContent(elementIndex, childCount, peekChildren());
                if (result != -1) {
                    int majorCode = result != childCount ? XMLMessages.MSG_CONTENT_INVALID : XMLMessages.MSG_CONTENT_INCOMPLETE;
                    reportRecoverableXMLError(majorCode,
                                              0,
                                              fStringPool.toString(elementType),
                                              getContentSpecAsString(elementIndex));
                }
            }
        }
        if (fNamespacesEnabled)
            fNamespacesScope.decreaseDepth();
        if (fElementDepth-- < 0)
            throw new RuntimeException("FWK008 Element stack underflow");
        if (fElementDepth < 0) {
            fCurrentElementType = -1;
            fCurrentElementEntity = -1;
            fCurrentElementIndex = -1;
            fCurrentContentSpecType = -1;
            fInElementContent = false;
            //
            // Check after document is fully parsed
            // (1) check that there was an element with a matching id for every
            //   IDREF and IDREFS attr (V_IDREF0)
            //
            if (fValidating && fIdRefs != null)
                checkIdRefs();
            return;
        }
        fCurrentElementType = fElementTypeStack[fElementDepth];
        fCurrentElementEntity = fElementEntityStack[fElementDepth];
        fCurrentElementIndex = fElementIndexStack[fElementDepth];
        fCurrentContentSpecType = fContentSpecTypeStack[fElementDepth];
        if (fValidating)
            fBufferDatatype = false;
        fInElementContent = (fCurrentContentSpecType == fCHILDRENSymbol);
    }

 
  //
  // validateVersionNum
  //

    public boolean validVersionNum(String version) {

    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.validVersionNum: version=" + version + "\n");
    //****DEBUG****

        return XMLCharacterProperties.validVersionNum(version);
    }


  //
  // validEncName
  //

    public boolean validEncName(String encoding) {

    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.validEncName: encoding=" + encoding + "\n");
    //****DEBUG****

        return XMLCharacterProperties.validEncName(encoding);
    }

 
  //
  // callStartCDATA
  //

    public void callStartCDATA() throws Exception {

    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.callStartCDATA\n");
    //****DEBUG****

        fDocumentHandler.startCDATA();
    }


  //
  // callEndCDATA
  //

    public void callEndCDATA() throws Exception {

    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.callEndCDATA\n");
    //****DEBUG****

        fDocumentHandler.endCDATA();
    }


  //
  // callCharacters
  //

    public void callCharacters(int ch) throws Exception {

    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.callCharacters: ... \n");
    //****DEBUG****

        if (fCharRefData == null)
            fCharRefData = new char[2];
        int count = (ch < 0x10000) ? 1 : 2;
        if (count == 1)
            fCharRefData[0] = (char)ch;
        else {
            fCharRefData[0] = (char)(((ch-0x00010000)>>10)+0xd800);
            fCharRefData[1] = (char)(((ch-0x00010000)&0x3ff)+0xdc00);
        }
        if (fValidating && (fInElementContent || fCurrentContentSpecType == fEMPTYSymbol)) {
            charDataInContent();
        }
        if (fSendCharDataAsCharArray) {
            fDocumentHandler.characters(fCharRefData, 0, count);
        } else {
            int index = fStringPool.addString(new String(fCharRefData, 0, count));
            fDocumentHandler.characters(index);
        }
    }


  //
  // callProcessingInstruction
  //

    public void callProcessingInstruction(int target, int data) throws Exception {
        fDocumentHandler.processingInstruction(target, data);
    }


  //
  // callComment
  //

    public void callComment(int comment) throws Exception {
        fDocumentHandler.comment(comment);
    }


  //
  // scanDocTypeDecl
  //

    public void scanDoctypeDecl(boolean standalone) throws Exception {

    //****DEBUG****
    if (DEBUG) print("(SCN) XMLValidator.scanDoctypeDecl\n");
    //****DEBUG****

    //****DEBUG****
    if (DEBUG) print("\n\n**** BEGIN DTD ****\n\n");
    //****DEBUG****

        fScanningDTD = true;
        fCheckedForSchema = true;
        fSeenDoctypeDecl = true;
        fStandaloneReader = standalone ? fEntityHandler.getReaderId() : -1;
        fDeclsAreExternal = false;
        if (fDTDImporter == null) {
            fDTDImporter = new DTDImporter(fStringPool, fErrorReporter, fEntityHandler, this);
        } else {
            fDTDImporter.reset(fStringPool);
        }
        fDTDImporter.initHandlers(fDTDHandler);
        fDTDImporter.setValidating(fValidating);
        fDTDImporter.setNamespacesEnabled(fNamespacesEnabled);
        if (fDTDImporter.scanDoctypeDecl(standalone) && fValidating) {
            // check declared elements
            if (fWarningOnUndeclaredElements)
                checkDeclaredElements();

            // check required notations
            fEntityHandler.checkRequiredNotations();
        }
        fScanningDTD = false;

    //****DEBUG****
    if (DEBUG) print("\n\n**** END DTD ****\n\n");
    //****DEBUG****
    }


  //
  // scannAttValue
  //

    public int scanAttValue(int elementType, int attrName) throws Exception {


        fAttrNameLocator = getLocatorImpl(fAttrNameLocator);
        int attValue = fDocumentScanner.scanAttValue(elementType, attrName, fValidating/* && attType != fCDATASymbol*/);
        if (attValue == -1) {
            return XMLDocumentScanner.RESULT_FAILURE;
        }

    //****DEBUG****
    if (DEBUG) print("(SCN) XMLValidator.scanAttValue: " + param ("elementType",elementType) +  param ("attrName",attrName) +
              param ("attValue",attValue) + "\n" );
    //****DEBUG****

    //
    // Check for Schema and load
    //

        if (!fCheckedForSchema) {
            fCheckedForSchema = true;
            if (attrName == fStringPool.addSymbol("xmlns")) { // default namespacedecl
       
                if (fSchemaImporter == null) {
                    fSchemaImporter = new SchemaImporter(fStringPool, fErrorReporter, fEntityHandler, this);
                } else {
                    fSchemaImporter.reset(fStringPool);
                }
                String fs = fEntityHandler.expandSystemId(fStringPool.toString(attValue));
                EntityResolver resolver = fEntityHandler.getEntityResolver();
                InputSource is = resolver == null ? null : resolver.resolveEntity(null, fs);
                if (is == null) {
                    is = new InputSource(fs);
                }

        //****DEBUG****
        if (DEBUG) print( "\n\n**** BEGIN SCHEMA ****\n\n" );
        //****DEBUG****

                fSchemaImporter.loadSchema(is);

        //****DEBUG****
        if (DEBUG) print( "\n\n**** END SCHEMA ****\n\n" );
        //****DEBUG****

                fSchemaDocument = fSchemaImporter.getSchemaDocument();
            }
        }

        if (!fValidating && fAttDefCount == 0) {
            int attType = fCDATASymbol;
            if (fAttrListHandle == -1)
                fAttrListHandle = fAttrList.startAttrList();
            if (fAttrList.addAttr(attrName, attValue, attType, true, true) == -1) {
                return XMLDocumentScanner.RESULT_DUPLICATE_ATTR;
            }
            return XMLDocumentScanner.RESULT_SUCCESS;
        }

        int attDefIndex = getAttDef(elementType, attrName);
        if (attDefIndex == -1) {
            if (fValidating) {
                // REVISIT - cache the elem/attr tuple so that we only give
                //  this error once for each unique occurrence
                Object[] args = { fStringPool.toString(elementType),
                                  fStringPool.toString(attrName) };
                fErrorReporter.reportError(fAttrNameLocator,
                                           XMLMessages.XML_DOMAIN,
                                           XMLMessages.MSG_ATTRIBUTE_NOT_DECLARED,
                                           XMLMessages.VC_ATTRIBUTE_VALUE_TYPE,
                                           args,
                                           XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
            }
            int attType = fCDATASymbol;
            if (fAttrListHandle == -1)
                fAttrListHandle = fAttrList.startAttrList();
            if (fAttrList.addAttr(attrName, attValue, attType, true, true) == -1) {
                return XMLDocumentScanner.RESULT_DUPLICATE_ATTR;
            }
            return XMLDocumentScanner.RESULT_SUCCESS;
        }

        int attType = getAttType(attDefIndex);
        if (attType != fCDATASymbol) {
            AttributeValidator av = getAttributeValidator(attDefIndex);
            int enumHandle = getEnumeration(attDefIndex);
            attValue = av.normalize(elementType, attrName, attValue, attType, enumHandle);
        }

        if (fAttrListHandle == -1)
            fAttrListHandle = fAttrList.startAttrList();
        if (fAttrList.addAttr(attrName, attValue, attType, true, true) == -1) {
            return XMLDocumentScanner.RESULT_DUPLICATE_ATTR;
        }

        return XMLDocumentScanner.RESULT_SUCCESS;
    }

    //
    // NamespacesScope.NamespacesHandler interface
    //
    //    public void startNamespaceDeclScope(int prefix, int uri) throws Exception;
    //    public void endNamespaceDeclScope(int prefix) throws Exception;
    //

    public void startNamespaceDeclScope(int prefix, int uri) throws Exception {
    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.startNamespaceDeclScope: " + param("prefix",prefix) + param("uri",uri) + "\n");
    //****DEBUG****
        fDocumentHandler.startNamespaceDeclScope(prefix, uri);
    }


  //
  // endNamespaceDeclScope
  //

    public void endNamespaceDeclScope(int prefix) throws Exception {
    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.endNamespaceDeclScope: " + param("prefix",prefix) + "\n");
    //****DEBUG****
        fDocumentHandler.endNamespaceDeclScope(prefix);
    }

    //
    // XMLValidator implementation
    //
    private void resetCommon(StringPool stringPool) throws Exception {
    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.resetCommon\n");
    //****DEBUG****
        fStringPool = stringPool;
        fValidating = fValidationEnabled;
        fValidationEnabledByDynamic = false;
        fDynamicDisabledByValidation = false;
        poolReset();
        fCalledStartDocument = false;
        fStandaloneReader = -1;
        fElementDepth = -1;
        fSeenRootElement = false;
        fSeenDoctypeDecl = false;
        fNamespacesScope = null;
        fNamespacesPrefix = -1;
        fRootElementType = -1;
        fAttrListHandle = -1;
        fElementDeclCount = 0;
        fCheckedForSchema = false;
        fSchemaDocument = null;
        init();
    }

    private void init() {
    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.init\n");
    //****DEBUG****
        fEMPTYSymbol = fStringPool.addSymbol("EMPTY");
        fANYSymbol = fStringPool.addSymbol("ANY");
        fMIXEDSymbol = fStringPool.addSymbol("MIXED");
        fCHILDRENSymbol = fStringPool.addSymbol("CHILDREN");
        fCDATASymbol = fStringPool.addSymbol("CDATA");
        fIDSymbol = fStringPool.addSymbol("ID");
        fIDREFSymbol = fStringPool.addSymbol("IDREF");
        fIDREFSSymbol = fStringPool.addSymbol("IDREFS");
        fENTITYSymbol = fStringPool.addSymbol("ENTITY");
        fENTITIESSymbol = fStringPool.addSymbol("ENTITIES");
        fNMTOKENSymbol = fStringPool.addSymbol("NMTOKEN");
        fNMTOKENSSymbol = fStringPool.addSymbol("NMTOKENS");
        fNOTATIONSymbol = fStringPool.addSymbol("NOTATION");
        fENUMERATIONSymbol = fStringPool.addSymbol("ENUMERATION");
        fREQUIREDSymbol = fStringPool.addSymbol("#REQUIRED");
        fFIXEDSymbol = fStringPool.addSymbol("#FIXED");
        fDATATYPESymbol = fStringPool.addSymbol("DATATYPE");
        fEpsilonIndex = fStringPool.addSymbol("<<CMNODE_EPSILON>>");
        fXMLLang = fStringPool.addSymbol("xml:lang");
    }
    //
    //
    //
    public int normalizeAttValue(int elementType, int attrName, int attValue, int attType, int enumHandle) throws Exception {
    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.normalizeAttValue: " + param("elementType",elementType) + param("attValue",attValue) +
              param("attType",attType) + param("enumHandle",enumHandle) + "\n");
    //****DEBUG****
        AttributeValidator av = getValidatorForAttType(attType);
        return av.normalize(elementType, attrName, attValue, attType, enumHandle);
    }



  //====================================================
  // AttributeValidator Interface and Implementations
  //====================================================


    //
    // AttributeValidator
    //

    public interface AttributeValidator {
        //
        //
        //
        public int normalize(int elementType, int attrName, int attValue, int attType, int enumHandle) throws Exception;
    }


  //
  // AttValidatorCDATA
  //

    final class AttValidatorCDATA implements AttributeValidator {
        public int normalize(int elementType, int attrName, int attValueHandle, int attType, int enumHandle) throws Exception {
            //
            // Normalize attribute based upon attribute type...
            //
            return attValueHandle;
        }
    }


  //
  // AttValidatorID
  //

    final class AttValidatorID implements AttributeValidator {
        public int normalize(int elementType, int attrName, int attValueHandle, int attType, int enumHandle) throws Exception {
            //
            // Normalize attribute based upon attribute type...
            //
            String attValue = fStringPool.toString(attValueHandle);
            String newAttValue = attValue.trim();
            if (fValidating) {
                // REVISIT - can we release the old string?
                if (newAttValue != attValue) {
                    if (invalidStandaloneAttDef(elementType, attrName)) {
                        reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
                                                  XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
                                                  fStringPool.toString(attrName), attValue, newAttValue);
                    }
                    attValueHandle = fStringPool.addSymbol(newAttValue);
                } else {
                    attValueHandle = fStringPool.addSymbol(attValueHandle);
                }
                if (!XMLCharacterProperties.validName(newAttValue)) {
                    reportRecoverableXMLError(XMLMessages.MSG_ID_INVALID,
                                              XMLMessages.VC_ID,
                                              fStringPool.toString(attrName), newAttValue);
                }
                //
                // ID - check that the id value is unique within the document (V_TAG8)
                //
                if (elementType != -1 && !addId(attValueHandle)) {
                    reportRecoverableXMLError(XMLMessages.MSG_ID_NOT_UNIQUE,
                                              XMLMessages.VC_ID,
                                              fStringPool.toString(attrName), newAttValue);
                }
            } else if (newAttValue != attValue) {
                // REVISIT - can we release the old string?
                attValueHandle = fStringPool.addSymbol(newAttValue);
            }
            return attValueHandle;
        }
        //
        //
        //
        boolean invalidStandaloneAttDef(int elementType, int attrName) {
            if (fStandaloneReader == -1)
                return false;
            if (elementType == -1) // we are normalizing a default att value...  this ok?
                return false;
            return getAttDefIsExternal(elementType, attrName);
        }
    }

 
  //
  // AttValidatorIDREF
  //

    final class AttValidatorIDREF implements AttributeValidator {
        public int normalize(int elementType, int attrName, int attValueHandle, int attType, int enumHandle) throws Exception {
            //
            // Normalize attribute based upon attribute type...
            //
            String attValue = fStringPool.toString(attValueHandle);
            String newAttValue = attValue.trim();
            if (fValidating) {
                // REVISIT - can we release the old string?
                if (newAttValue != attValue) {
                    if (invalidStandaloneAttDef(elementType, attrName)) {
                        reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
                                                  XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
                                                  fStringPool.toString(attrName), attValue, newAttValue);
                    }
                    attValueHandle = fStringPool.addSymbol(newAttValue);
                } else {
                    attValueHandle = fStringPool.addSymbol(attValueHandle);
                }
                if (!XMLCharacterProperties.validName(newAttValue)) {
                    reportRecoverableXMLError(XMLMessages.MSG_IDREF_INVALID,
                                              XMLMessages.VC_IDREF,
                                              fStringPool.toString(attrName), newAttValue);
                }
                //
                // IDREF - remember the id value
                //
                if (elementType != -1)
                    addIdRef(attValueHandle);
            } else if (newAttValue != attValue) {
                // REVISIT - can we release the old string?
                attValueHandle = fStringPool.addSymbol(newAttValue);
            }
            return attValueHandle;
        }
        //
        //
        //
        boolean invalidStandaloneAttDef(int elementType, int attrName) {
            if (fStandaloneReader == -1)
                return false;
            if (elementType == -1) // we are normalizing a default att value...  this ok?
                return false;
            return getAttDefIsExternal(elementType, attrName);
        }
    }


  //
  // AttValidatorIDREFS
  //

    final class AttValidatorIDREFS implements AttributeValidator {
        public int normalize(int elementType, int attrName, int attValueHandle, int attType, int enumHandle) throws Exception {
            //
            // Normalize attribute based upon attribute type...
            //
            String attValue = fStringPool.toString(attValueHandle);
            StringTokenizer tokenizer = new StringTokenizer(attValue);
            StringBuffer sb = new StringBuffer(attValue.length());
            boolean ok = true;
            if (tokenizer.hasMoreTokens()) {
                while (true) {
                    String idName = tokenizer.nextToken();
                    if (fValidating) {
                        if (!XMLCharacterProperties.validName(idName)) {
                            ok = false;
                        }
                        //
                        // IDREFS - remember the id values
                        //
                        if (elementType != -1)
                            addIdRef(fStringPool.addSymbol(idName));
                    }
                    sb.append(idName);
                    if (!tokenizer.hasMoreTokens())
                        break;
                    sb.append(' ');
                }
            }
            String newAttValue = sb.toString();
            if (fValidating && (!ok || newAttValue.length() == 0)) {
                reportRecoverableXMLError(XMLMessages.MSG_IDREFS_INVALID,
                                          XMLMessages.VC_IDREF,
                                          fStringPool.toString(attrName), newAttValue);
            }
            if (!newAttValue.equals(attValue)) {
                attValueHandle = fStringPool.addString(newAttValue);
                if (fValidating && invalidStandaloneAttDef(elementType, attrName)) {
                    reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
                                              XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
                                              fStringPool.toString(attrName), attValue, newAttValue);
                }
            }
            return attValueHandle;
        }
        //
        //
        //
        boolean invalidStandaloneAttDef(int elementType, int attrName) {
            if (fStandaloneReader == -1)
                return false;
            if (elementType == -1) // we are normalizing a default att value...  this ok?
                return false;
            return getAttDefIsExternal(elementType, attrName);
        }
    }


  //
  // AttValidatorENTITY
  //

    final class AttValidatorENTITY implements AttributeValidator {
        public int normalize(int elementType, int attrName, int attValueHandle, int attType, int enumHandle) throws Exception {
            //
            // Normalize attribute based upon attribute type...
            //
            String attValue = fStringPool.toString(attValueHandle);
            String newAttValue = attValue.trim();
            if (fValidating) {
                // REVISIT - can we release the old string?
                if (newAttValue != attValue) {
                    if (invalidStandaloneAttDef(elementType, attrName)) {
                        reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
                                                  XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
                                                  fStringPool.toString(attrName), attValue, newAttValue);
                    }
                    attValueHandle = fStringPool.addSymbol(newAttValue);
                } else {
                    attValueHandle = fStringPool.addSymbol(attValueHandle);
                }
                //
                // ENTITY - check that the value is an unparsed entity name (V_TAGa)
                //
                if (!fEntityHandler.isUnparsedEntity(attValueHandle)) {
                    reportRecoverableXMLError(XMLMessages.MSG_ENTITY_INVALID,
                                              XMLMessages.VC_ENTITY_NAME,
                                              fStringPool.toString(attrName), newAttValue);
                }
            } else if (newAttValue != attValue) {
                // REVISIT - can we release the old string?
                attValueHandle = fStringPool.addSymbol(newAttValue);
            }
            return attValueHandle;
        }
        //
        //
        //
        boolean invalidStandaloneAttDef(int elementType, int attrName) {
            if (fStandaloneReader == -1)
                return false;
            if (elementType == -1) // we are normalizing a default att value...  this ok?
                return false;
            return getAttDefIsExternal(elementType, attrName);
        }
    }


  //
  // AttValidatorENTITIES
  //

    final class AttValidatorENTITIES implements AttributeValidator {
        public int normalize(int elementType, int attrName, int attValueHandle, int attType, int enumHandle) throws Exception {
            //
            // Normalize attribute based upon attribute type...
            //
            String attValue = fStringPool.toString(attValueHandle);
            StringTokenizer tokenizer = new StringTokenizer(attValue);
            StringBuffer sb = new StringBuffer(attValue.length());
            boolean ok = true;
            if (tokenizer.hasMoreTokens()) {
                while (true) {
                    String entityName = tokenizer.nextToken();
                    //
                    // ENTITIES - check that each value is an unparsed entity name (V_TAGa)
                    //
                    if (fValidating && !fEntityHandler.isUnparsedEntity(fStringPool.addSymbol(entityName))) {
                        ok = false;
                    }
                    sb.append(entityName);
                    if (!tokenizer.hasMoreTokens())
                        break;
                    sb.append(' ');
                }
            }
            String newAttValue = sb.toString();
            if (fValidating && (!ok || newAttValue.length() == 0)) {
                reportRecoverableXMLError(XMLMessages.MSG_ENTITIES_INVALID,
                                          XMLMessages.VC_ENTITY_NAME,
                                          fStringPool.toString(attrName), newAttValue);
            }
            if (!newAttValue.equals(attValue)) {
                attValueHandle = fStringPool.addString(newAttValue);
                if (fValidating && invalidStandaloneAttDef(elementType, attrName)) {
                    reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
                                              XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
                                              fStringPool.toString(attrName), attValue, newAttValue);
                }
            }
            return attValueHandle;
        }
        //
        //
        //
        boolean invalidStandaloneAttDef(int elementType, int attrName) {
            if (fStandaloneReader == -1)
                return false;
            if (elementType == -1) // we are normalizing a default att value...  this ok?
                return false;
            return getAttDefIsExternal(elementType, attrName);
        }
    }


  //
  // AttValidatorNMTOKEN
  //

    final class AttValidatorNMTOKEN implements AttributeValidator {
        public int normalize(int elementType, int attrName, int attValueHandle, int attType, int enumHandle) throws Exception {
            //
            // Normalize attribute based upon attribute type...
            //
            String attValue = fStringPool.toString(attValueHandle);
            String newAttValue = attValue.trim();
            if (fValidating) {
                // REVISIT - can we release the old string?
                if (newAttValue != attValue) {
                    if (invalidStandaloneAttDef(elementType, attrName)) {
                        reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
                                                  XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
                                                  fStringPool.toString(attrName), attValue, newAttValue);
                    }
                    attValueHandle = fStringPool.addSymbol(newAttValue);
                } else {
                    attValueHandle = fStringPool.addSymbol(attValueHandle);
                }
                if (!XMLCharacterProperties.validNmtoken(newAttValue)) {
                    reportRecoverableXMLError(XMLMessages.MSG_NMTOKEN_INVALID,
                                              XMLMessages.VC_NAME_TOKEN,
                                              fStringPool.toString(attrName), newAttValue);
                }
            } else if (newAttValue != attValue) {
                // REVISIT - can we release the old string?
                attValueHandle = fStringPool.addSymbol(newAttValue);
            }
            return attValueHandle;
        }
        //
        //
        //
        boolean invalidStandaloneAttDef(int elementType, int attrName) {
            if (fStandaloneReader == -1)
                return false;
            if (elementType == -1) // we are normalizing a default att value...  this ok?
                return false;
            return getAttDefIsExternal(elementType, attrName);
        }
    }


  //
  // AttValidatorNMTOKENS
  //

    final class AttValidatorNMTOKENS implements AttributeValidator {
        public int normalize(int elementType, int attrName, int attValueHandle, int attType, int enumHandle) throws Exception {
            //
            // Normalize attribute based upon attribute type...
            //
            String attValue = fStringPool.toString(attValueHandle);
            StringTokenizer tokenizer = new StringTokenizer(attValue);
            StringBuffer sb = new StringBuffer(attValue.length());
            boolean ok = true;
            if (tokenizer.hasMoreTokens()) {
                while (true) {
                    String nmtoken = tokenizer.nextToken();
                    if (fValidating && !XMLCharacterProperties.validNmtoken(nmtoken)) {
                        ok = false;
                    }
                    sb.append(nmtoken);
                    if (!tokenizer.hasMoreTokens())
                        break;
                    sb.append(' ');
                }
            }
            String newAttValue = sb.toString();
            if (fValidating && (!ok || newAttValue.length() == 0)) {
                reportRecoverableXMLError(XMLMessages.MSG_NMTOKENS_INVALID,
                                          XMLMessages.VC_NAME_TOKEN,
                                          fStringPool.toString(attrName), newAttValue);
            }
            if (!newAttValue.equals(attValue)) {
                attValueHandle = fStringPool.addString(newAttValue);
                if (fValidating && invalidStandaloneAttDef(elementType, attrName)) {
                    reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
                                              XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
                                              fStringPool.toString(attrName), attValue, newAttValue);
                }
            }
            return attValueHandle;
        }
        //
        //
        //
        boolean invalidStandaloneAttDef(int elementType, int attrName) {
            if (fStandaloneReader == -1)
                return false;
            if (elementType == -1) // we are normalizing a default att value...  this ok?
                return false;
            return getAttDefIsExternal(elementType, attrName);
        }
    }


  //
  // AttValidatorNOTATION
  //

    final class AttValidatorNOTATION implements AttributeValidator {
        public int normalize(int elementType, int attrName, int attValueHandle, int attType, int enumHandle) throws Exception {
            //
            // Normalize attribute based upon attribute type...
            //
            String attValue = fStringPool.toString(attValueHandle);
            String newAttValue = attValue.trim();
            if (fValidating) {
                // REVISIT - can we release the old string?
                if (newAttValue != attValue) {
                    if (invalidStandaloneAttDef(elementType, attrName)) {
                        reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
                                                  XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
                                                  fStringPool.toString(attrName), attValue, newAttValue);
                    }
                    attValueHandle = fStringPool.addSymbol(newAttValue);
                } else {
                    attValueHandle = fStringPool.addSymbol(attValueHandle);
                }
                //
                // NOTATION - check that the value is in the AttDef enumeration (V_TAGo)
                //
                if (!fStringPool.stringInList(enumHandle, attValueHandle)) {
                    reportRecoverableXMLError(XMLMessages.MSG_ATTRIBUTE_VALUE_NOT_IN_LIST,
                                              XMLMessages.VC_NOTATION_ATTRIBUTES,
                                              fStringPool.toString(attrName),
                                              newAttValue, fStringPool.stringListAsString(enumHandle));
                }
            } else if (newAttValue != attValue) {
                // REVISIT - can we release the old string?
                attValueHandle = fStringPool.addSymbol(newAttValue);
            }
            return attValueHandle;
        }
        //
        //
        //
        boolean invalidStandaloneAttDef(int elementType, int attrName) {
            if (fStandaloneReader == -1)
                return false;
            if (elementType == -1) // we are normalizing a default att value...  this ok?
                return false;
            return getAttDefIsExternal(elementType, attrName);
        }
    }


  //
  // AttValidatorENUMERATION
  //

    final class AttValidatorENUMERATION implements AttributeValidator {
        public int normalize(int elementType, int attrName, int attValueHandle, int attType, int enumHandle) throws Exception {
            //
            // Normalize attribute based upon attribute type...
            //
            String attValue = fStringPool.toString(attValueHandle);
            String newAttValue = attValue.trim();
            if (fValidating) {
                // REVISIT - can we release the old string?
                if (newAttValue != attValue) {
                    if (invalidStandaloneAttDef(elementType, attrName)) {
                        reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
                                                  XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
                                                  fStringPool.toString(attrName), attValue, newAttValue);
                    }
                    attValueHandle = fStringPool.addSymbol(newAttValue);
                } else {
                    attValueHandle = fStringPool.addSymbol(attValueHandle);
                }
                //
                // ENUMERATION - check that value is in the AttDef enumeration (V_TAG9)
                //
                if (!fStringPool.stringInList(enumHandle, attValueHandle)) {
                    reportRecoverableXMLError(XMLMessages.MSG_ATTRIBUTE_VALUE_NOT_IN_LIST,
                                              XMLMessages.VC_ENUMERATION,
                                              fStringPool.toString(attrName),
                                              newAttValue, fStringPool.stringListAsString(enumHandle));
                }
            } else if (newAttValue != attValue) {
                // REVISIT - can we release the old string?
                attValueHandle = fStringPool.addSymbol(newAttValue);
            }
            return attValueHandle;
        }
        //
        //
        //
        boolean invalidStandaloneAttDef(int elementType, int attrName) {
            if (fStandaloneReader == -1)
                return false;
            if (elementType == -1) // we are normalizing a default att value...  this ok?
                return false;
            return getAttDefIsExternal(elementType, attrName);
        }
    }

  //====================================================
  // End AttributeValidator Interface and Implementations
  //====================================================


    public void setRootElementType(int rootElementType) {
        fRootElementType = rootElementType;
    }

    private void rootElementSpecified(int rootElementType) throws Exception {
        if (fDynamicValidation && !fSeenDoctypeDecl) {
            fValidating = false;
        }
        if (fValidating) {
            if (fRootElementType != -1) {
                String root1 = fStringPool.toString(fRootElementType);
                String root2 = fStringPool.toString(rootElementType);
                if (!root1.equals(root2)) {
                    reportRecoverableXMLError(XMLMessages.MSG_ROOT_ELEMENT_TYPE,
                                                XMLMessages.VC_ROOT_ELEMENT_TYPE,
                                                fRootElementType, rootElementType);
                }
            }
        }
        if (fNamespacesEnabled) {
            if (fNamespacesScope == null) {
                fNamespacesScope = new NamespacesScope(this);
                fNamespacesPrefix = fStringPool.addSymbol("xmlns");
                fNamespacesScope.setNamespaceForPrefix(fNamespacesPrefix, -1);
                int xmlSymbol = fStringPool.addSymbol("xml");
                int xmlNamespace = fStringPool.addSymbol("http://www.w3.org/XML/1998/namespace");
                fNamespacesScope.setNamespaceForPrefix(xmlSymbol, xmlNamespace);
            }
        }
    }


  //
  // validateElementAndAttributes
  //

    private void validateElementAndAttributes(int elementType, XMLAttrList attrList) throws Exception {

    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.validateElementAndAttributes: " + param("elementType",elementType) + " !!!!\n");
    //****DEBUG****

        if (fElementDeclCount == 0 && fAttDefCount == 0 && !fValidating && !fNamespacesEnabled) {
            fCurrentElementIndex = -1;
            fCurrentContentSpecType = -1;
            fInElementContent = false;
            if (fAttrListHandle != -1) {
                fAttrList.endAttrList();
                int index = fAttrList.getFirstAttr(fAttrListHandle);
                while (index != -1) {
                    if (fStringPool.equalNames(fAttrList.getAttrName(index), fXMLLang)) {
                        fDocumentScanner.checkXMLLangAttributeValue(fAttrList.getAttValue(index));
                        break;
                    }
                    index = fAttrList.getNextAttr(index);
                }
            }
            return;
        }
        int elementIndex = fStringPool.getDeclaration(elementType);
        int contentSpecType = (elementIndex == -1) ? -1 : getContentSpecType(elementIndex);
        if (contentSpecType == -1 && fValidating) {
            reportRecoverableXMLError(XMLMessages.MSG_ELEMENT_NOT_DECLARED,
                                      XMLMessages.VC_ELEMENT_VALID,
                                      elementType);
        }
        if (fAttDefCount != 0 && elementIndex != -1) {
            fAttrListHandle = addDefaultAttributes(elementIndex, attrList, fAttrListHandle, fValidating, fStandaloneReader != -1);
        }
        if (fAttrListHandle != -1) {
            fAttrList.endAttrList();
        }

        if (DEBUG_PRINT_ATTRIBUTES) {
            String element = fStringPool.toString(elementType);
            System.out.print("startElement: <" + element);
            if (fAttrListHandle != -1) {
                int index = attrList.getFirstAttr(fAttrListHandle);
                while (index != -1) {
                    System.out.print(" " + fStringPool.toString(attrList.getAttrName(index)) + "=\"" +
                            fStringPool.toString(attrList.getAttValue(index)) + "\"");
                    index = attrList.getNextAttr(index);
                }
            }
            System.out.println(">");
        }
        //
        // Namespace support
        //
        if (fNamespacesEnabled) {
            fNamespacesScope.increaseDepth();
            if (fAttrListHandle != -1) {
                int index = attrList.getFirstAttr(fAttrListHandle);
                while (index != -1) {
                    int attName = attrList.getAttrName(index);
                    if (fStringPool.equalNames(attName, fXMLLang)) {
                        fDocumentScanner.checkXMLLangAttributeValue(attrList.getAttValue(index));
                    } else if (fStringPool.equalNames(attName, fNamespacesPrefix)) {
                        int uri = fStringPool.addSymbol(attrList.getAttValue(index));
                        fNamespacesScope.setNamespaceForPrefix(StringPool.EMPTY_STRING, uri);
                    } else {
                        int attPrefix = fStringPool.getPrefixForQName(attName);
                        if (attPrefix == fNamespacesPrefix) {
                            attPrefix = fStringPool.getLocalPartForQName(attName);
                            int uri = fStringPool.addSymbol(attrList.getAttValue(index));
                            fNamespacesScope.setNamespaceForPrefix(attPrefix, uri);
                        }
                    }
                    index = attrList.getNextAttr(index);
                }
            }
            int prefix = fStringPool.getPrefixForQName(elementType);
            int elementURI;
            if (prefix == -1) {
                elementURI = fNamespacesScope.getNamespaceForPrefix(StringPool.EMPTY_STRING);
                if (elementURI != -1) {
                    fStringPool.setURIForQName(elementType, elementURI);
                }
            } else {
                elementURI = fNamespacesScope.getNamespaceForPrefix(prefix);
                if (elementURI == -1) {
                    Object[] args = { fStringPool.toString(prefix) };
                    fErrorReporter.reportError(fErrorReporter.getLocator(),
                                               XMLMessages.XMLNS_DOMAIN,
                                               XMLMessages.MSG_PREFIX_DECLARED,
                                               XMLMessages.NC_PREFIX_DECLARED,
                                               args,
                                               XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
                }
                fStringPool.setURIForQName(elementType, elementURI);
            }
            if (fAttrListHandle != -1) {
                int index = attrList.getFirstAttr(fAttrListHandle);
                while (index != -1) {
                    int attName = attrList.getAttrName(index);
                    if (!fStringPool.equalNames(attName, fNamespacesPrefix)) {
                        int attPrefix = fStringPool.getPrefixForQName(attName);
                        if (attPrefix != fNamespacesPrefix) {
                            if (attPrefix != -1) {
                                int uri = fNamespacesScope.getNamespaceForPrefix(attPrefix);
                                if (uri == -1) {
                                    Object[] args = { fStringPool.toString(attPrefix) };
                                    fErrorReporter.reportError(fErrorReporter.getLocator(),
                                                               XMLMessages.XMLNS_DOMAIN,
                                                               XMLMessages.MSG_PREFIX_DECLARED,
                                                               XMLMessages.NC_PREFIX_DECLARED,
                                                               args,
                                                               XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
                                }
                                fStringPool.setURIForQName(attName, uri);
                            }
                        }
                    }
                    index = attrList.getNextAttr(index);
                }
            }
        } else if (fAttrListHandle != -1) {
            int index = fAttrList.getFirstAttr(fAttrListHandle);
            while (index != -1) {
                if (fStringPool.equalNames(attrList.getAttrName(index), fXMLLang)) {
                    fDocumentScanner.checkXMLLangAttributeValue(attrList.getAttValue(index));
                    break;
                }
                index = fAttrList.getNextAttr(index);
            }
        }
        fCurrentElementIndex = elementIndex;
        fCurrentContentSpecType = contentSpecType;
        if (fValidating && contentSpecType == fDATATYPESymbol) {
            fBufferDatatype = true;
            fDatatypeBuffer.setLength(0);
        }
        fInElementContent = (contentSpecType == fCHILDRENSymbol);
    }


    //
    // charDataInContent
    //

    private void charDataInContent () {

    //****DEBUG****
    if (DEBUG) print("(???) XMLValidator.charDataInContent\n");
    //****DEBUG****

        int[] children = fElementChildren[fElementDepth];
        int childCount = fElementChildCount[fElementDepth];
        try {
            children[childCount] = -1;
        } catch (NullPointerException ex) {
            children = fElementChildren[fElementDepth] = new int[256];
            childCount = 0; // should really assert this...
            children[childCount] = -1;
        } catch (ArrayIndexOutOfBoundsException ex) {
            int[] newChildren = new int[childCount * 2];
            System.arraycopy(children, 0, newChildren, 0, childCount);
            children = fElementChildren[fElementDepth] = newChildren;
            children[childCount] = -1;
        }
        fElementChildCount[fElementDepth] = ++childCount;
    }


  //
  // peekChildCount
  //

    private int peekChildCount() {

    //****DEBUG****
    if (DEBUG) print("(???) XMLValidator.peekChildCount\n");
    //****DEBUG****

        return fElementChildCount[fElementDepth];
    }


  //
  // peekChildren
  //

    private int[] peekChildren() {

    //****DEBUG****
    if (DEBUG) print("(???) XMLValidator.peekChildren\n");
    //****DEBUG****

        return fElementChildren[fElementDepth];
    }

    /**
     * Check that the content of an element is valid.
     * <p>
     * This is the method of primary concern to the validator. This method is called
     * upon the scanner reaching the end tag of an element. At that time, the
     * element's children must be structurally validated, so it calls this method.
     * The index of the element being checked (in the decl pool), is provided as
     * well as an array of element name indexes of the children. The validator must
     * confirm that this element can have these children in this order.
     * <p>
     * This can also be called to do 'what if' testing of content models just to see
     * if they would be valid.
     * <p>
     * Note that the element index is an index into the element decl pool, whereas
     * the children indexes are name indexes, i.e. into the string pool.
     * <p>
     * A value of -1 in the children array indicates a PCDATA node. All other
     * indexes will be positive and represent child elements. The count can be
     * zero, since some elements have the EMPTY content model and that must be
     * confirmed.
     *
     * @param elementIndex The index within the <code>ElementDeclPool</code> of this
     *                     element.
     * @param childCount The number of entries in the <code>children</code> array.
     * @param children The children of this element.  Each integer is an index within
     *                 the <code>StringPool</code> of the child element name.  An index
     *                 of -1 is used to indicate an occurrence of non-whitespace character
     *                 data.
     *
     * @return The value -1 if fully valid, else the 0 based index of the child
     *         that first failed. If the value returned is equal to the number
     *         of children, then additional content is required to reach a valid
     *         ending state.
     *
     * @exception Exception Thrown on error.
     */

    private int checkContent(int elementIndex, int childCount, int[] children) throws Exception
    {
    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.checkContent: " + param("elementIndex",elementIndex) + "... \n");
    //****DEBUG****

        // Get the element name index from the element
        final int elementType = fCurrentElementType;

        if (DEBUG_PRINT_CONTENT)
        {
            String strTmp = fStringPool.toString(elementType);
            System.out.println
            (
                "Name: "
                + strTmp
                + ", Count: "
                + childCount
                + ", ContentSpec: "
                + getContentSpecAsString(elementIndex)
            );
            for (int index = 0; index < childCount && index < 10; index++) {
                if (index == 0) System.out.print("  (");
                String childName = (children[index] == -1) ? "#PCDATA" : fStringPool.toString(children[index]);
                if (index + 1 == childCount)
                    System.out.println(childName + ")");
                else if (index + 1 == 10)
                    System.out.println(childName + ",...)");
                else
                    System.out.print(childName + ",");
            }
        }

        // Get out the content spec for this element
        final int contentSpec = fCurrentContentSpecType;

        //
        //  Deal with the possible types of content. We try to optimized here
        //  by dealing specially with content models that don't require the
        //  full DFA treatment.
        //
        if (contentSpec == fEMPTYSymbol)
        {
            //
            //  If the child count is greater than zero, then this is
            //  an error right off the bat at index 0.
            //
            if (childCount != 0)
                return 0;
        }
         else if (contentSpec == fANYSymbol)
        {
            //
            //  This one is open game so we don't pass any judgement on it
            //  at all. Its assumed to fine since it can hold anything.
            //
        }
         else if (contentSpec == fMIXEDSymbol ||  contentSpec == fCHILDRENSymbol)
        {
            // Get the content model for this element, faulting it in if needed
            XMLContentModel cmElem = null;
            try
            {
                cmElem = getContentModel(elementIndex);
                return cmElem.validateContent(childCount, children);
            }

            catch(CMException excToCatch)
            {
                // REVISIT - Translate the caught exception to the protected error API
                int majorCode = excToCatch.getErrorCode();
                fErrorReporter.reportError(fErrorReporter.getLocator(),
                                           ImplementationMessages.XERCES_IMPLEMENTATION_DOMAIN,
                                           majorCode,
                                           0,
                                           null,
                                           XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
            }
        }
         else if (contentSpec == -1)
        {
            reportRecoverableXMLError(XMLMessages.MSG_ELEMENT_NOT_DECLARED,
                                      XMLMessages.VC_ELEMENT_VALID,
                                      elementType);
        }
         else if (contentSpec == fStringPool.addSymbol("DATATYPE"))
        {

            XMLContentModel cmElem = null;
            try {
                cmElem = getContentModel(elementIndex);
                return cmElem.validateContent(1, new int[] { fStringPool.addString(fDatatypeBuffer.toString()) });
            } catch (CMException cme) {
                System.out.println("Internal Error in datatype validation");
            } catch (InvalidDatatypeValueException idve) {
                fErrorReporter.reportError(fErrorReporter.getLocator(),
                                           SchemaMessageProvider.SCHEMA_DOMAIN,
                                           SchemaMessageProvider.DatatypeError,
                                           SchemaMessageProvider.MSG_NONE,
                                           new Object [] { idve.getMessage() },
                                           XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
            }
/*
            boolean DEBUG_DATATYPES = false;
            if (DEBUG_DATATYPES) {
                System.out.println("Checking content of datatype");
                String strTmp = fStringPool.toString(elementTypeIndex);
                int contentSpecIndex = fElementDeclPool.getContentSpec(elementIndex);
                XMLContentSpec.Node csn = new XMLContentSpec.Node();
                fElementDeclPool.getContentSpecNode(contentSpecIndex, csn);
                String contentSpecString = fStringPool.toString(csn.value);
                System.out.println
                (
                    "Name: "
                    + strTmp
                    + ", Count: "
                    + childCount
                    + ", ContentSpec: "
                    + contentSpecString
                );
                for (int index = 0; index < childCount && index < 10; index++) {
                    if (index == 0) System.out.print("  (");
                    String childName = (children[index] == -1) ? "#PCDATA" : fStringPool.toString(children[index]);
                    if (index + 1 == childCount)
                        System.out.println(childName + ")");
                    else if (index + 1 == 10)
                        System.out.println(childName + ",...)");
                    else
                        System.out.print(childName + ",");
                }
            }
            try { // REVISIT - integrate w/ error handling
                int contentSpecIndex = fElementDeclPool.getContentSpec(elementIndex);
                XMLContentSpec.Node csn = new XMLContentSpec.Node();
                fElementDeclPool.getContentSpecNode(contentSpecIndex, csn);
                String type = fStringPool.toString(csn.value);
                DatatypeValidator v = fDatatypeRegistry.getValidatorFor(type);
                if (v != null)
                    v.validate(fDatatypeBuffer.toString());
                else
                    System.out.println("No validator for datatype "+type);
            } catch (InvalidDatatypeValueException idve) {
                System.out.println("Incorrect datatype: "+idve.getMessage());
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("Internal error in datatype validation");
            }
*/
        }
         else
        {
            fErrorReporter.reportError(fErrorReporter.getLocator(),
                                       ImplementationMessages.XERCES_IMPLEMENTATION_DOMAIN,
                                       ImplementationMessages.VAL_CST,
                                       0,
                                       null,
                                       XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
        }

        // We succeeded
        return -1;
    }

    /**
     * Returns information about which elements can be placed at a particular point
     * in the passed element's content model.
     * <p>
     * Note that the incoming content model to test must be valid at least up to
     * the insertion point. If not, then -1 will be returned and the info object
     * will not have been filled in.
     * <p>
     * If, on return, the info.isValidEOC flag is set, then the 'insert after'
     * elemement is a valid end of content, i.e. nothing needs to be inserted
     * after it to make the parent element's content model valid.
     *
     * @param elementIndex The index within the <code>ElementDeclPool</code> of the
     *                     element which is being querying.
     * @param fullyValid Only return elements that can be inserted and still
     *                   maintain the validity of subsequent elements past the
     *                   insertion point (if any).  If the insertion point is at
     *                   the end, and this is true, then only elements that can
     *                   be legal final states will be returned.
     * @param info An object that contains the required input data for the method,
     *             and which will contain the output information if successful.
     *
     * @return The value -1 if fully valid, else the 0 based index of the child
     *         that first failed before the insertion point. If the value
     *         returned is equal to the number of children, then the specified
     *         children are valid but additional content is required to reach a
     *         valid ending state.
     *
     * @exception Exception Thrown on error.
     *
     * @see InsertableElementsInfo
     */
    protected int whatCanGoHere(int elementIndex,boolean fullyValid,InsertableElementsInfo info) throws Exception
    {

    //****DEBUG****
    if (DEBUG) print("(VAL) XMLValidator.whatCanGoHere: ...\n");
    //****DEBUG****

        //
        //  Do some basic sanity checking on the info packet. First, make sure
        //  that insertAt is not greater than the child count. It can be equal,
        //  which means to get appendable elements, but not greater. Or, if
        //  the current children array is null, that's bad too.
        //
        //  Since the current children array must have a blank spot for where
        //  the insert is going to be, the child count must always be at least
        //  one.
        //
        //  Make sure that the child count is not larger than the current children
        //  array. It can be equal, which means get appendable elements, but not
        //  greater.
        //
        if ((info.insertAt > info.childCount)
        ||  (info.curChildren == null)
        ||  (info.childCount < 1)
        ||  (info.childCount > info.curChildren.length))
        {
            fErrorReporter.reportError(fErrorReporter.getLocator(),
                                       ImplementationMessages.XERCES_IMPLEMENTATION_DOMAIN,
                                       ImplementationMessages.VAL_WCGHI,
                                       0,
                                       null,
                                       XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
        }

        int retVal = 0;
        try
        {
            // Get the content model for this element
            final XMLContentModel cmElem = getContentModel(elementIndex);

            // And delegate this call to it
            retVal = cmElem.whatCanGoHere(fullyValid, info);
        }

        catch(CMException excToCatch)
        {
            // REVISIT - Translate caught error to the protected error handler interface
            int majorCode = excToCatch.getErrorCode();
            fErrorReporter.reportError(fErrorReporter.getLocator(),
                                       ImplementationMessages.XERCES_IMPLEMENTATION_DOMAIN,
                                       majorCode,
                                       0,
                                       null,
                                       XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
        }
        return retVal;
    }


    // -----------------------------------------------------------------------
    //  Private methods
    // -----------------------------------------------------------------------

    //
    //  When the element has a 'CHILDREN' model, this method is called to
    //  create the content model object. It looks for some special case simple
    //  models and creates SimpleContentModel objects for those. For the rest
    //  it creates the standard DFA style model.
    //
    private XMLContentModel createChildModel(int elementIndex) throws CMException
    {
    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.createChildModel: " + param("elementIndex",elementIndex) + "\n");
    //****DEBUG****

        //
        //  Get the content spec node for the element we are working on.
        //  This will tell us what kind of node it is, which tells us what
        //  kind of model we will try to create.
        //
        XMLContentSpec.Node specNode = new XMLContentSpec.Node();
        int contentSpecIndex = getContentSpecHandle(elementIndex);
        getContentSpecNode(contentSpecIndex, specNode);

        //
        //  Check that the left value is not -1, since any content model
        //  with PCDATA should be MIXED, so we should not have gotten here.
        //
        if (specNode.value == -1)
            throw new CMException(ImplementationMessages.VAL_NPCD);

        if (specNode.type == XMLContentSpec.CONTENTSPECNODE_LEAF)
        {
            //
            //  Its a single leaf, so its an 'a' type of content model, i.e.
            //  just one instance of one element. That one is definitely a
            //  simple content model.
            //
            return new SimpleContentModel(specNode.value, -1, specNode.type);
        }
         else if ((specNode.type == XMLContentSpec.CONTENTSPECNODE_CHOICE)
              ||  (specNode.type == XMLContentSpec.CONTENTSPECNODE_SEQ))
        {
            //
            //  Lets see if both of the children are leafs. If so, then it
            //  it has to be a simple content model
            //
            XMLContentSpec.Node specLeft = new XMLContentSpec.Node();
            XMLContentSpec.Node specRight = new XMLContentSpec.Node();
            getContentSpecNode(specNode.value, specLeft);
            getContentSpecNode(specNode.otherValue, specRight);

            if ((specLeft.type == XMLContentSpec.CONTENTSPECNODE_LEAF)
            &&  (specRight.type == XMLContentSpec.CONTENTSPECNODE_LEAF))
            {
                //
                //  Its a simple choice or sequence, so we can do a simple
                //  content model for it.
                //
                return new SimpleContentModel
                (
                    specLeft.value
                    , specRight.value
                    , specNode.type
                );
            }
        }
         else if ((specNode.type == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE)
              ||  (specNode.type == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE)
              ||  (specNode.type == XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE))
        {
            //
            //  Its a repetition, so see if its one child is a leaf. If so
            //  its a repetition of a single element, so we can do a simple
            //  content model for that.
            //
            XMLContentSpec.Node specLeft = new XMLContentSpec.Node();
            getContentSpecNode(specNode.value, specLeft);

            if (specLeft.type == XMLContentSpec.CONTENTSPECNODE_LEAF)
            {
                //
                //  It is, so we can create a simple content model here that
                //  will check for this repetition. We pass -1 for the unused
                //  right node.
                //
                return new SimpleContentModel(specLeft.value, -1, specNode.type);
            }
        }
         else
        {
            throw new CMException(ImplementationMessages.VAL_CST);
        }

        //
        //  Its not a simple content model, so here we have to create a DFA
        //  for this element. So we create a DFAContentModel object. He
        //  encapsulates all of the work to create the DFA.
        //
        fLeafCount = 0;
        CMNode cmn = buildSyntaxTree(contentSpecIndex, specNode);
        return new DFAContentModel
        (
            fStringPool
            , cmn
            , fLeafCount
        );
    }


    //
    //  This method will handle the querying of the content model for a
    //  particular element. If the element does not have a content model, then
    //  it will be created.
    //
    private XMLContentModel getContentModel(int elementIndex) throws CMException
    {
    //****DEBUG****
    if (DEBUG) print("(INF) XMLValidator.getContentModel: " + param("elementIndex",elementIndex) + "\n");
    //****DEBUG****

        // See if a content model already exists first
        XMLContentModel cmRet = getElementContentModel(elementIndex);

        // If we have one, just return that. Otherwise, gotta create one
        if (cmRet != null)
            return cmRet;

        // Get the type of content this element has
        final int contentSpec = getContentSpecType(elementIndex);

        // And create the content model according to the spec type
        if (contentSpec == fMIXEDSymbol)
        {
            //
            //  Just create a mixel content model object. This type of
            //  content model is optimized for mixed content validation.
            //
            XMLContentSpec.Node specNode = new XMLContentSpec.Node();
            int contentSpecIndex = getContentSpecHandle(elementIndex);
            makeContentList(contentSpecIndex, specNode);
            cmRet = new MixedContentModel(fCount, fContentList);
        }
         else if (contentSpec == fCHILDRENSymbol)
        {
            //
            //  This method will create an optimal model for the complexity
            //  of the element's defined model. If its simple, it will create
            //  a SimpleContentModel object. If its a simple list, it will
            //  create a SimpleListContentModel object. If its complex, it
            //  will create a DFAContentModel object.
            //
            cmRet = createChildModel(elementIndex);
        }
         else if (contentSpec == fDATATYPESymbol)
        {
            cmRet = fSchemaImporter.createDatatypeContentModel(elementIndex);
        }
         else
        {
            throw new CMException(ImplementationMessages.VAL_CST);
        }

        // Add the new model to the content model for this element
        setContentModel(elementIndex, cmRet);

        return cmRet;
    }
    //
    //
    //
    protected void reportRecoverableXMLError(int majorCode, int minorCode) throws Exception {
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   null,
                                   XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
    }
    protected void reportRecoverableXMLError(int majorCode, int minorCode, int stringIndex1) throws Exception {
        Object[] args = { fStringPool.toString(stringIndex1) };
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   args,
                                   XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
    }
    protected void reportRecoverableXMLError(int majorCode, int minorCode, String string1) throws Exception {
        Object[] args = { string1 };
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   args,
                                   XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
    }
    protected void reportRecoverableXMLError(int majorCode, int minorCode, int stringIndex1, int stringIndex2) throws Exception {
        Object[] args = { fStringPool.toString(stringIndex1), fStringPool.toString(stringIndex2) };
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   args,
                                   XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
    }
    protected void reportRecoverableXMLError(int majorCode, int minorCode, String string1, String string2) throws Exception {
        Object[] args = { string1, string2 };
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   args,
                                   XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
    }
    protected void reportRecoverableXMLError(int majorCode, int minorCode, String string1, String string2, String string3) throws Exception {
        Object[] args = { string1, string2, string3 };
        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                   XMLMessages.XML_DOMAIN,
                                   majorCode,
                                   minorCode,
                                   args,
                                   XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
    }

    //
    //
    //
    private boolean usingStandaloneReader() {
        return fStandaloneReader == -1 || fEntityHandler.getReaderId() == fStandaloneReader;
    }


  //
  // getLocatorImpl
  //

    private LocatorImpl getLocatorImpl(LocatorImpl fillin) {

    //****DEBUG****
    if (DEBUG) print("(INF) XMLValidator.getLocatorImpl: ...\n");
    //****DEBUG****

        Locator here = fErrorReporter.getLocator();
        if (fillin == null)
            return new LocatorImpl(here);
        fillin.setPublicId(here.getPublicId());
        fillin.setSystemId(here.getSystemId());
        fillin.setLineNumber(here.getLineNumber());
        fillin.setColumnNumber(here.getColumnNumber());
        return fillin;
    }

    //
    //  This method will build our syntax tree by recursively going though
    //  the element's content model and creating new CMNode type node for
    //  the model, and rewriting '?' and '+' nodes along the way.
    //
    //  On final return, the head node of the syntax tree will be returned.
    //  This top node will be a sequence node with the left side being the
    //  rewritten content, and the right side being a special end of content
    //  node.
    //
    //  We also count the non-epsilon leaf nodes, which is an important value
    //  that is used in a number of places later.
    //

    private int fLeafCount = 0;
    private CMNode buildSyntaxTree(int startNode, XMLContentSpec.Node specNode) throws CMException
    {
    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.buildSyntaxTree: ... \n");
    //****DEBUG****

        // We will build a node at this level for the new tree
        CMNode nodeRet = null;

        getContentSpecNode(startNode, specNode);

        //
        //  If this node is a leaf, then its an easy one. We just add it
        //  to the tree.
        //
        if (specNode.type == XMLContentSpec.CONTENTSPECNODE_LEAF)
        {
            //
            //  Create a new leaf node, and pass it the current leaf count,
            //  which is its DFA state position. Bump the leaf count after
            //  storing it. This makes the positions zero based since we
            //  store first and then increment.
            //
            nodeRet = new CMLeaf(specNode.type, specNode.value, fLeafCount++);
        }
         else
        {
            //
            //  Its not a leaf, so we have to recurse its left and maybe right
            //  nodes. Save both values before we recurse and trash the node.
            //
            final int leftNode = specNode.value;
            final int rightNode = specNode.otherValue;

            if ((specNode.type == XMLContentSpec.CONTENTSPECNODE_CHOICE)
            ||  (specNode.type == XMLContentSpec.CONTENTSPECNODE_SEQ))
            {
                //
                //  Recurse on both children, and return a binary op node
                //  with the two created sub nodes as its children. The node
                //  type is the same type as the source.
                //

                nodeRet = new CMBinOp
                (
                    specNode.type
                    , buildSyntaxTree(leftNode, specNode)
                    , buildSyntaxTree(rightNode, specNode)
                );
            }
             else if (specNode.type == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE)
            {
                // This one is fine as is, just change to our form
                nodeRet = new CMUniOp
                (
                    specNode.type
                    , buildSyntaxTree(leftNode, specNode)
                );
            }
             else if (specNode.type == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE)
            {
                // Convert to (x|epsilon)
                nodeRet = new CMBinOp
                (
                    XMLContentSpec.CONTENTSPECNODE_CHOICE
                    , buildSyntaxTree(leftNode, specNode)
                    , new CMLeaf(XMLContentSpec.CONTENTSPECNODE_LEAF, fEpsilonIndex)
                );
            }
             else if (specNode.type == XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE)
            {
                // Convert to (x,x*)
                nodeRet = new CMBinOp
                (
                    XMLContentSpec.CONTENTSPECNODE_SEQ
                    , buildSyntaxTree(leftNode, specNode)
                    , new CMUniOp
                      (
                        XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE
                        , buildSyntaxTree(leftNode, specNode)
                      )
                );
            }
             else
            {
                throw new CMException(ImplementationMessages.VAL_CST);
            }
        }

        // And return our new node for this level
        return nodeRet;
    }

    private int fCount = 0;
    private int[] fContentList = new int[64];
    private void makeContentList(int startNode, XMLContentSpec.Node specNode) throws CMException {

    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.makeContentList: ...\n");
    //****DEBUG****

        //
        //  Ok, we need to build up an array of the possible children
        //  under this element. The mixed content model can only be a
        //  repeated series of alternations with no numeration or ordering.
        //  So we call a local recursive method to iterate the tree and
        //  build up the array.
        //
        //  So we get the content spec of the element, which gives us the
        //  starting node. Everything else kicks off from there. We pass
        //  along a content node for each iteration to use so that it does
        //  not have to create and trash lots of objects.
        //
        while (true)
        {
            fCount = 0;

            try
            {
                fCount = buildContentList
                (
                    startNode
                    , 0
                    , specNode
                );
            }

            catch(IndexOutOfBoundsException excToCatch)
            {
                //
                //  Expand the array and try it again. Yes, this is
                //  piggy, but the odds of it ever actually happening
                //  are slim to none.
                //
                fContentList = new int[fContentList.length * 2];
                fCount = 0;
                continue;
            }

            // We survived, so break out
            break;
        }
    }
    private int buildContentList( int startNode, int count, XMLContentSpec.Node specNode) throws CMException
    {
    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.buildContentList: ...\n");
    //****DEBUG****

        // Get the content spec for the passed start node
        getContentSpecNode(startNode, specNode);

        // If this node is a leaf, then add it to our list and return.
        if (specNode.type == XMLContentSpec.CONTENTSPECNODE_LEAF)
        {
            fContentList[count++] = specNode.value;
            return count;
        }

        //
        //  Its not a leaf, so we have to recurse its left and maybe right
        //  nodes. Save both values before we recurse and trash the node.
        //
        final int leftNode = specNode.value;
        final int rightNode = specNode.otherValue;

        if ((specNode.type == XMLContentSpec.CONTENTSPECNODE_CHOICE)
        ||  (specNode.type == XMLContentSpec.CONTENTSPECNODE_SEQ))
        {
            //
            //  Recurse on the left and right nodes of this guy, making sure
            //  to keep the count correct.
            //
            count = buildContentList(leftNode, count, specNode);
            count = buildContentList(rightNode, count, specNode);
        }
         else if ((specNode.type == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE)
              ||  (specNode.type == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE)
              ||  (specNode.type == XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE))
        {
            // Just do the left node on this one
            count = buildContentList(leftNode, count, specNode);
        }
         else
        {
            throw new CMException(ImplementationMessages.VAL_CST);
        }

        // And return our accumlated new count
        return count;
    }

    //
    // Chunk size constants
    //
    private static final int CHUNK_SHIFT = 8;           // 2^8 = 256
    private static final int CHUNK_SIZE = (1 << CHUNK_SHIFT);
    private static final int CHUNK_MASK = CHUNK_SIZE - 1;
    private static final int INITIAL_CHUNK_COUNT = (1 << (10 - CHUNK_SHIFT));   // 2^10 = 1k
    //
    // Instance variables
    //
    //
    // Element list
    //
    private int fElementCount = 0;
    private int[][] fElementType = new int[INITIAL_CHUNK_COUNT][];
    private byte[][] fElementDeclIsExternal = new byte[INITIAL_CHUNK_COUNT][];
    private int[][] fContentSpecType = new int[INITIAL_CHUNK_COUNT][];
    private int[][] fContentSpec = new int[INITIAL_CHUNK_COUNT][];
    private XMLContentModel[][] fContentModel = new XMLContentModel[INITIAL_CHUNK_COUNT][];
    private int[][] fAttlistHead = new int[INITIAL_CHUNK_COUNT][];
    private int[][] fAttlistTail = new int[INITIAL_CHUNK_COUNT][];
    //
    // ContentSpecNode list
    //
    private int fNodeCount = 0;
    private byte[][] fNodeType = new byte[INITIAL_CHUNK_COUNT][];
    private int[][] fNodeValue = new int[INITIAL_CHUNK_COUNT][];
    //
    // AttDef list
    //
    private int fAttDefCount = 0;
    private int[][] fAttName = new int[INITIAL_CHUNK_COUNT][];
    private int[][] fAttType = new int[INITIAL_CHUNK_COUNT][];
    private AttributeValidator[][] fAttValidator = new AttributeValidator[INITIAL_CHUNK_COUNT][];
    private int[][] fEnumeration = new int[INITIAL_CHUNK_COUNT][];
    private int[][] fAttDefaultType = new int[INITIAL_CHUNK_COUNT][];
    private int[][] fAttValue = new int[INITIAL_CHUNK_COUNT][];
    private byte[][] fAttDefIsExternal = new byte[INITIAL_CHUNK_COUNT][];
    private int[][] fNextAttDef = new int[INITIAL_CHUNK_COUNT][];
    //
    //
    //
    private Hashtable fIdDefs = null;
    private Hashtable fIdRefs = null;
    private Object fNullValue = null;
    private AttributeValidator fAttValidatorCDATA = null;
    private AttributeValidator fAttValidatorID = null;
    private AttributeValidator fAttValidatorIDREF = null;
    private AttributeValidator fAttValidatorIDREFS = null;
    private AttributeValidator fAttValidatorENTITY = null;
    private AttributeValidator fAttValidatorENTITIES = null;
    private AttributeValidator fAttValidatorNMTOKEN = null;
    private AttributeValidator fAttValidatorNMTOKENS = null;
    private AttributeValidator fAttValidatorNOTATION = null;
    private AttributeValidator fAttValidatorENUMERATION = null;
    private AttributeValidator fAttValidatorDATATYPE = null;
    //
    //
    //
    private void poolReset() {
        int chunk = 0;
        int index = 0;
        for (int i = 0; i < fElementCount; i++) {
            fContentModel[chunk][index] = null;
            if (++index == CHUNK_SIZE) {
                chunk++;
                index = 0;
            }
        }
        fElementCount = 0;
        fNodeCount = 0;
        fAttDefCount = 0;
        if (fIdDefs != null)
            fIdDefs.clear();
        if (fIdRefs != null)
            fIdRefs.clear();
    }
    //
    // Element entries
    //
    private boolean ensureElementCapacity(int chunk) {
        try {
            return fElementType[chunk][0] == 0;
        } catch (ArrayIndexOutOfBoundsException ex) {
            byte[][] newByteArray = new byte[chunk * 2][];
            System.arraycopy(fElementDeclIsExternal, 0, newByteArray, 0, chunk);
            fElementDeclIsExternal = newByteArray;
            int[][] newIntArray = new int[chunk * 2][];
            System.arraycopy(fElementType, 0, newIntArray, 0, chunk);
            fElementType = newIntArray;
            newIntArray = new int[chunk * 2][];
            System.arraycopy(fContentSpecType, 0, newIntArray, 0, chunk);
            fContentSpecType = newIntArray;
            newIntArray = new int[chunk * 2][];
            System.arraycopy(fContentSpec, 0, newIntArray, 0, chunk);
            fContentSpec = newIntArray;
            XMLContentModel[][] newContentModel = new XMLContentModel[chunk * 2][];
            System.arraycopy(fContentModel, 0, newContentModel, 0, chunk);
            fContentModel = newContentModel;
            newIntArray = new int[chunk * 2][];
            System.arraycopy(fAttlistHead, 0, newIntArray, 0, chunk);
            fAttlistHead = newIntArray;
            newIntArray = new int[chunk * 2][];
            System.arraycopy(fAttlistTail, 0, newIntArray, 0, chunk);
            fAttlistTail = newIntArray;
        } catch (NullPointerException ex) {
        }
        fElementType[chunk] = new int[CHUNK_SIZE];
        fElementDeclIsExternal[chunk] = new byte[CHUNK_SIZE];
        fContentSpecType[chunk] = new int[CHUNK_SIZE];
        fContentSpec[chunk] = new int[CHUNK_SIZE];
        fContentModel[chunk] = new XMLContentModel[CHUNK_SIZE];
        fAttlistHead[chunk] = new int[CHUNK_SIZE];
        fAttlistTail[chunk] = new int[CHUNK_SIZE];
        return true;
    }

 
  //
  // addElement
  //

    public int addElement(int elementType) {

    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.addElement: " + param("elementType",elementType) + "\n");
    //****DEBUG****

        int elementIndex = fStringPool.getDeclaration(elementType);
        if (elementIndex != -1)
            return elementIndex;
        int chunk = fElementCount >> CHUNK_SHIFT;
        int index = fElementCount & CHUNK_MASK;
        ensureElementCapacity(chunk);
        fElementType[chunk][index] = elementType;
        fElementDeclIsExternal[chunk][index] = 0;
        fContentSpecType[chunk][index] = -1;
        fContentSpec[chunk][index] = -1;
        fContentModel[chunk][index] = null;
        fAttlistHead[chunk][index] = -1;
        fAttlistTail[chunk][index] = -1;
        fStringPool.setDeclaration(elementType, fElementCount);
        return fElementCount++;
    }


  //
  // addElementDecl
  //

    public int addElementDecl(int elementType, int contentSpecType, int contentSpec, boolean isExternal) {

    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.addElementDecl: " + param("elementType",elementType) + "\n");
    //****DEBUG****

    //System.out.println("Pool " + this + " add " + decl.elementType + " (" + fStringPool.toString(decl.elementType) + ")");
       
    int elementIndex = fStringPool.getDeclaration(elementType);
        if (elementIndex != -1) {
            int chunk = elementIndex >> CHUNK_SHIFT;
            int index = elementIndex & CHUNK_MASK;
            if (fContentSpecType[chunk][index] != -1)
                return -1;
            fElementDeclIsExternal[chunk][index] = (byte)(isExternal ? 1 : 0);
            fContentSpecType[chunk][index] = contentSpecType;
            fContentSpec[chunk][index] = contentSpec;
            fContentModel[chunk][index] = null;
            fElementDeclCount++;
            return elementIndex;
        }
        int chunk = fElementCount >> CHUNK_SHIFT;
        int index = fElementCount & CHUNK_MASK;
        ensureElementCapacity(chunk);
        fElementType[chunk][index] = elementType;
        fElementDeclIsExternal[chunk][index] = (byte)(isExternal ? 1 : 0);
        fContentSpecType[chunk][index] = contentSpecType;
        fContentSpec[chunk][index] = contentSpec;
        fContentModel[chunk][index] = null;
        fAttlistHead[chunk][index] = -1;
        fAttlistTail[chunk][index] = -1;
        fStringPool.setDeclaration(elementType, fElementCount);
        fElementDeclCount++;
        return fElementCount++;
    }

    public int getElementType(int elementIndex) {
        if (elementIndex < 0 || elementIndex >= fElementCount)
            return -1;
        int chunk = elementIndex >> CHUNK_SHIFT;
        int index = elementIndex & CHUNK_MASK;
        return fElementType[chunk][index];
    }

    private boolean getElementDeclIsExternal(int elementIndex) {
        if (elementIndex < 0 || elementIndex >= fElementCount)
            return false;
        int chunk = elementIndex >> CHUNK_SHIFT;
        int index = elementIndex & CHUNK_MASK;
        return (fElementDeclIsExternal[chunk][index] != 0);
    }

    public int getContentSpecType(int elementIndex) {
        if (elementIndex < 0 || elementIndex >= fElementCount)
            return -1;
        int chunk = elementIndex >> CHUNK_SHIFT;
        int index = elementIndex & CHUNK_MASK;
        return fContentSpecType[chunk][index];
    }

    final class ContentSpecImpl implements XMLContentSpec {
        protected StringPool fStringPool;
        protected int fHandle;
        protected int fType;
        public String toString() {
            if (fType == fMIXEDSymbol || fType == fCHILDRENSymbol)
                return contentSpecNodeAsString(fHandle);
            else
                return fStringPool.toString(fType);
        }
        public int getType() {
            return fType;
        }
        public int getHandle() {
            return fHandle;
        }
        public void getNode(int handle, XMLContentSpec.Node node) {
            getContentSpecNode(handle, node);
        }
    }

    private ContentSpecImpl fContentSpecImpl = null;
    public XMLContentSpec getContentSpec(int elementIndex) {
        if (elementIndex < 0 || elementIndex >= fElementCount)
            return null;
        int chunk = elementIndex >> CHUNK_SHIFT;
        int index = elementIndex & CHUNK_MASK;
        if (fContentSpecImpl == null)
            fContentSpecImpl = new ContentSpecImpl();
        fContentSpecImpl.fStringPool = fStringPool;
        fContentSpecImpl.fHandle = fContentSpec[chunk][index];
        fContentSpecImpl.fType = fContentSpecType[chunk][index];
        return fContentSpecImpl;
    }

    public int getContentSpecHandle(int elementIndex) {
        if (elementIndex < 0 || elementIndex >= fElementCount)
            return -1;
        int chunk = elementIndex >> CHUNK_SHIFT;
        int index = elementIndex & CHUNK_MASK;
        return fContentSpec[chunk][index];
    }

    protected String getContentSpecAsString(int elementIndex) {
        if (elementIndex < 0 || elementIndex >= fElementCount)
            return null;
        int chunk = elementIndex >> CHUNK_SHIFT;
        int index = elementIndex & CHUNK_MASK;
        int contentSpecType = fContentSpecType[chunk][index];
        if (contentSpecType == fMIXEDSymbol || contentSpecType == fCHILDRENSymbol)
            return contentSpecNodeAsString(fContentSpec[chunk][index]);
        else
            return fStringPool.toString(contentSpecType);
    }

    private XMLContentModel getElementContentModel(int elementIndex) {
        if (elementIndex < 0 || elementIndex >= fElementCount)
            return null;
        int chunk = elementIndex >> CHUNK_SHIFT;
        int index = elementIndex & CHUNK_MASK;
        return fContentModel[chunk][index];
    }

    private void setContentModel(int elementIndex, XMLContentModel cm) {
        if (elementIndex < 0 || elementIndex >= fElementCount)
            return;
        int chunk = elementIndex >> CHUNK_SHIFT;
        int index = elementIndex & CHUNK_MASK;
        fContentModel[chunk][index] = cm;
    }

   
  //
    // contentspec entries
    //
   
  private boolean ensureNodeCapacity(int chunk) {
        try {
            return fNodeType[chunk][0] == 0;
        } catch (ArrayIndexOutOfBoundsException ex) {
            byte[][] newByteArray = new byte[chunk * 2][];
            System.arraycopy(fNodeType, 0, newByteArray, 0, chunk);
            fNodeType = newByteArray;
            int[][] newIntArray = new int[chunk * 2][];
            System.arraycopy(fNodeValue, 0, newIntArray, 0, chunk);
            fNodeValue = newIntArray;
        } catch (NullPointerException ex) {
        }
        fNodeType[chunk] = new byte[CHUNK_SIZE];
        fNodeValue[chunk] = new int[CHUNK_SIZE];
        return true;
    }


    //
    // addContentSpecLeafNode
    //

    private int addContentSpecLeafNode(int nodeValue) throws Exception {

    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.addContentSpecLeafNode: " + param("nodeValue",nodeValue) + "\n");
    //****DEBUG****

        //
        // Check that we have not seen this value before...
        //
        if (nodeValue != -1) {
            int nodeCount = fNodeCount;
            int chunk = fNodeCount >> CHUNK_SHIFT;
            int index = fNodeCount & CHUNK_MASK;
            while (true) {
                if (index-- == 0) {
                    index = CHUNK_SIZE - 1;
                    chunk--;
                }
                int nodeType = fNodeType[chunk][index];
                if (nodeType == XMLContentSpec.CONTENTSPECNODE_LEAF) {
                    int otherNodeValue = fNodeValue[chunk][index];
                    if (otherNodeValue == -1)
                        break;
                    if (otherNodeValue == nodeValue) {
                        return -1;
                    }
                }
            }
        }
        int chunk = fNodeCount >> CHUNK_SHIFT;
        int index = fNodeCount & CHUNK_MASK;
        ensureNodeCapacity(chunk);
        fNodeType[chunk][index] = (byte)XMLContentSpec.CONTENTSPECNODE_LEAF;
        fNodeValue[chunk][index] = nodeValue;
        return fNodeCount++;
    }


    //
    // addContentSpecNode
    //

    public int addContentSpecNode(int nodeType, int nodeValue, int otherNodeValue, boolean mustBeUnique) throws Exception {

    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.addContentSpecNode: " + param("nodeType",nodeType) + param("nodeValue",nodeValue) + "\n");
    //****DEBUG****

        if (mustBeUnique) // REVISIT - merge these methods...
            return addContentSpecLeafNode(nodeValue);
        int chunk = fNodeCount >> CHUNK_SHIFT;
        int index = fNodeCount & CHUNK_MASK;
        ensureNodeCapacity(chunk);
        switch (nodeType) {
        case XMLContentSpec.CONTENTSPECNODE_LEAF:
        case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE:
        case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE:
        case XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE:
            fNodeType[chunk][index] = (byte)nodeType;
            fNodeValue[chunk][index] = nodeValue;
            return fNodeCount++;
        case XMLContentSpec.CONTENTSPECNODE_CHOICE:
        case XMLContentSpec.CONTENTSPECNODE_SEQ:
            fNodeType[chunk][index] = (byte)nodeType;
            fNodeValue[chunk][index] = nodeValue;
            int nodeIndex = fNodeCount++;
            if (++index == CHUNK_SIZE) {
                chunk++;
                ensureNodeCapacity(chunk);
                index = 0;
            }
            fNodeType[chunk][index] = (byte)(nodeType | 64); // flag second entry for consistancy checking
            fNodeValue[chunk][index] = otherNodeValue;
            fNodeCount++;
            return nodeIndex;
        default:
            return -1;
        }
    }

    //
    // Protected for access by ContentSpecImpl
    //

    protected void getContentSpecNode(int contentSpecIndex, XMLContentSpec.Node csn) {
        int chunk = contentSpecIndex >> CHUNK_SHIFT;
        int index = contentSpecIndex & CHUNK_MASK;
        csn.type = fNodeType[chunk][index];
        csn.value = fNodeValue[chunk][index];
        if (csn.type == XMLContentSpec.CONTENTSPECNODE_CHOICE || csn.type == XMLContentSpec.CONTENTSPECNODE_SEQ) {
            if (++index == CHUNK_SIZE) {
                chunk++;
                index = 0;
            }
            csn.otherValue = fNodeValue[chunk][index];
        } else
            csn.otherValue = -1;
    }

    public String contentSpecNodeAsString(int contentSpecIndex) {
        int chunk = contentSpecIndex >> CHUNK_SHIFT;
        int index = contentSpecIndex & CHUNK_MASK;
        int type = fNodeType[chunk][index];
        int value = fNodeValue[chunk][index];
        StringBuffer sb = new StringBuffer();
        switch (type) {
        case XMLContentSpec.CONTENTSPECNODE_LEAF:
            sb.append("(" + (value == -1 ? "#PCDATA" : fStringPool.toString(value)) + ")");
            break;
        case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE:
            chunk = value >> CHUNK_SHIFT;
            index = value & CHUNK_MASK;
            if (fNodeType[chunk][index] == XMLContentSpec.CONTENTSPECNODE_LEAF) {
                value = fNodeValue[chunk][index];
                sb.append("(" + (value == -1 ? "#PCDATA" : fStringPool.toString(value)) + ")?");
            } else
                appendContentSpecNode(contentSpecIndex, sb, false);
            break;
        case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE:
            chunk = value >> CHUNK_SHIFT;
            index = value & CHUNK_MASK;
            if (fNodeType[chunk][index] == XMLContentSpec.CONTENTSPECNODE_LEAF) {
                value = fNodeValue[chunk][index];
                sb.append("(" + (value == -1 ? "#PCDATA" : fStringPool.toString(value)) + ")*");
            } else
                appendContentSpecNode(contentSpecIndex, sb, false);
            break;
        case XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE:
            chunk = value >> CHUNK_SHIFT;
            index = value & CHUNK_MASK;
            if (fNodeType[chunk][index] == XMLContentSpec.CONTENTSPECNODE_LEAF) {
                value = fNodeValue[chunk][index];
                sb.append("(" + (value == -1 ? "#PCDATA" : fStringPool.toString(value)) + ")+");
            } else
                appendContentSpecNode(contentSpecIndex, sb, false);
            break;
        case XMLContentSpec.CONTENTSPECNODE_CHOICE:
        case XMLContentSpec.CONTENTSPECNODE_SEQ:
            appendContentSpecNode(contentSpecIndex, sb, false);
            break;
        default:
            return null;
        }
        return sb.toString();
    }

    private void appendContentSpecNode(int contentSpecIndex, StringBuffer sb, boolean noParen) {

    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.appendContentSpecNode: ...\n");
    //****DEBUG****

        int chunk = contentSpecIndex >> CHUNK_SHIFT;
        int index = contentSpecIndex & CHUNK_MASK;
        int type = fNodeType[chunk][index];
        int value = fNodeValue[chunk][index];
        switch (type) {
        case XMLContentSpec.CONTENTSPECNODE_LEAF:
            sb.append(value == -1 ? "#PCDATA" : fStringPool.toString(value));
            return;
        case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE:
            appendContentSpecNode(value, sb, false);
            sb.append('?');
            return;
        case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE:
            appendContentSpecNode(value, sb, false);
            sb.append('*');
            return;
        case XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE:
            appendContentSpecNode(value, sb, false);
            sb.append('+');
            return;
        case XMLContentSpec.CONTENTSPECNODE_CHOICE:
        case XMLContentSpec.CONTENTSPECNODE_SEQ:
            if (!noParen)
                sb.append('(');
            int leftChunk = value >> CHUNK_SHIFT;
            int leftIndex = value & CHUNK_MASK;
            int leftType = fNodeType[leftChunk][leftIndex];
            appendContentSpecNode(value, sb, leftType == type);
            sb.append(type == XMLContentSpec.CONTENTSPECNODE_CHOICE ? '|' : ',');
            if (++index == CHUNK_SIZE) {
                chunk++;
                index = 0;
            }
            appendContentSpecNode(fNodeValue[chunk][index], sb, false);
            if (!noParen)
                sb.append(')');
            return;
        default:
            return;
        }
    }

    //
    // attribute list interfaces
    //
    private boolean ensureAttrCapacity(int chunk) {
        try {
            return fAttName[chunk][0] == 0;
        } catch (ArrayIndexOutOfBoundsException ex) {
            byte[][] newByteArray = new byte[chunk * 2][];
            System.arraycopy(fAttDefIsExternal, 0, newByteArray, 0, chunk);
            fAttDefIsExternal = newByteArray;
            int[][] newIntArray = new int[chunk * 2][];
            System.arraycopy(fAttName, 0, newIntArray, 0, chunk);
            fAttName = newIntArray;
            newIntArray = new int[chunk * 2][];
            System.arraycopy(fAttType, 0, newIntArray, 0, chunk);
            fAttType = newIntArray;
            newIntArray = new int[chunk * 2][];
            System.arraycopy(fEnumeration, 0, newIntArray, 0, chunk);
            fEnumeration = newIntArray;
            newIntArray = new int[chunk * 2][];
            System.arraycopy(fAttDefaultType, 0, newIntArray, 0, chunk);
            fAttDefaultType = newIntArray;
            newIntArray = new int[chunk * 2][];
            System.arraycopy(fAttValue, 0, newIntArray, 0, chunk);
            fAttValue = newIntArray;
            newIntArray = new int[chunk * 2][];
            System.arraycopy(fNextAttDef, 0, newIntArray, 0, chunk);
            fNextAttDef = newIntArray;
            AttributeValidator[][] newValidatorArray = new AttributeValidator[chunk * 2][];
            System.arraycopy(fAttValidator, 0, newValidatorArray, 0, chunk);
            fAttValidator = newValidatorArray;
        } catch (NullPointerException ex) {
        }
        fAttDefIsExternal[chunk] = new byte[CHUNK_SIZE];
        fAttName[chunk] = new int[CHUNK_SIZE];
        fAttType[chunk] = new int[CHUNK_SIZE];
        fAttValidator[chunk] = new AttributeValidator[CHUNK_SIZE];
        fEnumeration[chunk] = new int[CHUNK_SIZE];
        fAttDefaultType[chunk] = new int[CHUNK_SIZE];
        fAttValue[chunk] = new int[CHUNK_SIZE];
        fNextAttDef[chunk] = new int[CHUNK_SIZE];
        return true;
    }


    //
  // addAttDef
  //

    public int addAttDef(int elementIndex, int attName, int attType, int enumeration, int attDefaultType, int attDefaultValue, boolean isExternal) throws Exception {

    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.addAttDef\n");
    //****DEBUG****

        //
        // check fields
        //
        int elemChunk = elementIndex >> CHUNK_SHIFT;
        int elemIndex = elementIndex & CHUNK_MASK;
        int attlistIndex = fAttlistHead[elemChunk][elemIndex];
        int dupID = -1;
        int dupNotation = -1;
        while (attlistIndex != -1) {
            int attrChunk = attlistIndex >> CHUNK_SHIFT;
            int attrIndex = attlistIndex & CHUNK_MASK;
            if (fStringPool.equalNames(fAttName[attrChunk][attrIndex], attName)) {
                if (fWarningOnDuplicateAttDef) {
                    Object[] args = { fStringPool.toString(fElementType[elemChunk][elemIndex]),
                                      fStringPool.toString(attName) };
                    fErrorReporter.reportError(fErrorReporter.getLocator(),
                                               XMLMessages.XML_DOMAIN,
                                               XMLMessages.MSG_DUPLICATE_ATTDEF,
                                               XMLMessages.P53_DUPLICATE,
                                               args,
                                               XMLErrorReporter.ERRORTYPE_WARNING);
                }
                return -1;
            }
            if (fValidating) {
                if (attType == fIDSymbol && fAttType[attrChunk][attrIndex] == fIDSymbol)
                    dupID = fAttName[attrChunk][attrIndex];
                if (attType == fNOTATIONSymbol && fAttType[attrChunk][attrIndex] == fNOTATIONSymbol)
                    dupNotation = fAttName[attrChunk][attrIndex];
            }
            attlistIndex = fNextAttDef[attrChunk][attrIndex];
        }
        if (fValidating) {
            if (dupID != -1) {
                Object[] args = { fStringPool.toString(fElementType[elemChunk][elemIndex]),
                                  fStringPool.toString(dupID),
                                  fStringPool.toString(attName) };
                fErrorReporter.reportError(fErrorReporter.getLocator(),
                                           XMLMessages.XML_DOMAIN,
                                           XMLMessages.MSG_MORE_THAN_ONE_ID_ATTRIBUTE,
                                           XMLMessages.VC_ONE_ID_PER_ELEMENT_TYPE,
                                           args,
                                           XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
                return -1;
            }
            if (dupNotation != -1) {
                Object[] args = { fStringPool.toString(fElementType[elemChunk][elemIndex]),
                                  fStringPool.toString(dupNotation),
                                  fStringPool.toString(attName) };
                fErrorReporter.reportError(fErrorReporter.getLocator(),
                                           XMLMessages.XML_DOMAIN,
                                           XMLMessages.MSG_MORE_THAN_ONE_NOTATION_ATTRIBUTE,
                                           XMLMessages.VC_ONE_NOTATION_PER_ELEMENT_TYPE,
                                           args,
                                           XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
                return -1;
            }
        }
        //
        // save the fields
        //
        int chunk = fAttDefCount >> CHUNK_SHIFT;
        int index = fAttDefCount & CHUNK_MASK;
        ensureAttrCapacity(chunk);
        fAttName[chunk][index] = attName;
        fAttType[chunk][index] = attType;
        fAttValidator[chunk][index] = getValidatorForAttType(attType);
        fEnumeration[chunk][index] = enumeration;
        fAttDefaultType[chunk][index] = attDefaultType;
        fAttDefIsExternal[chunk][index] = (byte)(isExternal ? 1 : 0);
        fAttValue[chunk][index] = attDefaultValue;
        //
        // add to the attr list for this element
        //
        int nextIndex = -1;
        if (attDefaultValue != -1) {
            nextIndex = fAttlistHead[elemChunk][elemIndex];
            fAttlistHead[elemChunk][elemIndex] = fAttDefCount;
            if (nextIndex == -1)
                fAttlistTail[elemChunk][elemIndex] = fAttDefCount;
        } else {
            nextIndex = fAttlistTail[elemChunk][elemIndex];
            fAttlistTail[elemChunk][elemIndex] = fAttDefCount;
            if (nextIndex == -1)
                fAttlistHead[elemChunk][elemIndex] = fAttDefCount;
            else {
                fNextAttDef[nextIndex >> CHUNK_SHIFT][nextIndex & CHUNK_MASK] = fAttDefCount;
                nextIndex = -1;
            }
        }
        fNextAttDef[chunk][index] = nextIndex;
        return fAttDefCount++;
    }


  //
  // getValidatorAttType
  //

    private AttributeValidator getValidatorForAttType(int attType) {
        if (attType == fCDATASymbol) {
            if (fAttValidatorCDATA == null)
                fAttValidatorCDATA = new AttValidatorCDATA();
            return fAttValidatorCDATA;
        }
        if (attType == fIDSymbol) {
            if (fAttValidatorID == null)
                fAttValidatorID = new AttValidatorID();
            return fAttValidatorID;
        }
        if (attType == fIDREFSymbol) {
            if (fAttValidatorIDREF == null)
                fAttValidatorIDREF = new AttValidatorIDREF();
            return fAttValidatorIDREF;
        }
        if (attType == fIDREFSSymbol) {
            if (fAttValidatorIDREFS == null)
                fAttValidatorIDREFS = new AttValidatorIDREFS();
            return fAttValidatorIDREFS;
        }
        if (attType == fENTITYSymbol) {
            if (fAttValidatorENTITY == null)
                fAttValidatorENTITY = new AttValidatorENTITY();
            return fAttValidatorENTITY;
        }
        if (attType == fENTITIESSymbol) {
            if (fAttValidatorENTITIES == null)
                fAttValidatorENTITIES = new AttValidatorENTITIES();
            return fAttValidatorENTITIES;
        }
        if (attType == fNMTOKENSymbol) {
            if (fAttValidatorNMTOKEN == null)
                fAttValidatorNMTOKEN = new AttValidatorNMTOKEN();
            return fAttValidatorNMTOKEN;
        }
        if (attType == fNMTOKENSSymbol) {
            if (fAttValidatorNMTOKENS == null)
                fAttValidatorNMTOKENS = new AttValidatorNMTOKENS();
            return fAttValidatorNMTOKENS;
        }
        if (attType == fNOTATIONSymbol) {
            if (fAttValidatorNOTATION == null)
                fAttValidatorNOTATION = new AttValidatorNOTATION();
            return fAttValidatorNOTATION;
        }
        if (attType == fENUMERATIONSymbol) {
            if (fAttValidatorENUMERATION == null)
                fAttValidatorENUMERATION = new AttValidatorENUMERATION();
            return fAttValidatorENUMERATION;
        }
        if (attType == fDATATYPESymbol) {
            if (fAttValidatorDATATYPE == null)
                fAttValidatorDATATYPE = fSchemaImporter.createDatatypeAttributeValidator();
            return fAttValidatorDATATYPE;
        }
        throw new RuntimeException("getValidatorForAttType(" + fStringPool.toString(attType) + ")");
    }

    private int getAttDef(int elementType, int attrName) {
        int elementIndex = fStringPool.getDeclaration(elementType);
        if (elementIndex == -1)
            return -1;
        int chunk = elementIndex >> CHUNK_SHIFT;
        int index = elementIndex & CHUNK_MASK;
        int attDefIndex = fAttlistHead[chunk][index];
        while (attDefIndex != -1) {
            chunk = attDefIndex >> CHUNK_SHIFT;
            index = attDefIndex & CHUNK_MASK;
            if (fAttName[chunk][index] == attrName || fStringPool.equalNames(fAttName[chunk][index], attrName))
                return attDefIndex;
            attDefIndex = fNextAttDef[chunk][index];
        }
        return -1;
    }

    private int getAttName(int attDefIndex) {
        int chunk = attDefIndex >> CHUNK_SHIFT;
        int index = attDefIndex & CHUNK_MASK;
        return fAttName[chunk][index];
    }

    private int getAttValue(int attDefIndex) {
        int chunk = attDefIndex >> CHUNK_SHIFT;
        int index = attDefIndex & CHUNK_MASK;
        return fAttValue[chunk][index];
    }

    private AttributeValidator getAttributeValidator(int attDefIndex) {
        int chunk = attDefIndex >> CHUNK_SHIFT;
        int index = attDefIndex & CHUNK_MASK;
        return fAttValidator[chunk][index];
    }

    private int getAttType(int attDefIndex) {
        int chunk = attDefIndex >> CHUNK_SHIFT;
        int index = attDefIndex & CHUNK_MASK;
        return fAttType[chunk][index];
    }

    private int getAttDefaultType(int attDefIndex) {
        int chunk = attDefIndex >> CHUNK_SHIFT;
        int index = attDefIndex & CHUNK_MASK;
        return fAttDefaultType[chunk][index];
    }

    private int getEnumeration(int attDefIndex) {
        int chunk = attDefIndex >> CHUNK_SHIFT;
        int index = attDefIndex & CHUNK_MASK;
        return fEnumeration[chunk][index];
    }


  //
  // copyAtts
  //
    // added by twl

    public void copyAtts(int fromElementType, int toElementType) {

    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.copyAtts: " + param("fromElementType",fromElementType) + param("toElementType",toElementType) + "\n");
    //****DEBUG****

        int fromElement = fStringPool.getDeclaration(fromElementType);
        int toElement = fStringPool.getDeclaration(toElementType);
        if (fromElement == -1)
            return;
        int chunk = fromElement >> CHUNK_SHIFT;
        int index = fromElement & CHUNK_MASK;
        int attDefIndex = fAttlistHead[chunk][index];
        while (attDefIndex != -1) {
            chunk = attDefIndex >> CHUNK_SHIFT;
            index = attDefIndex & CHUNK_MASK;
            try {
                addAttDef(toElement,
                          fAttName[chunk][index],
                          fAttType[chunk][index],
                          fEnumeration[chunk][index],
                          fAttDefaultType[chunk][index],
                          fAttValue[chunk][index],
                          fAttDefIsExternal[chunk][index] != 0);
            } catch (Exception ex) {
            }
            attDefIndex = fNextAttDef[chunk][index];
        }
    }


  //
  // addDefaultAttributes
  //

    private int addDefaultAttributes(int elementIndex, XMLAttrList attrList, int attrIndex, boolean validationEnabled, boolean standalone) throws Exception {

    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.addDefaultAttributes\n");
    //****DEBUG****

        //
        // Check after all specified attrs are scanned
        // (1) report error for REQUIRED attrs that are missing (V_TAGc)
        // (2) check that FIXED attrs have matching value (V_TAGd)
        // (3) add default attrs (FIXED and NOT_FIXED)
        //
        int elemChunk = elementIndex >> CHUNK_SHIFT;
        int elemIndex = elementIndex & CHUNK_MASK;
        int attlistIndex = fAttlistHead[elemChunk][elemIndex];
        int firstCheck = attrIndex;
        int lastCheck = -1;
//System.err.println("specified attributes for element type " + fStringPool.toString(fElementType[elemChunk][elemIndex]));
        while (attlistIndex != -1) {
            int adChunk = attlistIndex >> CHUNK_SHIFT;
            int adIndex = attlistIndex & CHUNK_MASK;
            int attName = fAttName[adChunk][adIndex];
            int attType = fAttType[adChunk][adIndex];
            int attDefType = fAttDefaultType[adChunk][adIndex];
            int attValue = fAttValue[adChunk][adIndex];
            boolean specified = false;
            boolean required = attDefType == fREQUIREDSymbol;
            if (firstCheck != -1) {
                boolean cdata = attType == fCDATASymbol;
                if (!cdata || required || attValue != -1) {
                    int i = attrList.getFirstAttr(firstCheck);
                    while (i != -1 && (lastCheck == -1 || i <= lastCheck)) {
                        if (fStringPool.equalNames(attrList.getAttrName(i), attName)) {
//System.err.println(fStringPool.toString(attrList.getAttrName(i)) + " == " + fStringPool.toString(attName));
                            if (validationEnabled && attDefType == fFIXEDSymbol) {
                                int alistValue = attrList.getAttValue(i);
                                if (alistValue != attValue &&
                                    !fStringPool.toString(alistValue).equals(fStringPool.toString(attValue))) {
                                    Object[] args = { fStringPool.toString(fElementType[elemChunk][elemIndex]),
                                                    fStringPool.toString(attName),
                                                    fStringPool.toString(alistValue),
                                                    fStringPool.toString(attValue) };
                                    fErrorReporter.reportError(fErrorReporter.getLocator(),
                                                            XMLMessages.XML_DOMAIN,
                                                            XMLMessages.MSG_FIXED_ATTVALUE_INVALID,
                                                            XMLMessages.VC_FIXED_ATTRIBUTE_DEFAULT,
                                                            args,
                                                            XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
                                }
                            }
                            specified = true;
                            break;
                        }
//System.err.println(fStringPool.toString(attrList.getAttrName(i)) + " != " + fStringPool.toString(attName));
                        i = attrList.getNextAttr(i);
                    }
                }
            }
            if (!specified) {
                if (required) {
                    if (validationEnabled) {
                        Object[] args = { fStringPool.toString(fElementType[elemChunk][elemIndex]),
                                          fStringPool.toString(attName) };
                        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                                   XMLMessages.XML_DOMAIN,
                                                   XMLMessages.MSG_REQUIRED_ATTRIBUTE_NOT_SPECIFIED,
                                                   XMLMessages.VC_REQUIRED_ATTRIBUTE,
                                                   args,
                                                   XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
                    }
                } else if (attValue != -1) {
                    if (validationEnabled && standalone && fAttDefIsExternal[adChunk][adIndex] != 0) {
                        Object[] args = { fStringPool.toString(fElementType[elemChunk][elemIndex]),
                                          fStringPool.toString(attName) };
                        fErrorReporter.reportError(fErrorReporter.getLocator(),
                                                   XMLMessages.XML_DOMAIN,
                                                   XMLMessages.MSG_DEFAULTED_ATTRIBUTE_NOT_SPECIFIED,
                                                   XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
                                                   args,
                                                   XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
                    }
                    if (attType == fIDREFSymbol) {
                        addIdRef(attValue);
                    } else if (attType == fIDREFSSymbol) {
                        StringTokenizer tokenizer = new StringTokenizer(fStringPool.toString(attValue));
                        while (tokenizer.hasMoreTokens()) {
                            String idName = tokenizer.nextToken();
                            addIdRef(fStringPool.addSymbol(idName));
                        }
                    }
                    if (attrIndex == -1)
                        attrIndex = attrList.startAttrList();
                    int newAttr = attrList.addAttr(attName, attValue, attType, false, false);
                    if (lastCheck == -1)
                        lastCheck = newAttr;
                }
            }
            attlistIndex = fNextAttDef[adChunk][adIndex];
        }
        return attrIndex;
    }


    //
    // Protected for use by AttributeValidator classes.
    //
    protected boolean getAttDefIsExternal(int elementType, int attrName) {
        int attDefIndex = getAttDef(elementType, attrName);
        int chunk = attDefIndex >> CHUNK_SHIFT;
        int index = attDefIndex & CHUNK_MASK;
        return (fAttDefIsExternal[chunk][index] != 0);
    }


  //
  // addId
  //

    protected boolean addId (int idIndex) {

    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.addId" + param("idIndex",idIndex) + "\n");
    //****DEBUG****

    //System.err.println("addId(" + fStringPool.toString(idIndex) + ") " + idIndex);
        Integer key = new Integer(idIndex);
        if (fIdDefs == null)
            fIdDefs = new Hashtable();
        else if (fIdDefs.containsKey(key))
            return false;
        if (fNullValue == null)
            fNullValue = new Object();
        fIdDefs.put(key, fNullValue/*new Integer(elementType)*/);
        return true;
    }


  //
  // addIdRef
  //

    protected void addIdRef(int idIndex) {

    //****DEBUG****
    if (DEBUG) print("(POP) XMLValidator.addIdRef" + param("idIndex",idIndex) + "\n");
    //****DEBUG****

    //System.err.println("addIdRef(" + fStringPool.toString(idIndex) + ") " + idIndex);
        Integer key = new Integer(idIndex);
        if (fIdDefs != null && fIdDefs.containsKey(key))
            return;
        if (fIdRefs == null)
            fIdRefs = new Hashtable();
        else if (fIdRefs.containsKey(key))
            return;
        if (fNullValue == null)
            fNullValue = new Object();
        fIdRefs.put(key, fNullValue/*new Integer(elementType)*/);
    }

    /**
     * Check that all ID references were to ID attributes present in the document.
     * <p>
     * This method is a convenience call that allows the validator to do any id ref
     * checks above and beyond those done by the scanner. The scanner does the checks
     * specificied in the XML spec, i.e. that ID refs refer to ids which were
     * eventually defined somewhere in the document.
     * <p>
     * If the validator is for a Schema perhaps, which defines id semantics beyond
     * those of the XML specificiation, this is where that extra checking would be
     * done. For most validators, this is a no-op.
     *
     * @exception Exception Thrown on error.
     */
    private void checkIdRefs() throws Exception {

    //****DEBUG****
    if (DEBUG) print("(???) XMLValidator.checkIdRefs\n");
    //****DEBUG****

        if (fIdRefs == null)
            return;
        Enumeration en = fIdRefs.keys();
        while (en.hasMoreElements()) {
            Integer key = (Integer)en.nextElement();
            if (fIdDefs == null || !fIdDefs.containsKey(key)) {
                Object[] args = { fStringPool.toString(key.intValue()) };
                fErrorReporter.reportError(fErrorReporter.getLocator(),
                                           XMLMessages.XML_DOMAIN,
                                           XMLMessages.MSG_ELEMENT_WITH_ID_REQUIRED,
                                           XMLMessages.VC_IDREF,
                                           args,
                                           XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
            }
        }
    }

    /**
     * Checks that all declared elements refer to declared elements
     * in their content models. This method calls out to the error
     * handler to indicate warnings.
     */
    private void checkDeclaredElements() throws Exception {

    //****DEBUG****
    if (DEBUG) print("(???) XMLValidator.checkDeclaredElements\n");
    //****DEBUG****

        for (int i = 0; i < fElementCount; i++) {
            int type = getContentSpecType(i);
            if (type == fMIXEDSymbol || type == fCHILDRENSymbol) {
                int chunk = i >> CHUNK_SHIFT;
                int index = i &  CHUNK_MASK;
                int contentSpecIndex = fContentSpec[chunk][index];
                checkDeclaredElements(i, contentSpecIndex);
            }
        }
    }

    /**
     * Does a recursive (if necessary) check on the specified element's
     * content spec to make sure that all children refer to declared
     * elements.
     * <p>
     * This method assumes that it will only be called when there is
     * a validation handler.
     */
    private void checkDeclaredElements(int elementIndex, int contentSpecIndex) throws Exception {
       
    //****DEBUG****
    if (DEBUG) print("(???) XMLValidator.checkDeclaredElements\n");
    //****DEBUG****

        // get spec type and value
        int chunk = contentSpecIndex >> CHUNK_SHIFT;
        int index = contentSpecIndex &  CHUNK_MASK;
        int type  = fNodeType[chunk][index];
        int value = fNodeValue[chunk][index];

        // handle type
        switch (type) {
       
            // #PCDATA | element
            case XMLContentSpec.CONTENTSPECNODE_LEAF: {
                // perform check for declared element
                if (value != -1 && fStringPool.getDeclaration(value) == -1) {
                    int elemChunk = elementIndex >> CHUNK_SHIFT;
                    int elemIndex = elementIndex &  CHUNK_MASK;
                    int elementType = fElementType[elemChunk][elemIndex];
                    Object[] args = { fStringPool.toString(elementType),
                                      fStringPool.toString(value) };
                    fErrorReporter.reportError(fErrorReporter.getLocator(),
                                               XMLMessages.XML_DOMAIN,
                                               XMLMessages.MSG_UNDECLARED_ELEMENT_IN_CONTENTSPEC,
                                               XMLMessages.P45_UNDECLARED_ELEMENT_IN_CONTENTSPEC,
                                               args,
                                               XMLErrorReporter.ERRORTYPE_WARNING);
                }
                break;
            }

            // (...)? | (...)* | (...)+
            case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE:
            case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE:
            case XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE: {
                checkDeclaredElements(elementIndex, value);
                break;
            }

            // (... , ...) | (... | ...)
            case XMLContentSpec.CONTENTSPECNODE_CHOICE:
            case XMLContentSpec.CONTENTSPECNODE_SEQ: {
                checkDeclaredElements(elementIndex, value);
                if (++index == CHUNK_SIZE) {
                    chunk++;
                    index = 0;
                }
                checkDeclaredElements(elementIndex, fNodeValue[chunk][index]);
                break;
            }
        }
    }
}
TOP

Related Classes of org.apache.xerces.validators.common.XMLValidator$AttValidatorNMTOKENS

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.