/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.xerces.impl.xs;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.Vector;

import org.apache.xerces.impl.Constants;
import org.apache.xerces.impl.XMLEntityManager;
import org.apache.xerces.impl.XMLErrorReporter;
import org.apache.xerces.impl.dv.ValidatedInfo;
import org.apache.xerces.impl.dv.XSSimpleType;
import org.apache.xerces.impl.dv.xs.EqualityHelper;
import org.apache.xerces.impl.validation.ValidationManager;
import org.apache.xerces.impl.validation.ValidationState;
import org.apache.xerces.impl.xs.identity.Field;
import org.apache.xerces.impl.xs.identity.FieldActivator;
import org.apache.xerces.impl.xs.identity.IdentityConstraint;
import org.apache.xerces.impl.xs.identity.KeyRef;
import org.apache.xerces.impl.xs.identity.Selector;
import org.apache.xerces.impl.xs.identity.UniqueOrKey;
import org.apache.xerces.impl.xs.identity.ValueStore;
import org.apache.xerces.impl.xs.identity.XPathMatcher;
import org.apache.xerces.impl.xs.models.CMBuilder;
import org.apache.xerces.impl.xs.models.CMNodeFactory;
import org.apache.xerces.impl.xs.models.XSCMValidator;
import org.apache.xerces.util.AugmentationsImpl;
import org.apache.xerces.util.IntStack;
import org.apache.xerces.util.SymbolTable;
import org.apache.xerces.util.XMLSymbols;
import org.apache.xerces.util.URI.MalformedURIException;
import org.apache.xerces.xni.NamespaceContext;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.XMLAttributes;
import org.apache.xerces.xni.XMLDocumentHandler;
import org.apache.xerces.xni.XMLLocator;
import org.apache.xerces.xni.XMLString;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.grammars.XMLGrammarPool;
import org.apache.xerces.xni.parser.XMLDocumentSource;
import org.apache.xerces.xni.parser.XMLEntityResolver;
import org.apache.xerces.xni.parser.XMLInputSource;
import org.apache.xerces.xs.ShortList;
import org.apache.xerces.xs.StringList;
import org.apache.xerces.xs.XSConstants;
import org.apache.xerces.xs.XSTypeAlternative;
import org.apache.xerces.xs.XSTypeDefinition;
import org.apache.xerces.xs.datatypes.ObjectList;

/**
 * 
 * A class providing certain functionalities and has validation session data for the 
 * XML Schema validator. Important functionalities provided by this class are IDC constraint 
 * implementation, schema error handling routines and CTA/assertion interfaces with the 
 * XML Schema validator.
 * 
 * @xerces.internal
 * 
 * @author Sandy Gao IBM
 * @author Elena Litani IBM
 * @author Andy Clark IBM
 * @author Neeraj Bajaj, Sun Microsystems, inc.
 * @author Mukul Gandhi IBM
 * @version $Id: XMLSchemaValidatorBase.java 1871904 2019-12-22 06:51:11Z mukulg $
 */
public class XMLSchemaValidatorBase implements XSElementDeclHelper, FieldActivator {
    
    protected XMLSchemaValidatorBase() {
       // NO OP  
    }
    
    //
    // Constants
    //
    protected static final boolean DEBUG = false;

    // feature identifiers

    /** Feature identifier: validation. */
    protected static final String VALIDATION =
        Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;

    /** Feature identifier: validation. */
    protected static final String SCHEMA_VALIDATION =
        Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_VALIDATION_FEATURE;

    /** Feature identifier: schema full checking*/
    protected static final String SCHEMA_FULL_CHECKING =
        Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_FULL_CHECKING;

    /** Feature identifier: dynamic validation. */
    protected static final String DYNAMIC_VALIDATION =
        Constants.XERCES_FEATURE_PREFIX + Constants.DYNAMIC_VALIDATION_FEATURE;

    /** Feature identifier: expose schema normalized value */
    protected static final String NORMALIZE_DATA =
        Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_NORMALIZED_VALUE;

    /** Feature identifier: send element default value via characters() */
    protected static final String SCHEMA_ELEMENT_DEFAULT =
        Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_ELEMENT_DEFAULT;

    /** Feature identifier: augment PSVI */
    protected static final String SCHEMA_AUGMENT_PSVI =
        Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_AUGMENT_PSVI;

    /** Feature identifier: whether to recognize java encoding names */
    protected static final String ALLOW_JAVA_ENCODINGS =
        Constants.XERCES_FEATURE_PREFIX + Constants.ALLOW_JAVA_ENCODINGS_FEATURE;

    /** Feature identifier: standard uri conformant feature. */
    protected static final String STANDARD_URI_CONFORMANT_FEATURE =
        Constants.XERCES_FEATURE_PREFIX + Constants.STANDARD_URI_CONFORMANT_FEATURE;
    
    /** Feature: generate synthetic annotations */
    protected static final String GENERATE_SYNTHETIC_ANNOTATIONS = 
        Constants.XERCES_FEATURE_PREFIX + Constants.GENERATE_SYNTHETIC_ANNOTATIONS_FEATURE;
    
    /** Feature identifier: validate annotations. */
    protected static final String VALIDATE_ANNOTATIONS =
        Constants.XERCES_FEATURE_PREFIX + Constants.VALIDATE_ANNOTATIONS_FEATURE;
    
    /** Feature identifier: honour all schemaLocations */
    protected static final String HONOUR_ALL_SCHEMALOCATIONS = 
        Constants.XERCES_FEATURE_PREFIX + Constants.HONOUR_ALL_SCHEMALOCATIONS_FEATURE;

    /** Feature identifier: use grammar pool only */
    protected static final String USE_GRAMMAR_POOL_ONLY =
        Constants.XERCES_FEATURE_PREFIX + Constants.USE_GRAMMAR_POOL_ONLY_FEATURE;

    /** Feature identifier: whether to continue parsing a schema after a fatal error is encountered */
    protected static final String CONTINUE_AFTER_FATAL_ERROR =
        Constants.XERCES_FEATURE_PREFIX + Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE;

    protected static final String PARSER_SETTINGS =
            Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
    
    /** Feature identifier: namespace growth */
    protected static final String NAMESPACE_GROWTH = 
        Constants.XERCES_FEATURE_PREFIX + Constants.NAMESPACE_GROWTH_FEATURE;

    /** Feature identifier: tolerate duplicates */
    protected static final String TOLERATE_DUPLICATES = 
        Constants.XERCES_FEATURE_PREFIX + Constants.TOLERATE_DUPLICATES_FEATURE;

    /** Feature identifier: whether to ignore xsi:type attributes until a global element declaration is encountered */
    protected static final String IGNORE_XSI_TYPE =
        Constants.XERCES_FEATURE_PREFIX + Constants.IGNORE_XSI_TYPE_FEATURE;
    
    /** Feature identifier: whether to ignore ID/IDREF errors */
    protected static final String ID_IDREF_CHECKING =
        Constants.XERCES_FEATURE_PREFIX + Constants.ID_IDREF_CHECKING_FEATURE;
    
    /** Feature identifier: whether to ignore unparsed entity errors */
    protected static final String UNPARSED_ENTITY_CHECKING =
        Constants.XERCES_FEATURE_PREFIX + Constants.UNPARSED_ENTITY_CHECKING_FEATURE;
    
    /** Feature identifier: whether to ignore identity constraint errors */
    protected static final String IDENTITY_CONSTRAINT_CHECKING =
        Constants.XERCES_FEATURE_PREFIX + Constants.IDC_CHECKING_FEATURE;

    /** Feature identifier: whether to ignore type alternatives */
    protected static final String TYPE_ALTERNATIVES_CHECKING =
        Constants.XERCES_FEATURE_PREFIX + Constants.TYPE_ALTERNATIVES_CHEKING_FEATURE;
    
    /** Feature identifier: whether to allow full XPath 2.0 checking for CTA processing */
    protected static final String CTA_FULL_XPATH_CHECKING =
        Constants.XERCES_FEATURE_PREFIX + Constants.CTA_FULL_XPATH_CHECKING_FEATURE;
    
    /** Feature identifier: whether to allow comment and PI nodes to be visible during <assert> processing */
    protected static final String ASSERT_COMMENT_PI_CHECKING =
        Constants.XERCES_FEATURE_PREFIX + Constants.ASSERT_COMMENT_PI_CHECKING_FEATURE;

    // property identifiers

    /** Property identifier: symbol table. */
    public static final String SYMBOL_TABLE =
        Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;

    /** Property identifier: error reporter. */
    public static final String ERROR_REPORTER =
        Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;

    /** Property identifier: entity resolver. */
    public static final String ENTITY_RESOLVER =
        Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;

    /** Property identifier: grammar pool. */
    public static final String XMLGRAMMAR_POOL =
        Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY;

    protected static final String VALIDATION_MANAGER =
        Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY;

    protected static final String ENTITY_MANAGER =
        Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_MANAGER_PROPERTY;

    /** Property identifier: schema location. */
    protected static final String SCHEMA_LOCATION =
        Constants.XERCES_PROPERTY_PREFIX + Constants.SCHEMA_LOCATION;

    /** Property identifier: no namespace schema location. */
    protected static final String SCHEMA_NONS_LOCATION =
        Constants.XERCES_PROPERTY_PREFIX + Constants.SCHEMA_NONS_LOCATION;

    /** Property identifier: JAXP schema source. */
    protected static final String JAXP_SCHEMA_SOURCE =
        Constants.JAXP_PROPERTY_PREFIX + Constants.SCHEMA_SOURCE;

    /** Property identifier: JAXP schema language. */
    protected static final String JAXP_SCHEMA_LANGUAGE =
        Constants.JAXP_PROPERTY_PREFIX + Constants.SCHEMA_LANGUAGE;

    /** Property identifier: root type definition. */
    protected static final String ROOT_TYPE_DEF =
        Constants.XERCES_PROPERTY_PREFIX + Constants.ROOT_TYPE_DEFINITION_PROPERTY;
    
    /** Property identifier: root element declaration. */
    protected static final String ROOT_ELEMENT_DECL =
        Constants.XERCES_PROPERTY_PREFIX + Constants.ROOT_ELEMENT_DECLARATION_PROPERTY;

    /** Property identifier: Schema DV Factory */
    protected static final String SCHEMA_DV_FACTORY = 
        Constants.XERCES_PROPERTY_PREFIX + Constants.SCHEMA_DV_FACTORY_PROPERTY;

    /** Property identifier: xml schema version. */
    protected static final String XML_SCHEMA_VERSION =
        Constants.XERCES_PROPERTY_PREFIX + Constants.XML_SCHEMA_VERSION_PROPERTY;

    /** Property identifier: datatype xml version. */
    protected static final String DATATYPE_XML_VERSION = 
        Constants.XERCES_PROPERTY_PREFIX + Constants.DATATYPE_XML_VERSION_PROPERTY;

    // recognized features and properties

    /** Recognized features. */
    protected static final String[] RECOGNIZED_FEATURES =
        {
            VALIDATION,
            SCHEMA_VALIDATION,
            DYNAMIC_VALIDATION,
            SCHEMA_FULL_CHECKING,
            ALLOW_JAVA_ENCODINGS,
            CONTINUE_AFTER_FATAL_ERROR,
            STANDARD_URI_CONFORMANT_FEATURE,
            GENERATE_SYNTHETIC_ANNOTATIONS,
            VALIDATE_ANNOTATIONS,
            HONOUR_ALL_SCHEMALOCATIONS,
            USE_GRAMMAR_POOL_ONLY,
            IGNORE_XSI_TYPE,
            ID_IDREF_CHECKING,
            IDENTITY_CONSTRAINT_CHECKING,
            UNPARSED_ENTITY_CHECKING,
            NAMESPACE_GROWTH,
            TOLERATE_DUPLICATES,
            TYPE_ALTERNATIVES_CHECKING,
            CTA_FULL_XPATH_CHECKING,
            ASSERT_COMMENT_PI_CHECKING
        };


    /** Feature defaults. */
    protected static final Boolean[] FEATURE_DEFAULTS = { null,
        // NOTE: The following defaults are nulled out on purpose.
        //       If they are set, then when the XML Schema validator
        //       is constructed dynamically, these values may override
        //       those set by the application. This goes against the
        //       whole purpose of XMLComponent#getFeatureDefault but
        //       it can't be helped in this case. -Ac
        // NOTE: Instead of adding default values here, add them (and
        //       the corresponding recognized features) to the objects
        //       that have an XMLSchemaValidator instance as a member,
        //       such as the parser configurations. -PM
        null, //Boolean.FALSE,
        null, //Boolean.FALSE,
        null, //Boolean.FALSE,
        null, //Boolean.FALSE,
        null, //Boolean.FALSE,
        null,
        null,
        null,
        null,
        null,
        null,
        null,
        null,
        null,
        null,
        null,
        null,
        null,
        null
    };

    /** Recognized properties. */
    protected static final String[] RECOGNIZED_PROPERTIES =
        {
            SYMBOL_TABLE,
            ERROR_REPORTER,
            ENTITY_RESOLVER,
            VALIDATION_MANAGER,
            SCHEMA_LOCATION,
            SCHEMA_NONS_LOCATION,
            JAXP_SCHEMA_SOURCE,
            JAXP_SCHEMA_LANGUAGE,
            ROOT_TYPE_DEF,
            ROOT_ELEMENT_DECL,
            SCHEMA_DV_FACTORY,
            XML_SCHEMA_VERSION,
            DATATYPE_XML_VERSION
        };

    /** Property defaults. */
    protected static final Object[] PROPERTY_DEFAULTS =
        { null, null, null, null, null, null, null, null, null, null, null, null, null};

    // this is the number of valuestores of each kind
    // we expect an element to have.  It's almost
    // never > 1; so leave it at that.
    protected static final int ID_CONSTRAINT_NUM = 1;
    
    // xsi:* attribute declarations
    static final XSAttributeDecl XSI_TYPE = SchemaGrammar.SG_XSI.getGlobalAttributeDecl(SchemaSymbols.XSI_TYPE);
    static final XSAttributeDecl XSI_NIL = SchemaGrammar.SG_XSI.getGlobalAttributeDecl(SchemaSymbols.XSI_NIL);
    static final XSAttributeDecl XSI_SCHEMALOCATION = SchemaGrammar.SG_XSI.getGlobalAttributeDecl(SchemaSymbols.XSI_SCHEMALOCATION);
    static final XSAttributeDecl XSI_NONAMESPACESCHEMALOCATION = SchemaGrammar.SG_XSI.getGlobalAttributeDecl(SchemaSymbols.XSI_NONAMESPACESCHEMALOCATION);

    //
    protected static final Hashtable EMPTY_TABLE = new Hashtable();

    //
    // Data
    //

    /** current PSVI element info */
    protected ElementPSVImpl fCurrentPSVI = new ElementPSVImpl();

    // since it is the responsibility of each component to an
    // Augmentations parameter if one is null, to save ourselves from
    // having to create this object continually, it is created here.
    // If it is not present in calls that we're passing on, we *must*
    // clear this before we introduce it into the pipeline.
    protected final AugmentationsImpl fAugmentations = new AugmentationsImpl();

    // this is included for the convenience of handleEndElement
    protected XMLString fDefaultValue;

    // Validation features
    protected boolean fDynamicValidation = false;
    protected boolean fSchemaDynamicValidation = false;
    protected boolean fDoValidation = false;
    protected boolean fFullChecking = false;
    protected boolean fNormalizeData = true;
    protected boolean fSchemaElementDefault = true;
    protected boolean fAugPSVI = true;
    protected boolean fIdConstraint = false;
    protected boolean fUseGrammarPoolOnly = false;

    // Namespace growth feature
    protected boolean fNamespaceGrowth = false;
    
    /** Schema type: None, DTD, Schema */
    protected String fSchemaType = null;

    // to indicate whether we are in the scope of entity reference or CData
    protected boolean fEntityRef = false;
    protected boolean fInCDATA = false;

    // properties

    /** Symbol table. */
    protected SymbolTable fSymbolTable;

    /**
     * While parsing a document, keep the location of the document.
     */
    protected XMLLocator fLocator;

    protected ArrayList fXSITypeErrors = new ArrayList(4);

    protected IDContext fIDContext = null;
    
    protected String fDatatypeXMLVersion = null;
    
    protected NamespaceContext fNamespaceContext = null;
    
    /** Error reporter. */
    protected final XSIErrorReporter fXSIErrorReporter = new XSIErrorReporter();

    /** Entity resolver */
    protected XMLEntityResolver fEntityResolver;

    // updated during reset
    protected ValidationManager fValidationManager = null;
    protected XSValidationState fValidationState = new XSValidationState();
    protected XMLGrammarPool fGrammarPool;

    // schema location property values
    protected String fExternalSchemas = null;
    protected String fExternalNoNamespaceSchema = null;

    //JAXP Schema Source property
    protected Object fJaxpSchemaSource = null;

    /** Schema Grammar Description passed,  to give a chance to application to supply the Grammar */
    protected final XSDDescription fXSDDescription = new XSDDescription();
    protected final Hashtable fLocationPairs = new Hashtable();
    protected final Hashtable fExpandedLocationPairs = new Hashtable();
    protected final ArrayList fUnparsedLocations = new ArrayList();

    /** XML Schema 1.1 Support */
    short fSchemaVersion;

    /** XML Schema Constraints */
    XSConstraints fXSConstraints;

    // handlers

    /** Document handler. */
    protected XMLDocumentHandler fDocumentHandler;

    protected XMLDocumentSource fDocumentSource;
    
    // constants

    static final int INITIAL_STACK_SIZE = 8;
    static final int INC_STACK_SIZE = 8;

    //
    // Data
    //

    // Schema Normalization

    protected static final boolean DEBUG_NORMALIZATION = false;
    // temporary empty string buffer.
    protected final XMLString fEmptyXMLStr = new XMLString(null, 0, -1);
    // temporary character buffer, and empty string buffer.
    protected static final int BUFFER_SIZE = 20;
    protected final XMLString fNormalizedStr = new XMLString();
    protected boolean fFirstChunk = true;
    // got first chunk in characters() (SAX)
    protected boolean fTrailing = false; // Previous chunk had a trailing space
    protected short fWhiteSpace = -1; //whiteSpace: preserve/replace/collapse
    protected boolean fUnionType = false;

    /** Schema grammar resolver. */
    protected final XSGrammarBucket fGrammarBucket = new XSGrammarBucket();
    protected final SubstitutionGroupHandler fSubGroupHandler = new SubstitutionGroupHandler(this);

    /** the DV usd to convert xsi:type to a QName */
    // REVISIT: in new simple type design, make things in DVs static,
    //          so that we can QNameDV.getCompiledForm()
    //          using 1.0 xs:QName
    protected final XSSimpleType fQNameDV =
        (XSSimpleType) SchemaGrammar.getS4SGrammar(Constants.SCHEMA_VERSION_1_0).getGlobalTypeDecl(SchemaSymbols.ATTVAL_QNAME);

    protected final CMNodeFactory nodeFactory = new CMNodeFactory();
    /** used to build content models */
    // REVISIT: create decl pool, and pass it to each traversers
    protected final CMBuilder fCMBuilder = new CMBuilder(nodeFactory);
    
    // Schema grammar loader
    protected final XMLSchemaLoader fSchemaLoader =
        new XMLSchemaLoader(
                fXSIErrorReporter.fErrorReporter,
                fGrammarBucket,
                fSubGroupHandler,
                fCMBuilder);

    // state

    /** String representation of the validation root. */
    // REVISIT: what do we store here? QName, XPATH, some ID? use rawname now.
    protected String fValidationRoot;

    /** Skip validation: anything below this level should be skipped */
    protected int fSkipValidationDepth;

    /** anything above this level has validation_attempted != full */
    protected int fNFullValidationDepth;

    /** anything above this level has validation_attempted != none */
    protected int fNNoneValidationDepth;

    /** Element depth: -2: validator not in pipeline; >= -1 current depth. */
    protected int fElementDepth;

    /** Seen sub elements. */
    protected boolean fSubElement;

    /** Seen sub elements stack. */
    protected boolean[] fSubElementStack = new boolean[INITIAL_STACK_SIZE];

    /** Current element declaration. */
    protected XSElementDecl fCurrentElemDecl;

    /** Element decl stack. */
    protected XSElementDecl[] fElemDeclStack = new XSElementDecl[INITIAL_STACK_SIZE];

    /** nil value of the current element */
    protected boolean fNil;

    /** nil value stack */
    protected boolean[] fNilStack = new boolean[INITIAL_STACK_SIZE];

    /** notation value of the current element */
    protected XSNotationDecl fNotation;

    /** notation stack */
    protected XSNotationDecl[] fNotationStack = new XSNotationDecl[INITIAL_STACK_SIZE];

    /** Current type. */
    protected XSTypeDefinition fCurrentType;
    
    /** Failed assertions. */
    protected ObjectList fFailedAssertions;
    
    /** Type Alternative augmentation information. */
    protected XSTypeAlternative fTypeAlternative;

    /** type stack. */
    protected XSTypeDefinition[] fTypeStack = new XSTypeDefinition[INITIAL_STACK_SIZE];

    /** Current content model. */
    protected XSCMValidator fCurrentCM;

    /** Content model stack. */
    protected XSCMValidator[] fCMStack = new XSCMValidator[INITIAL_STACK_SIZE];

    /** the current state of the current content model */
    protected int[] fCurrCMState;

    /** stack to hold content model states */
    protected int[][] fCMStateStack = new int[INITIAL_STACK_SIZE][];

    /** whether the curret element is strictly assessed */
    protected boolean fStrictAssess = true;

    /** strict assess stack */
    protected boolean[] fStrictAssessStack = new boolean[INITIAL_STACK_SIZE];

    /** Temporary string buffers. */
    protected final StringBuffer fBuffer = new StringBuffer();

    /** Whether need to append characters to fBuffer */
    protected boolean fAppendBuffer = true;

    /** Did we see any character data? */
    protected boolean fSawText = false;

    /** stack to record if we saw character data */
    protected boolean[] fSawTextStack = new boolean[INITIAL_STACK_SIZE];

    /** Did we see non-whitespace character data? */
    protected boolean fSawCharacters = false;

    /** Stack to record if we saw character data outside of element content*/
    protected boolean[] fStringContent = new boolean[INITIAL_STACK_SIZE];

    /** temporary qname */
    protected final QName fTempQName = new QName();
    
    /** value of the "root-type-definition" property. */
    protected javax.xml.namespace.QName fRootTypeQName = null;
    protected XSTypeDefinition fRootTypeDefinition = null;
    
    /** value of the "root-element-declaration" property. */
    protected javax.xml.namespace.QName fRootElementDeclQName = null;
    protected XSElementDecl fRootElementDeclaration = null;
    
    protected int fIgnoreXSITypeDepth;
    
    protected boolean fIDCChecking;

    protected boolean fTypeAlternativesChecking;
    
    protected boolean fCommentsAndPIsForAssert;

    /** temporary validated info */
    protected ValidatedInfo fValidatedInfo = new ValidatedInfo();

    // used to validate default/fixed values against xsi:type
    // only need to check facets, so we set extraChecking to false (in reset)
    protected ValidationState fState4XsiType = new ValidationState();

    // used to apply default/fixed values
    // only need to check id/idref/entity, so we set checkFacets to false
    protected ValidationState fState4ApplyDefault = new ValidationState();

    // identity constraint information

    /**
     * Stack of active XPath matchers for identity constraints. All
     * active XPath matchers are notified of startElement
     * and endElement callbacks in order to perform their matches.
     * <p>
     * For each element with identity constraints, the selector of
     * each identity constraint is activated. When the selector matches
     * its XPath, then all the fields of the identity constraint are
     * activated.
     * <p>
     * <strong>Note:</strong> Once the activation scope is left, the
     * XPath matchers are automatically removed from the stack of
     * active matchers and no longer receive callbacks.
     */
    protected XPathMatcherStack fMatcherStack = new XPathMatcherStack();

    /** Cache of value stores for identity constraint fields. */
    protected ValueStoreCache fValueStoreCache = new ValueStoreCache();
    
    // assertion validator subcomponent
    protected XSDAssertionValidator fAssertionValidator = null;

    // variable to track validity of element content for simpleType->union. if a member type of union in XSD namespace can
    // successfully validate an element instance value, we don't process assertions for such union types in downstream checks. i.e.,
    // an element instance known to be valid doesn't require assertion evaluations.
    boolean fIsAssertProcessingNeededForSTUnionElem = true;

    // variable with similar semantics as fIsAssertProcessingNeededForSTUnionElem, but for attributes of an element instance (i.e.,
    // all attributes of one element instance).
    List fIsAssertProcessingNeededForSTUnionAttrs = new ArrayList();
    
    // 'type alternative' validator subcomponent
    protected XSDTypeAlternativeValidator fTypeAlternativeValidator = null;
    
    // a Vector list storing inheritable attributes
    Vector fInheritableAttrList = new Vector();
    
    // a Stack storing inheritable attribute count for the elements
    protected IntStack fInhrAttrCountStack = new IntStack();
    
    // Implements XSElementDeclHelper interface
    public XSElementDecl getGlobalElementDecl(QName element) {
        final SchemaGrammar sGrammar =
            findSchemaGrammar(
                XSDDescription.CONTEXT_ELEMENT,
                element.uri,
                null,
                element,
                null);
        if (sGrammar != null) {
            return sGrammar.getGlobalElementDecl(element.localpart);
        }
        return null;
    }
    
    /**
     * A wrapper of the standard error reporter. We'll store all schema errors
     * in this wrapper object, so that we can get all errors (error codes) of
     * a specific element. This is useful for PSVI.
     */
    protected final class XSIErrorReporter {

        // the error reporter property
        XMLErrorReporter fErrorReporter;

        // store error codes; starting position of the errors for each element;
        // number of element (depth); and whether to record error
        Vector fErrors = new Vector();
        int[] fContext = new int[INITIAL_STACK_SIZE];
        int fContextCount;

        // set the external error reporter, clear errors
        public void reset(XMLErrorReporter errorReporter) {
            fErrorReporter = errorReporter;
            fErrors.removeAllElements();
            fContextCount = 0;
        }

        // should be called when starting process an element or an attribute.
        // store the starting position for the current context
        public void pushContext() {
            if (!fAugPSVI) {
                return;
            }
            // resize array if necessary
            if (fContextCount == fContext.length) {
                int newSize = fContextCount + INC_STACK_SIZE;
                int[] newArray = new int[newSize];
                System.arraycopy(fContext, 0, newArray, 0, fContextCount);
                fContext = newArray;
            }

            fContext[fContextCount++] = fErrors.size();
        }

        // should be called on endElement: get all errors of the current element
        public String[] popContext() {
            if (!fAugPSVI) {
                return null;
            }
            // get starting position of the current element
            int contextPos = fContext[--fContextCount];
            // number of errors of the current element
            int size = fErrors.size() - contextPos;
            // if no errors, return null
            if (size == 0)
                return null;
            // copy errors from the list to an string array
            String[] errors = new String[size];
            for (int i = 0; i < size; i++) {
                errors[i] = (String) fErrors.elementAt(contextPos + i);
            }
            // remove errors of the current element
            fErrors.setSize(contextPos);
            return errors;
        }

        // should be called when an attribute is done: get all errors of
        // this attribute, but leave the errors to the containing element
        // also called after an element was strictly assessed.
        public String[] mergeContext() {
            if (!fAugPSVI) {
                return null;
            }
            // get starting position of the current element
            int contextPos = fContext[--fContextCount];
            // number of errors of the current element
            int size = fErrors.size() - contextPos;
            // if no errors, return null
            if (size == 0)
                return null;
            // copy errors from the list to an string array
            String[] errors = new String[size];
            for (int i = 0; i < size; i++) {
                errors[i] = (String) fErrors.elementAt(contextPos + i);
            }
            // don't resize the vector: leave the errors for this attribute
            // to the containing element
            return errors;
        }

        public void reportError(String domain, String key, Object[] arguments, short severity)
            throws XNIException {
            String message = fErrorReporter.reportError(domain, key, arguments, severity);
            if (fAugPSVI) {
                fErrors.addElement(key);
                fErrors.addElement(message);
            }
        } // reportError(String,String,Object[],short)

        public void reportError(
            XMLLocator location,
            String domain,
            String key,
            Object[] arguments,
            short severity)
            throws XNIException {
            String message = fErrorReporter.reportError(location, domain, key, arguments, severity);
            if (fAugPSVI) {
                fErrors.addElement(key);
                fErrors.addElement(message);
            }
        } // reportError(XMLLocator,String,String,Object[],short)
    }
    
    //this is the function where logic of retrieving grammar is written , parser first tries to get the grammar from
    //the local pool, if not in local pool, it gives chance to application to be able to retrieve the grammar, then it
    //tries to parse the grammar using location hints from the give namespace.
    SchemaGrammar findSchemaGrammar(short contextType, String namespace,
                                    QName enclosingElement, QName triggeringComponent,
                                    XMLAttributes attributes) {
        SchemaGrammar grammar = null;
        //get the grammar from local pool...
        grammar = fGrammarBucket.getGrammar(namespace);
        if (grammar == null) {
            fXSDDescription.setNamespace(namespace);
            if (fGrammarPool != null) {
                grammar = (SchemaGrammar) fGrammarPool.retrieveGrammar(fXSDDescription);
                if (grammar != null) {
                    // put this grammar into the bucket, along with grammars
                    // imported by it (directly or indirectly)
                    if (!fGrammarBucket.putGrammar(grammar, true, fNamespaceGrowth)) {
                        // REVISIT: a conflict between new grammar(s) and grammars
                        // in the bucket. What to do? A warning? An exception?
                        fXSIErrorReporter.fErrorReporter.reportError(
                            XSMessageFormatter.SCHEMA_DOMAIN,
                            "GrammarConflict",
                            null,
                            XMLErrorReporter.SEVERITY_WARNING);
                        grammar = null;
                    }
                }
            }
        }

        if (!fUseGrammarPoolOnly && (grammar == null || 
            (fNamespaceGrowth && !hasSchemaComponent(grammar, contextType, triggeringComponent)))) {
            fXSDDescription.reset();
            fXSDDescription.fContextType = contextType;
            fXSDDescription.setNamespace(namespace);
            fXSDDescription.fEnclosedElementName = enclosingElement;
            fXSDDescription.fTriggeringComponent = triggeringComponent;
            fXSDDescription.fAttributes = attributes;
            if (fLocator != null) {
                fXSDDescription.setBaseSystemId(fLocator.getExpandedSystemId());
            }

            Hashtable locationPairs = fLocationPairs;
            Object locationArray =
                locationPairs.get(namespace == null ? XMLSymbols.EMPTY_STRING : namespace);
            if (locationArray != null) {
                String[] temp = ((XMLSchemaLoader.LocationArray) locationArray).getLocationArray();
                if (temp.length != 0) {
                    setLocationHints(fXSDDescription, temp, grammar);
                }
            }

            if (grammar == null || fXSDDescription.fLocationHints != null) {
                boolean toParseSchema = true;
                if (grammar != null) {
                     // use location hints instead
                    locationPairs = EMPTY_TABLE;
                }

                // try to parse the grammar using location hints from that namespace..
                try {
                    XMLInputSource xis =
                        XMLSchemaLoader.resolveDocument(
                            fXSDDescription,
                            locationPairs,
                            fEntityResolver);
                    if (grammar != null && fNamespaceGrowth) {
                        try {
                            // if we are dealing with a different schema location, then include the new schema
                            // into the existing grammar
                            if (grammar.getDocumentLocations().contains(XMLEntityManager.expandSystemId(xis.getSystemId(), xis.getBaseSystemId(), false))) {
                                toParseSchema = false; 
                            }
                        }
                        catch (MalformedURIException e) {
                        }
                    }
                    if (toParseSchema) {
                        grammar = fSchemaLoader.loadSchema(fXSDDescription, xis, fLocationPairs);
                    }
                } 
                catch (IOException ex) {
                    final String [] locationHints = fXSDDescription.getLocationHints();
                    fXSIErrorReporter.fErrorReporter.reportError(
                        XSMessageFormatter.SCHEMA_DOMAIN,
                        "schema_reference.4",
                        new Object[] { locationHints != null ? locationHints[0] : XMLSymbols.EMPTY_STRING },
                        XMLErrorReporter.SEVERITY_WARNING, ex);
                }
            }
        }

        return grammar;

    } // findSchemaGrammar
    
    private boolean hasSchemaComponent(SchemaGrammar grammar, short contextType, QName triggeringComponent) {
        if (grammar != null && triggeringComponent != null) {
            String localName = triggeringComponent.localpart;
            if (localName != null && localName.length() > 0) {
                switch (contextType) {
                    case XSDDescription.CONTEXT_ELEMENT:
                        return grammar.getElementDeclaration(localName) != null;
                    case XSDDescription.CONTEXT_ATTRIBUTE:
                        return grammar.getAttributeDeclaration(localName) != null;
                    case XSDDescription.CONTEXT_XSITYPE:
                        return grammar.getTypeDefinition(localName) != null;
                }
            }
        }
        return false;
    }

    private void setLocationHints(XSDDescription desc, String[] locations, SchemaGrammar grammar) {
        int length = locations.length;
        if (grammar == null) {
            fXSDDescription.fLocationHints = new String[length];
            System.arraycopy(locations, 0, fXSDDescription.fLocationHints, 0, length);
        }
        else {
            setLocationHints(desc, locations, grammar.getDocumentLocations());
        }
    }
   
    private void setLocationHints(XSDDescription desc, String[] locations, StringList docLocations) {
        int length = locations.length;
        String[] hints = new String[length];
        int counter = 0;

        for (int i=0; i<length; i++) {
            if (!docLocations.contains(locations[i])) {
                hints[counter++] = locations[i];
            }
        }

        if (counter > 0) {
            if (counter == length) {
                fXSDDescription.fLocationHints = hints;
            }
            else {
                fXSDDescription.fLocationHints = new String[counter];
                System.arraycopy(hints, 0, fXSDDescription.fLocationHints, 0, counter);
            }
        }
    }
    
    void reportSchemaError(String key, Object[] arguments) {
        if (fDoValidation)
            fXSIErrorReporter.reportError(
                XSMessageFormatter.SCHEMA_DOMAIN,
                key,
                arguments,
                XMLErrorReporter.SEVERITY_ERROR);
    }
    
    // xpath matcher information

    /**
     * Stack of XPath matchers for identity constraints.
     */
    protected static class XPathMatcherStack {

        //
        // Data
        //

        /** Active matchers. */
        protected XPathMatcher[] fMatchers = new XPathMatcher[4];

        /** Count of active matchers. */
        protected int fMatchersCount;

        /** Offset stack for contexts. */
        protected IntStack fContextStack = new IntStack();

        //
        // Constructors
        //

        public XPathMatcherStack() {
        } // <init>()

        //
        // Public methods
        //

        /** Resets the XPath matcher stack. */
        public void clear() {
            for (int i = 0; i < fMatchersCount; i++) {
                fMatchers[i] = null;
            }
            fMatchersCount = 0;
            fContextStack.clear();
        } // clear()

        /** Returns the size of the stack. */
        public int size() {
            return fContextStack.size();
        } // size():int

        /** Returns the count of XPath matchers. */
        public int getMatcherCount() {
            return fMatchersCount;
        } // getMatcherCount():int

        /** Adds a matcher. */
        public void addMatcher(XPathMatcher matcher) {
            ensureMatcherCapacity();
            fMatchers[fMatchersCount++] = matcher;
        } // addMatcher(XPathMatcher)

        /** Returns the XPath matcher at the specified index. */
        public XPathMatcher getMatcherAt(int index) {
            return fMatchers[index];
        } // getMatcherAt(index):XPathMatcher

        /** Pushes a new context onto the stack. */
        public void pushContext() {
            fContextStack.push(fMatchersCount);
        } // pushContext()

        /** Pops a context off of the stack. */
        public void popContext() {
            fMatchersCount = fContextStack.pop();
        } // popContext()

        //
        // Private methods
        //

        /** Ensures the size of the matchers array. */
        private void ensureMatcherCapacity() {
            if (fMatchersCount == fMatchers.length) {
                XPathMatcher[] array = new XPathMatcher[fMatchers.length * 2];
                System.arraycopy(fMatchers, 0, array, 0, fMatchers.length);
                fMatchers = array;
            }
        } // ensureMatcherCapacity()

    } // class XPathMatcherStack


    // the purpose of this class is to enable IdentityConstraint,int
    // pairs to be used easily as keys in Hashtables.
    protected static final class LocalIDKey {

        public IdentityConstraint fId;
        public int fDepth;

        public LocalIDKey() {
        }

        public LocalIDKey(IdentityConstraint id, int depth) {
            fId = id;
            fDepth = depth;
        } // init(IdentityConstraint, int)

        // object method
        public int hashCode() {
            return fId.hashCode() + fDepth;
        }

        public boolean equals(Object localIDKey) {
            if (localIDKey instanceof LocalIDKey) {
                LocalIDKey lIDKey = (LocalIDKey) localIDKey;
                return (lIDKey.fId == fId && lIDKey.fDepth == fDepth);
            }
            return false;
        }
    } // class LocalIDKey
    
    // value store implementations
    
    /**
     * Value store implementation base class. There are specific subclasses
     * for handling unique, key, and keyref.
     */
    protected abstract class ValueStoreBase implements ValueStore {

        //
        // Data
        //

        /** Identity constraint. */
        protected IdentityConstraint fIdentityConstraint;
        protected int fFieldCount = 0;
        protected Field[] fFields = null;
        protected String fElementName;
        /** current data */
        protected Object[] fLocalValues = null;
        protected short[] fLocalValueTypes = null;
        protected ShortList[] fLocalItemValueTypes = null;

        /** Current data value count. */
        protected int fValuesCount;
        protected boolean fHasValue = false;

        /** global data */
        public final Vector fValues = new Vector();
        public ShortVector fValueTypes = null;
        public Vector fItemValueTypes = null;
        
        private boolean fUseValueTypeVector = false;
        private int fValueTypesLength = 0; 
        private short fValueType = 0;
        
        private boolean fUseItemValueTypeVector = false;
        private int fItemValueTypesLength = 0;
        private ShortList fItemValueType = null;

        /** buffer for error messages */
        final StringBuffer fTempBuffer = new StringBuffer();

        //
        // Constructors
        //

        /** Constructs a value store for the specified identity constraint. */
        protected ValueStoreBase(IdentityConstraint identityConstraint, String elementName) {
            fElementName = elementName;
            fIdentityConstraint = identityConstraint;
            fFieldCount = fIdentityConstraint.getFieldCount();
            fFields = new Field[fFieldCount];
            fLocalValues = new Object[fFieldCount];
            fLocalValueTypes = new short[fFieldCount];
            fLocalItemValueTypes = new ShortList[fFieldCount];
            for (int i = 0; i < fFieldCount; i++) {
                fFields[i] = fIdentityConstraint.getFieldAt(i);
            }
        } // <init>(IdentityConstraint)

        //
        // Public methods
        //

        // destroys this ValueStore; useful when, for instance, a
        // locally-scoped ID constraint is involved.
        public void clear() {
            fValuesCount = 0;
            fUseValueTypeVector = false;
            fValueTypesLength = 0; 
            fValueType = 0;
            fUseItemValueTypeVector = false;
            fItemValueTypesLength = 0;
            fItemValueType = null;
            fValues.setSize(0);
            if (fValueTypes != null) {
                fValueTypes.clear();
            }
            if (fItemValueTypes != null) {
                fItemValueTypes.setSize(0);
            }
        } // end clear():void

        // appends the contents of one ValueStore to those of us.
        public void append(ValueStoreBase newVal) {
            for (int i = 0; i < newVal.fValues.size(); i++) {
                fValues.addElement(newVal.fValues.elementAt(i));

                // REVISIT:
                addValueType(newVal.getValueTypeAt(i));
                addItemValueType(newVal.getItemValueTypeAt(i));
            }
        } // append(ValueStoreBase)

        /** Start scope for value store. */
        public void startValueScope() {
            fValuesCount = 0;
            for (int i = 0; i < fFieldCount; i++) {
                fLocalValues[i] = null;
                fLocalValueTypes[i] = 0;
                fLocalItemValueTypes[i] = null;
            }
        } // startValueScope()

        /** Ends scope for value store. */
        public void endValueScope() {

            if (fValuesCount == 0) {
                if (fIdentityConstraint.getCategory() == IdentityConstraint.IC_KEY) {
                    String code = "AbsentKeyValue";
                    String cName = fIdentityConstraint.getIdentityConstraintName();
                    reportSchemaError(code, new Object[] { fElementName, cName });
                }
                return;
            }

            // Validation Rule: Identity-constraint Satisfied
            // 4.2 If the {identity-constraint category} is key, then all of the following must be true:
            // 4.2.1 The target node set and the qualified node set are equal, that is, every member of the 
            // target node set is also a member of the qualified node set and vice versa.
            //
            // If the IDC is a key check whether we have all the fields.
            if (fValuesCount != fFieldCount) {
                if (fIdentityConstraint.getCategory() == IdentityConstraint.IC_KEY) {
                    String code = "KeyNotEnoughValues";
                    UniqueOrKey key = (UniqueOrKey) fIdentityConstraint;
                    String cName = key.getIdentityConstraintName();
                    reportSchemaError(code, new Object[] { fElementName, cName });
                }
                return;
            }

        } // endValueScope()

        // This is needed to allow keyref's to look for matched keys
        // in the correct scope.  Unique and Key may also need to
        // override this method for purposes of their own.
        // This method is called whenever the DocumentFragment
        // of an ID Constraint goes out of scope.
        public void endDocumentFragment() {
        } // endDocumentFragment():void

        /**
         * Signals the end of the document. This is where the specific
         * instances of value stores can verify the integrity of the
         * identity constraints.
         */
        public void endDocument() {
        } // endDocument()

        //
        // ValueStore methods
        //

        /* reports an error if an element is matched
         * has nillable true and is matched by a key.
         */

        public void reportError(String key, Object[] args) {
            reportSchemaError(key, args);
        } // reportError(String,Object[])

        /**
         * Adds the specified value to the value store.
         *
         * @param field The field associated to the value. This reference
         *              is used to ensure that each field only adds a value
         *              once within a selection scope.
         * @param mayMatch a flag indiciating whether the field may be matched.
         * @param actualValue The value to add.
         * @param valueType Type of the value to add.
         * @param itemValueType If the value is a list, a list of types for each of the values in the list.
         */
        public void addValue(Field field, boolean mayMatch, Object actualValue, short valueType, ShortList itemValueType) {
            int i;
            for (i = fFieldCount - 1; i > -1; i--) {
                if (fFields[i] == field) {
                    break;
                }
            }
            // do we even know this field?
            if (i == -1) {
                String code = "UnknownField";
                String cName = fIdentityConstraint.getIdentityConstraintName();
                reportSchemaError(code, new Object[] { field.toString(), fElementName, cName });
                return;
            }
            if (!mayMatch) {
                String code = "FieldMultipleMatch";
                String cName = fIdentityConstraint.getIdentityConstraintName();
                reportSchemaError(code, new Object[] { field.toString(), cName });
            } 
            else {
                fValuesCount++;
                fHasValue = true;
            }
            fLocalValues[i] = actualValue;
            fLocalValueTypes[i] = valueType;
            fLocalItemValueTypes[i] = itemValueType;
            if (fValuesCount == fFieldCount) {
                checkDuplicateValues();
                // store values
                for (i = 0; i < fFieldCount; i++) {
                    fValues.addElement(fLocalValues[i]);
                    addValueType(fLocalValueTypes[i]);
                    addItemValueType(fLocalItemValueTypes[i]);
                }
            }
        } // addValue(String,Field)

        /**
         * Sets the name of the element which holds the identity constraint
         * that is stored in this value store
         */
        public void setElementName(String elementName) {
            fElementName = elementName;
        }

        /**
         * Returns the name of the element which holds the identity constraint 
         * that is stored in this value store
         */
        public String getElementName() {
            return fElementName;
        } // getElementName():String

        /**
         * Returns true if this value store contains the locally scoped value stores
         */
        public boolean contains() {
            // REVISIT: we can improve performance by using hash codes, instead of
            // traversing global vector that could be quite large.
            int next = 0;
            final int size = fValues.size();
            LOOP : for (int i = 0; i < size; i = next) {
                next = i + fFieldCount;
                for (int j = 0; j < fFieldCount; j++) {
                    final Object value1 = fLocalValues[j];
                    final Object value2 = fValues.elementAt(i);
                    final short valueType1 = fLocalValueTypes[j];
                    final short valueType2 = getValueTypeAt(i);
                    final ShortList typeList1 = isListType(valueType1) ? fLocalItemValueTypes[j] : null;
                    final ShortList typeList2 = isListType(valueType2) ? getItemValueTypeAt(i) : null;

                    if (!EqualityHelper.isEqual(value1, value2, valueType1, valueType2, typeList1, typeList2, fSchemaVersion)) {
                        continue LOOP;
                    }
                    i++;
                }
                // found it
                return true;
            }
            // didn't find it
            return false;
        } // contains():boolean

        /**
         * Returns -1 if this value store contains the specified
         * values, otherwise the index of the first field in the
         * key sequence.
         */
        public int contains(ValueStoreBase vsb) {
            
            final Vector values = vsb.fValues;         
            final int size1 = values.size();
            if (fFieldCount <= 1) {
                LOOP: for (int i = 0; i < size1; ++i) {
                    final Object value1 = values.elementAt(i);
                    final short valueType1 = vsb.getValueTypeAt(i);
                    final ShortList typeList1 = isListType(valueType1) ? vsb.getItemValueTypeAt(i) : null;
                    for (int j=0; j < fValues.size(); ++j) {
                        final Object value2 = fValues.elementAt(j);
                        final short valueType2 = getValueTypeAt(j);
                        final ShortList typeList2 = isListType(valueType2) ? getItemValueTypeAt(j) : null;
                        if (EqualityHelper.isEqual(value1, value2, valueType1, valueType2, typeList1, typeList2, fSchemaVersion)) {
                            continue LOOP;
                        }
                    }
                    return i;
                }
            }
            /** Handle n-tuples. **/
            else {
                final int size2 = fValues.size();
                /** Iterate over each set of fields. **/
                OUTER: for (int i = 0; i < size1; i += fFieldCount) {
                    /** Check whether this set is contained in the value store. **/
                    INNER: for (int j = 0; j < size2; j += fFieldCount) {
                        for (int k = 0; k < fFieldCount; ++k) {
                            final Object value1 = values.elementAt(i+k);
                            final Object value2 = fValues.elementAt(j+k);
                            final short valueType1 = vsb.getValueTypeAt(i+k);
                            final short valueType2 = getValueTypeAt(j+k);
                            final ShortList typeList1 = isListType(valueType1) ? vsb.getItemValueTypeAt(i+k) : null;
                            final ShortList typeList2 = isListType(valueType2) ? getItemValueTypeAt(j+k) : null;
                            
                            if (!EqualityHelper.isEqual(value1, value2, valueType1, valueType2, typeList1, typeList2, fSchemaVersion)) {
                                continue INNER;
                            }
                        }
                        continue OUTER;
                    }
                    return i;
                }
            }
            return -1;
            
        } // contains(Vector):Object

        //
        // Protected methods
        //

        protected void checkDuplicateValues() {
            // no-op
        } // duplicateValue(Hashtable)

        /** Returns a string of the specified values. */
        protected String toString(Object[] values) {

            // no values
            int size = values.length;
            if (size == 0) {
                return "";
            }

            fTempBuffer.setLength(0);

            // construct value string
            for (int i = 0; i < size; i++) {
                if (i > 0) {
                    fTempBuffer.append(',');
                }
                fTempBuffer.append(values[i]);
            }
            return fTempBuffer.toString();

        } // toString(Object[]):String
        
        /** Returns a string of the specified values. */
        protected String toString(Vector values, int start, int length) {

            // no values
            if (length == 0) {
                return "";
            }
            
            // one value
            if (length == 1) {
                return String.valueOf(values.elementAt(start));
            }

            // construct value string
            StringBuffer str = new StringBuffer();
            for (int i = 0; i < length; i++) {
                if (i > 0) {
                    str.append(',');
                }
                str.append(values.elementAt(start + i));
            }
            return str.toString();

        } // toString(Vector,int,int):String

        //
        // Object methods
        //

        /** Returns a string representation of this object. */
        public String toString() {
            String s = super.toString();
            int index1 = s.lastIndexOf('$');
            if (index1 != -1) {
                s = s.substring(index1 + 1);
            }
            int index2 = s.lastIndexOf('.');
            if (index2 != -1) {
                s = s.substring(index2 + 1);
            }
            return s + '[' + fIdentityConstraint + ']';
        } // toString():String
        
        //
        // Private methods
        //
        private boolean isListType(short type) {
            return type == XSConstants.LIST_DT || type == XSConstants.LISTOFUNION_DT;
        }
        
        private void addValueType(short type) {
            if (fUseValueTypeVector) {
                fValueTypes.add(type);
            }
            else if (fValueTypesLength++ == 0) {
                fValueType = type;
            }
            else if (fValueType != type) {
                fUseValueTypeVector = true;
                if (fValueTypes == null) {
                    fValueTypes = new ShortVector(fValueTypesLength * 2);
                }
                for (int i = 1; i < fValueTypesLength; ++i) {
                    fValueTypes.add(fValueType);
                }
                fValueTypes.add(type);
            }
        }
        
        private short getValueTypeAt(int index) {
            if (fUseValueTypeVector) {
                return fValueTypes.valueAt(index);
            }
            return fValueType;
        }
        
        private void addItemValueType(ShortList itemValueType) {
            if (fUseItemValueTypeVector) {
                fItemValueTypes.add(itemValueType);
            }
            else if (fItemValueTypesLength++ == 0) {
                fItemValueType = itemValueType;
            }
            else if (!(fItemValueType == itemValueType ||
                    (fItemValueType != null && fItemValueType.equals(itemValueType)))) {
                fUseItemValueTypeVector = true;
                if (fItemValueTypes == null) {
                    fItemValueTypes = new Vector(fItemValueTypesLength * 2);
                }
                for (int i = 1; i < fItemValueTypesLength; ++i) {
                    fItemValueTypes.add(fItemValueType);
                }
                fItemValueTypes.add(itemValueType);
            }
        }
        
        private ShortList getItemValueTypeAt(int index) {
            if (fUseItemValueTypeVector) {
                return (ShortList) fItemValueTypes.elementAt(index);
            }
            return fItemValueType;
        }
        
    } // class ValueStoreBase
    
    /**
     * Key value store.
     */
    protected class KeyValueStore extends ValueStoreBase {

        // REVISIT: Implement a more efficient storage mechanism. -Ac

        //
        // Constructors
        //

        /** Constructs a key value store. */
        public KeyValueStore(UniqueOrKey key, String elementName) {
            super(key, elementName);
        } // <init>(Key)

        //
        // ValueStoreBase protected methods
        //

        /**
         * Called when a duplicate value is added.
         */
        protected void checkDuplicateValues() {
            if (contains()) {
                String code = "DuplicateKey";
                String value = toString(fLocalValues);
                String cName = fIdentityConstraint.getIdentityConstraintName();
                reportSchemaError(code, new Object[] { value, fElementName, cName });
            }
        } // duplicateValue(Hashtable)

    } // class KeyValueStore

    /**
     * Key reference value store.
     */
    protected class KeyRefValueStore extends ValueStoreBase {

        //
        // Data
        //

        /** Key value store. */
        protected ValueStoreBase fKeyValueStore;

        //
        // Constructors
        //

        /** Constructs a key value store. */
        public KeyRefValueStore(KeyRef keyRef, KeyValueStore keyValueStore, String elementName) { 
            super(keyRef, elementName);
            fKeyValueStore = keyValueStore;
        } // <init>(KeyRef)

        //
        // ValueStoreBase methods
        //

        // end the value Scope; here's where we have to tie
        // up keyRef loose ends.
        public void endDocumentFragment() {

            // do all the necessary management...
            super.endDocumentFragment();

            // verify references
            // get the key store corresponding (if it exists):
            fKeyValueStore =
                (ValueStoreBase) fValueStoreCache.fGlobalIDConstraintMap.get(
                    ((KeyRef) fIdentityConstraint).getKey());

            if (fKeyValueStore == null) {
                // report error
                String code = "KeyRefOutOfScope";
                String value = fIdentityConstraint.getName();
                reportSchemaError(code, new Object[] { value });
                return;
            }
            int errorIndex = fKeyValueStore.contains(this);
            if (errorIndex != -1) {
                String code = "KeyNotFound";
                String values = toString(fValues, errorIndex, fFieldCount);
                String name = fIdentityConstraint.getName();
                reportSchemaError(code, new Object[] { name, values, fElementName });
            }

        } // endDocumentFragment()

        /** End document. */
        public void endDocument() {
            super.endDocument();

        } // endDocument()

    } // class KeyRefValueStore
    
    // a utility method for Identity constraints
    private void activateSelectorFor(IdentityConstraint ic) {
        Selector selector = ic.getSelector();
        FieldActivator activator = this;
        if (selector == null)
            return;
        XPathMatcher matcher = selector.createMatcher(activator, fElementDepth);
        if (fSchemaVersion == Constants.SCHEMA_VERSION_1_1) {
            matcher.setXPathDefaultNamespace(selector.getXPathDefaultNamespace());
        }
        fMatcherStack.addMatcher(matcher);
        matcher.startDocumentFragment();
    }
    
    //
    // FieldActivator methods
    //
    
    /**
     * Request to activate the specified field. This method returns the
     * matcher for the field.
     *
     * @param field The field to activate.
     */
    public XPathMatcher activateField(Field field, int initialDepth) {
        ValueStore valueStore =
            fValueStoreCache.getValueStoreFor(field.getIdentityConstraint(), initialDepth);
        XPathMatcher matcher = field.createMatcher(valueStore);
        if (fSchemaVersion == Constants.SCHEMA_VERSION_1_1) {
            matcher.setXPathDefaultNamespace(field.getXPathDefaultNamespace());
        }
        fMatcherStack.addMatcher(matcher);
        matcher.startDocumentFragment();
        return matcher;
    } // activateField(Field):XPathMatcher
    
    /**
     * Start the value scope for the specified identity constraint. This
     * method is called when the selector matches in order to initialize
     * the value store.
     *
     * @param identityConstraint The identity constraint.
     */
    public void startValueScopeFor(IdentityConstraint identityConstraint, int initialDepth) {

        ValueStoreBase valueStore =
            fValueStoreCache.getValueStoreFor(identityConstraint, initialDepth);
        valueStore.startValueScope();

    } // startValueScopeFor(IdentityConstraint identityConstraint)
    
    /**
     * Ends the value scope for the specified identity constraint.
     *
     * @param identityConstraint The identity constraint.
     */
    public void endValueScopeFor(IdentityConstraint identityConstraint, int initialDepth) {

        ValueStoreBase valueStore =
            fValueStoreCache.getValueStoreFor(identityConstraint, initialDepth);
        valueStore.endValueScope();

    } // endValueScopeFor(IdentityConstraint)
    
    /**
     * Unique value store.
     */
    protected class UniqueValueStore extends ValueStoreBase {

        //
        // Constructors
        //

        /** Constructs a unique value store. */
        public UniqueValueStore(UniqueOrKey unique, String elementName) {
            super(unique, elementName);
        } // <init>(Unique)

        //
        // ValueStoreBase protected methods
        //

        /**
         * Called when a duplicate value is added.
         */
        protected void checkDuplicateValues() {
            // is this value as a group duplicated?
            if (contains()) {
                String code = "DuplicateUnique";
                String value = toString(fLocalValues);
                String cName = fIdentityConstraint.getIdentityConstraintName();
                reportSchemaError(code, new Object[] { value, fElementName, cName });
            }
        } // duplicateValue(Hashtable)

    } // class UniqueValueStore
    
    // value store management

    /**
     * Value store cache. This class is used to store the values for
     * identity constraints.
     */
    protected class ValueStoreCache {

        //
        // Data
        //
        final LocalIDKey fLocalId = new LocalIDKey();
        // values stores

        /** stores all global Values stores. */
        protected final ArrayList fValueStores = new ArrayList();

        /**
         * Values stores associated to specific identity constraints.
         * This hashtable maps IdentityConstraints and
         * the 0-based element on which their selectors first matched to
         * a corresponding ValueStore.  This should take care
         * of all cases, including where ID constraints with
         * descendant-or-self axes occur on recursively-defined
         * elements.
         */
        protected final HashMap fIdentityConstraint2ValueStoreMap = new HashMap();

        // sketch of algorithm:
        // - when a constraint is first encountered, its
        //   values are stored in the (local) fIdentityConstraint2ValueStoreMap;
        // - Once it is validated (i.e., when it goes out of scope),
        //   its values are merged into the fGlobalIDConstraintMap;
        // - as we encounter keyref's, we look at the global table to
        //    validate them.
        //
        // The fGlobalIDMapStack has the following structure:
        // - validation always occurs against the fGlobalIDConstraintMap
        // (which comprises all the "eligible" id constraints);
        // When an endElement is found, this Hashtable is merged with the one
        // below in the stack.
        // When a start tag is encountered, we create a new
        // fGlobalIDConstraintMap.
        // i.e., the top of the fGlobalIDMapStack always contains
        // the preceding siblings' eligible id constraints;
        // the fGlobalIDConstraintMap contains descendants+self.
        // keyrefs can only match descendants+self.
        protected final Stack fGlobalMapStack = new Stack();
        protected final HashMap fGlobalIDConstraintMap = new HashMap();

        //
        // Constructors
        //

        /** Default constructor. */
        public ValueStoreCache() {
        } // <init>()

        //
        // Public methods
        //

        /** Resets the identity constraint cache. */
        public void startDocument() {
            fValueStores.clear();
            fIdentityConstraint2ValueStoreMap.clear();
            fGlobalIDConstraintMap.clear();
            fGlobalMapStack.removeAllElements();
        } // startDocument()

        // startElement:  pushes the current fGlobalIDConstraintMap
        // onto fGlobalMapStack and clears fGlobalIDConstraint map.
        public void startElement() {
            // only clone the map when there are elements
            if (fGlobalIDConstraintMap.size() > 0)
                fGlobalMapStack.push(fGlobalIDConstraintMap.clone());
            else
                fGlobalMapStack.push(null);
            fGlobalIDConstraintMap.clear();
        } // startElement(void)

        /** endElement():  merges contents of fGlobalIDConstraintMap with the
         * top of fGlobalMapStack into fGlobalIDConstraintMap.
         */
        public void endElement() {
            if (fGlobalMapStack.isEmpty()) {
                return; // must be an invalid doc!
            }
            HashMap oldMap = (HashMap) fGlobalMapStack.pop();
            // return if there is no element
            if (oldMap == null) {
                return;
            }

            Iterator entries = oldMap.entrySet().iterator();
            while (entries.hasNext()) {
                Map.Entry entry = (Map.Entry) entries.next();
                IdentityConstraint id = (IdentityConstraint) entry.getKey();
                ValueStoreBase oldVal = (ValueStoreBase) entry.getValue();
                if (oldVal != null) {
                    ValueStoreBase currVal = (ValueStoreBase) fGlobalIDConstraintMap.get(id);
                    if (currVal == null) {
                        fGlobalIDConstraintMap.put(id, oldVal);
                    }
                    else if (currVal != oldVal) {
                        currVal.append(oldVal);
                    }
                }
            }
        } // endElement()

        /**
         * Initializes the value stores for the specified element
         * declaration.
         */
        public void initValueStoresFor(XSElementDecl eDecl, FieldActivator activator) {
            // initialize value stores for unique fields
            IdentityConstraint[] icArray = eDecl.fIDConstraints;
            int icCount = eDecl.fIDCPos;
            for (int i = 0; i < icCount; i++) {
                switch (icArray[i].getCategory()) {
                    case (IdentityConstraint.IC_UNIQUE) :
                        // initialize value stores for unique fields
                        UniqueOrKey unique = (UniqueOrKey) icArray[i];
                        LocalIDKey toHash = new LocalIDKey(unique, fElementDepth);
                        UniqueValueStore uniqueValueStore =
                            (UniqueValueStore) fIdentityConstraint2ValueStoreMap.get(toHash);
                        if (uniqueValueStore == null) {
                            uniqueValueStore = new UniqueValueStore(unique, eDecl.getName());
                            fIdentityConstraint2ValueStoreMap.put(toHash, uniqueValueStore);
                        } else {
                            uniqueValueStore.clear();
                            uniqueValueStore.setElementName(eDecl.getName());
                        }
                        fValueStores.add(uniqueValueStore);
                        activateSelectorFor(icArray[i]);
                        break;
                    case (IdentityConstraint.IC_KEY) :
                        // initialize value stores for key fields
                        UniqueOrKey key = (UniqueOrKey) icArray[i];
                        toHash = new LocalIDKey(key, fElementDepth);
                        KeyValueStore keyValueStore =
                            (KeyValueStore) fIdentityConstraint2ValueStoreMap.get(toHash);
                        if (keyValueStore == null) {
                            keyValueStore = new KeyValueStore(key, eDecl.getName());
                            fIdentityConstraint2ValueStoreMap.put(toHash, keyValueStore);
                        } else {
                            keyValueStore.clear();
                            keyValueStore.setElementName(eDecl.getName());
                        }
                        fValueStores.add(keyValueStore);
                        activateSelectorFor(icArray[i]);
                        break;
                    case (IdentityConstraint.IC_KEYREF) :
                        // initialize value stores for keyRef fields
                        KeyRef keyRef = (KeyRef) icArray[i];
                        toHash = new LocalIDKey(keyRef, fElementDepth);
                        KeyRefValueStore keyRefValueStore =
                            (KeyRefValueStore) fIdentityConstraint2ValueStoreMap.get(toHash);
                        if (keyRefValueStore == null) {
                            keyRefValueStore = new KeyRefValueStore(keyRef, null, eDecl.getName());
                            fIdentityConstraint2ValueStoreMap.put(toHash, keyRefValueStore);
                        } else {
                            keyRefValueStore.clear();
                            keyRefValueStore.setElementName(eDecl.getName());
                        }
                        fValueStores.add(keyRefValueStore);
                        activateSelectorFor(icArray[i]);
                        break;
                }
            }
        } // initValueStoresFor(XSElementDecl)

        /** Returns the value store associated to the specified IdentityConstraint. */
        public ValueStoreBase getValueStoreFor(IdentityConstraint id, int initialDepth) {
            fLocalId.fDepth = initialDepth;
            fLocalId.fId = id;
            return (ValueStoreBase) fIdentityConstraint2ValueStoreMap.get(fLocalId);
        } // getValueStoreFor(IdentityConstraint, int):ValueStoreBase

        /** Returns the global value store associated to the specified IdentityConstraint. */
        public ValueStoreBase getGlobalValueStoreFor(IdentityConstraint id) {
            return (ValueStoreBase) fGlobalIDConstraintMap.get(id);
        } // getValueStoreFor(IdentityConstraint):ValueStoreBase

        // This method takes the contents of the (local) ValueStore
        // associated with id and moves them into the global
        // hashtable, if id is a <unique> or a <key>.
        // If it's a <keyRef>, then we leave it for later.
        public void transplant(IdentityConstraint id, int initialDepth) {
            fLocalId.fDepth = initialDepth;
            fLocalId.fId = id;
            ValueStoreBase newVals =
                (ValueStoreBase) fIdentityConstraint2ValueStoreMap.get(fLocalId);
            if (id.getCategory() == IdentityConstraint.IC_KEYREF)
                return;
            ValueStoreBase currVals = (ValueStoreBase) fGlobalIDConstraintMap.get(id);
            if (currVals != null) {
                currVals.append(newVals);
                fGlobalIDConstraintMap.put(id, currVals);
            } else
                fGlobalIDConstraintMap.put(id, newVals);

        } // transplant(id)

        /** Check identity constraints. */
        public void endDocument() {

            int count = fValueStores.size();
            for (int i = 0; i < count; i++) {
                ValueStoreBase valueStore = (ValueStoreBase) fValueStores.get(i);
                valueStore.endDocument();
            }

        } // endDocument()

        //
        // Object methods
        //

        /** Returns a string representation of this object. */
        public String toString() {
            String s = super.toString();
            int index1 = s.lastIndexOf('$');
            if (index1 != -1) {
                return s.substring(index1 + 1);
            }
            int index2 = s.lastIndexOf('.');
            if (index2 != -1) {
                return s.substring(index2 + 1);
            }
            return s;
        } // toString():String

    } // class ValueStoreCache
    
    /**
     * A simple vector for <code>short</code>s.
     */
    protected static final class ShortVector {
        
        //
        // Data
        //

        /** Current length. */
        private int fLength;

        /** Data. */
        private short[] fData;
        
        //
        // Constructors
        //
        
        public ShortVector() {}
        
        public ShortVector(int initialCapacity) {
            fData = new short[initialCapacity];
        }

        //
        // Public methods
        //

        /** Returns the length of the vector. */
        public int length() {
            return fLength;
        }

        /** Adds the value to the vector. */
        public void add(short value) {
            ensureCapacity(fLength + 1);
            fData[fLength++] = value;
        }

        /** Returns the short value at the specified position in the vector. */
        public short valueAt(int position) {
            return fData[position];
        }

        /** Clears the vector. */
        public void clear() {
            fLength = 0;
        }
        
        /** Returns whether the short is contained in the vector. */
        public boolean contains(short value) {
            for (int i = 0; i < fLength; ++i) {
                if (fData[i] == value) {
                    return true;
                }
            }
            return false;
        }

        //
        // Private methods
        //

        /** Ensures capacity. */
        private void ensureCapacity(int size) {
            if (fData == null) {
                fData = new short[8];
            }
            else if (fData.length <= size) {
                short[] newdata = new short[fData.length * 2];
                System.arraycopy(fData, 0, newdata, 0, fData.length);
                fData = newdata;
            }
        }
        
    } // class ShortVector
    
    XSDAssertionValidator getAssertionValidator() {
        return fAssertionValidator;
    }

    void setIsAssertProcessingNeededForSTUnionElem(boolean isAssertProcessingNeededForSTUnionElem) {
        this.fIsAssertProcessingNeededForSTUnionElem = isAssertProcessingNeededForSTUnionElem;
    }

    List getIsAssertProcessingNeededForSTUnionAttrs() {
        return fIsAssertProcessingNeededForSTUnionAttrs;
    }
    
    Vector getInheritableAttrList() {
        return fInheritableAttrList;
    }

} // XMLSchemaValidatorBase