Package org.apache.xerces.impl.xs.traversers

Source Code of org.apache.xerces.impl.xs.traversers.XSDHandler$LocationResolver

/*
* 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.impl.xs.traversers;

import org.apache.xerces.impl.xs.XSGrammarResolver;
import org.apache.xerces.impl.xs.XSParticleDecl;
import org.apache.xerces.impl.xs.XSElementDecl;
import org.apache.xerces.impl.xs.SchemaNamespaceSupport;
import org.apache.xerces.impl.xs.SchemaGrammar;
import org.apache.xerces.impl.xs.XSComplexTypeDecl;
import org.apache.xerces.impl.xs.SchemaSymbols;
import org.apache.xerces.impl.xs.XSMessageFormatter;
import org.apache.xerces.impl.xs.XMLSchemaValidator;
import org.apache.xerces.parsers.StandardParserConfiguration;
import org.apache.xerces.impl.XMLErrorReporter;
import org.apache.xerces.impl.XMLEntityManager;
import org.apache.xerces.parsers.DOMParser;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.parser.XMLEntityResolver;
import org.apache.xerces.xni.parser.XMLInputSource;
import org.apache.xerces.util.SymbolTable;
import org.apache.xerces.util.SymbolHash;
import org.apache.xerces.util.DOMUtil;

import org.w3c.dom.Document;
import org.apache.xerces.dom.DocumentImpl;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;

import java.util.Hashtable;
import java.util.Stack;
import java.util.Vector;
import java.util.StringTokenizer;
import java.io.IOException;

/**
* The purpose of this class is to co-ordinate the construction of a
* grammar object corresponding to a schema.  To do this, it must be
* prepared to parse several schema documents (for instance if the
* schema document originally referred to contains <include> or
* <redefined> information items).  If any of the schemas imports a
* schema, other grammars may be constructed as a side-effect.
*
* @author Neil Graham, IBM
* @version $Id: XSDHandler.java,v 1.7 2001/12/10 23:30:16 sandygao Exp $
*/

public class XSDHandler {

    // data

    // different sorts of declarations; should make lookup and
    // traverser calling more efficient/less bulky.
    final static int ATTRIBUTE_TYPE          = 1;
    final static int ATTRIBUTEGROUP_TYPE     = 2;
    final static int ELEMENT_TYPE            = 3;
    final static int GROUP_TYPE              = 4;
    final static int IDENTITYCONSTRAINT_TYPE = 5;
    final static int NOTATION_TYPE           = 6;
    final static int TYPEDECL_TYPE           = 7;

    // this string gets appended to redefined names; it's purpose is to be
    // as unlikely as possible to cause collisions.
    public final static String REDEF_IDENTIFIER = "_fn3dktizrknc9pi";

    // please note the difference between SchemaHandler.EMPTY_STRING and
    // SchemaSymbols.EMPTY_STRING:
    //   the one in SchemaHandler is only for namespace binding purpose, it's
    //   used as a legal prefix, and it's added to the current symbol table;
    //   while the one in SchemaSymbols is for general purpose: just empty.
    public String EMPTY_STRING;

    //
    //protected data that can be accessable by any traverser
    // stores <notation> decl
    protected Hashtable fNotationRegistry = new Hashtable();

    // These tables correspond to the symbol spaces defined in the
    // spec.
    // They are keyed with a QName (that is, String("URI,localpart) and
    // their values are nodes corresponding to the given name's decl.
    // By asking the node for its ownerDocument and looking in
    // XSDocumentInfoRegistry we can easily get the corresponding
    // XSDocumentInfo object.
    private Hashtable fUnparsedAttributeRegistry = new Hashtable();
    private Hashtable fUnparsedAttributeGroupRegistry = new Hashtable();
    private Hashtable fUnparsedElementRegistry = new Hashtable();
    private Hashtable fUnparsedGroupRegistry = new Hashtable();
    private Hashtable fUnparsedIdentityConstraintRegistry = new Hashtable();
    private Hashtable fUnparsedNotationRegistry = new Hashtable();
    private Hashtable fUnparsedTypeRegistry = new Hashtable();
    // this is keyed with a documentNode (or the schemaRoot nodes
    // contained in the XSDocumentInfo objects) and its value is the
    // XSDocumentInfo object corresponding to that document.
    // Basically, the function of this registry is to be a link
    // between the nodes we fetch from calls to the fUnparsed*
    // arrays and the XSDocumentInfos they live in.
    private Hashtable fXSDocumentInfoRegistry = new Hashtable();

    // this hashtable is keyed on by XSDocumentInfo objects.  Its values
    // are Vectors containing the XSDocumentInfo objects <include>d,
    // <import>ed or <redefine>d by the key XSDocumentInfo.
    private Hashtable fDependencyMap = new Hashtable();

    // This vector stores strings which are combinations of the
    // publicId and systemId of the inputSource corresponding to a
    // schema document.  This combination is used so that the user's
    // EntityResolver can provide a consistent way of identifying a
    // schema document that is included in multiple other schemas.
    private SymbolHash fTraversed = new SymbolHash();

    // this hashtable contains a mapping from Document to its systemId
    // this is useful to resolve a uri relative to the referring document
    private Hashtable fDoc2SystemId = new Hashtable();

    // the primary XSDocumentInfo we were called to parse
    private XSDocumentInfo fRoot = null;

    // This hashtable's job is to act as a link between the document
    // node at the root of the parsed schema's tree and its
    // XSDocumentInfo object.
    private Hashtable fDoc2XSDocumentMap = new Hashtable();

    // map between <redefine> elements and the XSDocumentInfo
    // objects that correspond to the documents being redefined.
    private Hashtable fRedefine2XSDMap = new Hashtable();

    // these objects store a mapping between the names of redefining
    // groups/attributeGroups and the groups/AttributeGroups which
    // they redefine by restriction (implicitly).  It is up to the
    // Group and AttributeGroup traversers to check these restrictions for
    // validity.
    private Hashtable fRedefinedRestrictedAttributeGroupRegistry = new Hashtable();
    private Hashtable fRedefinedRestrictedGroupRegistry = new Hashtable();

    // a variable storing whether the last schema document
    // processed (by getSchema) was a duplicate.
    private boolean fLastSchemaWasDuplicate;

    // the XMLErrorReporter
    private XMLErrorReporter fErrorReporter;

    // the XSAttributeChecker
    private XSAttributeChecker fAttributeChecker;

    // this class is to make use of the schema location property values.
    // we store the namespace/location pairs in a hashtable (use "" as the
    // namespace of absent namespace). when resolving an entity, we first try
    // to find in the hashtable whether there is a value for that namespace,
    // if so, pass that location value to the user-defined entity resolver.
    protected class LocationResolver {
        // the user-defined entity resolver
        public XMLEntityResolver fExternalResolver = null;
        // namespace/location pairs
        public Hashtable fLocationPairs = new Hashtable();

        public void reset(XMLEntityResolver entityResolver,
                          String sLocation, String nsLocation) {
            fLocationPairs.clear();
            fExternalResolver = entityResolver;

            if (sLocation != null) {
                StringTokenizer t = new StringTokenizer(sLocation, " \n\t\r");
                String namespace, location;
                while (t.hasMoreTokens()) {
                    namespace = t.nextToken ();
                    if (!t.hasMoreTokens()) {
                        break;
                    }
                    location = t.nextToken();
                    fLocationPairs.put(namespace, location);
                }
            }
            if (nsLocation != null) {
                fLocationPairs.put(EMPTY_STRING, nsLocation);
            }
        }

        public XMLInputSource resolveEntity(String namespace, String location, String base, boolean useProperties) throws IOException {
            if (fExternalResolver == null)
                return null;

            String loc = null;
            // we consider the schema location properties for import
            if (useProperties) {
                // use empty string as the key for absent namespace
                String ns = namespace == null ? EMPTY_STRING : namespace;
                // get the location hint for that namespace
                loc = (String)fLocationPairs.get(ns);
            }

            // if it's not import, or if the target namespace is not set
            // in the schema location properties, use location hint
            if (loc == null)
                loc = location;

            // REVISIT: resolve the entity. passing null as public id, instead
            // of passing namespace value. -SG
            return fExternalResolver.resolveEntity(null, loc, base);
        }
    }

    // the schema location resolver
    private LocationResolver fLocationResolver = new LocationResolver();

    // the symbol table
    private SymbolTable fSymbolTable;

    // the GrammarResolver
    private XSGrammarResolver fGrammarResolver;

    //************ Traversers **********
    XSDAttributeGroupTraverser fAttributeGroupTraverser;
    XSDAttributeTraverser fAttributeTraverser;
    XSDComplexTypeTraverser fComplexTypeTraverser;
    XSDElementTraverser fElementTraverser;
    XSDGroupTraverser fGroupTraverser;
    XSDKeyrefTraverser fKeyrefTraverser;
    XSDNotationTraverser fNotationTraverser;
    XSDSimpleTypeTraverser fSimpleTypeTraverser;
    XSDUniqueOrKeyTraverser fUniqueOrKeyTraverser;
    XSDWildcardTraverser fWildCardTraverser;

    DOMParser fSchemaParser;

    // these data members are needed for the deferred traversal
    // of local elements.

    // the initial size of the array to store deferred local elements
    private static final int INIT_STACK_SIZE = 30;
    // the incremental size of the array to store deferred local elements
    private static final int INC_STACK_SIZE  = 10;
    // current position of the array (# of deferred local elements)
    private int fLocalElemStackPos;

    private XSParticleDecl[] fParticle;
    private Element[] fLocalElementDecl;
    private int[] fAllContext;
    private String [][] fLocalElemNamespaceContext;

    // these data members are needed for the deferred traversal
    // of keyrefs.

    // the initial size of the array to store deferred keyrefs
    private static final int INIT_KEYREF_STACK = 2;
    // the incremental size of the array to store deferred keyrefs
    private static final int INC_KEYREF_STACK_AMOUNT = 2;
    // current position of the array (# of deferred keyrefs)
    private int fKeyrefStackPos;

    private Element [] fKeyrefs;
    private XSElementDecl [] fKeyrefElems;
    private String [][] fKeyrefNamespaceContext;

    // Constructors

    // it should be possible to use the same XSDHandler to parse
    // multiple schema documents; this will allow one to be
    // constructed.
    public XSDHandler (XSGrammarResolver gResolver) {
        fGrammarResolver = gResolver;

        // REVISIT: do we use shadowed or synchronized symbol table of
        //          SchemaSymbols.fSymbolTable?
        // REVISIT: don't use SchemaConfiguration internally
        //          we will get stack overflaw because
        //          XMLSchemaValidator will be instantiating XSDHandler...
        fSchemaParser = new DOMParser(new StandardParserConfiguration(new SchemaSymbols.SchemaSymbolTable()));
        // set ErrorHandler and EntityResolver (doesn't seem that
        // XMLErrorHandler or XMLEntityResolver will work with
        // standard DOMParser...
        //REVISIT: disable deferred dom expansion. there are bugs.
        try {
            fSchemaParser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false);
        }
        catch (Exception e) {
        }

        createTraversers();
    } // end constructor

    // This method initiates the parse of a schema.  It will likely be
    // called from the Validator and it will make the
    // resulting grammar available; it returns a reference to this object just
    // in case.  An ErrorHandler, EntityResolver, GrammarPool and SymbolTable must
    // already have been set; the last thing this method does is reset
    // this object (i.e., clean the registries, etc.).
    public SchemaGrammar parseSchema(String schemaNamespace,
                                     String schemaHint) {

        // first phase:  construct trees.
        Document schemaRoot = getSchema(schemaNamespace, schemaHint, null, true);
        if (schemaRoot == null) {
            // something went wrong right off the hop
            reportGenericSchemaError("Could not locate a schema document corresponding to grammar " + schemaNamespace);
            return null;
        }
        // handle empty string URI as null
        if (schemaNamespace != null) {
            schemaNamespace = fSymbolTable.addSymbol(schemaNamespace);
        }
        fRoot = constructTrees(schemaRoot, schemaNamespace);
        if (fRoot == null) {
            // REVISIT:  something went wrong; print error about no schema found
            reportGenericSchemaError("Could not locate a schema document");
            return null;
        }

        // second phase:  fill global registries.
        buildGlobalNameRegistries();

        // third phase:  call traversers
        traverseSchemas();

        // fourth phase: handle local element decls
        traverseLocalElements();

        // fifth phase:  handle Keyrefs
        resolveKeyRefs();

        // sixth phase: validate attribute of non-schema namespaces
        // REVISIT: skip this for now. we reall don't want to do it.
        //fAttributeChecker.checkNonSchemaAttributes(fGrammarResolver);

        // and return.
        return fGrammarResolver.getGrammar(schemaNamespace);
    } // end parseSchema

    // may wish to have setter methods for ErrorHandler,
    // EntityResolver...

    // This method does several things:
    // It constructs an instance of an XSDocumentInfo object using the
    // schemaRoot node.  Then, for each <include>,
    // <redefine>, and <import> children, it attempts to resolve the
    // requested schema document, initiates a DOM parse, and calls
    // itself recursively on that document's root.  It also records in
    // the DependencyMap object what XSDocumentInfo objects its XSDocumentInfo
    // depends on.
    // It also makes sure the targetNamespace of the schema it was
    // called to parse is correct.
    protected XSDocumentInfo constructTrees(Document schemaRoot, String callerTNS) {
        if (schemaRoot == null) return null;
        XSDocumentInfo currSchemaInfo = new XSDocumentInfo(schemaRoot, fAttributeChecker, fSymbolTable);

        // Modified by Pavani Mukthipudi, Sun Microsystems Inc.
        fDoc2XSDocumentMap.put(schemaRoot, currSchemaInfo);

        if (callerTNS != null) {
            // only set if we were included or redefined in.
            if (currSchemaInfo.fTargetNamespace == null) {
                currSchemaInfo.fTargetNamespace = callerTNS;
                currSchemaInfo.fIsChameleonSchema = true;
            }
            else if (callerTNS != currSchemaInfo.fTargetNamespace) {
                reportSchemaError("src-include.2", new Object [] {callerTNS, currSchemaInfo.fTargetNamespace});
            }
        }
        SchemaGrammar sg = null;
        if ((sg = fGrammarResolver.getGrammar(currSchemaInfo.fTargetNamespace)) == null) {
            sg = new SchemaGrammar(fSymbolTable, currSchemaInfo.fTargetNamespace);
            fGrammarResolver.putGrammar(sg);
        }

        Vector dependencies = new Vector();
        Element rootNode = DOMUtil.getRoot(schemaRoot);

        Document newSchemaRoot = null;
        for (Element child = DOMUtil.getFirstChildElement(rootNode);
            child != null;
            child = DOMUtil.getNextSiblingElement(child)) {
            String schemaNamespace=null;
            String schemaHint=null;
            String localName = DOMUtil.getLocalName(child);
            if (localName.equals(SchemaSymbols.ELT_ANNOTATION))
                continue;
            else if (localName.equals(SchemaSymbols.ELT_IMPORT)) {
                // have to handle some validation here too!
                // call XSAttributeChecker to fill in attrs
                Object[] includeAttrs = fAttributeChecker.checkAttributes(child, true, currSchemaInfo);
                schemaHint = (String)includeAttrs[XSAttributeChecker.ATTIDX_SCHEMALOCATION];
                schemaNamespace = (String)includeAttrs[XSAttributeChecker.ATTIDX_NAMESPACE];
                if (schemaNamespace != null)
                    schemaNamespace = fSymbolTable.addSymbol(schemaNamespace);
                if (schemaNamespace == currSchemaInfo.fTargetNamespace) {
                    reportSchemaError("src-import.1.1", new Object [] {schemaNamespace});
                }
                fAttributeChecker.returnAttrArray(includeAttrs, currSchemaInfo);
                // consciously throw away whether was a duplicate; don't care.
                // pass the systemId of the current document as the base systemId
                newSchemaRoot = getSchema(schemaNamespace, schemaHint, (String)fDoc2SystemId.get(schemaRoot), true);
            }
            else if ((localName.equals(SchemaSymbols.ELT_INCLUDE)) ||
                     (localName.equals(SchemaSymbols.ELT_REDEFINE))) {
                // validation for redefine/include will be the same here; just
                // make sure TNS is right (don't care about redef contents
                // yet).
                Object[] includeAttrs = fAttributeChecker.checkAttributes(child, true, currSchemaInfo);
                schemaHint = (String)includeAttrs[XSAttributeChecker.ATTIDX_SCHEMALOCATION];
                fAttributeChecker.returnAttrArray(includeAttrs, currSchemaInfo);
                // schemaLocation is required on <include> and <redefine>
                if (schemaHint == null)
                    reportGenericSchemaError("schemaLocation attribute must appear in <include> and <redefine>");
                // pass the systemId of the current document as the base systemId
                newSchemaRoot = getSchema(null, schemaHint, (String)fDoc2SystemId.get(schemaRoot), false);
                schemaNamespace = currSchemaInfo.fTargetNamespace;
            }
            else {
                // no more possibility of schema references in well-formed
                // schema...
                break;
            }

            // If the schema is duplicate, we needn't call constructTrees() again.
            // To handle mutual <include>s

            // REVISIT: this creates a bug if the same document is both
            //          imported and included. then only the first one takes
            //          effect.

            XSDocumentInfo newSchemaInfo = null;
            if (fLastSchemaWasDuplicate) {
                newSchemaInfo = (XSDocumentInfo)fDoc2XSDocumentMap.get(newSchemaRoot);
            }
            else {
                newSchemaInfo = constructTrees(newSchemaRoot, schemaNamespace);
            }
            if (localName.equals(SchemaSymbols.ELT_REDEFINE) &&
                newSchemaInfo != null) {
                // must record which schema we're redefining so that we can
                // rename the right things later!
                fRedefine2XSDMap.put(child, newSchemaInfo);
            }
            if (newSchemaRoot != null) {
                dependencies.addElement(newSchemaInfo);
                newSchemaRoot = null;
            }
        }
        fDependencyMap.put(currSchemaInfo, dependencies);
        return currSchemaInfo;
    } // end constructTrees

    // This method builds registries for all globally-referenceable
    // names.  A registry will be built for each symbol space defined
    // by the spec.  It is also this method's job to rename redefined
    // components, and to record which components redefine others (so
    // that implicit redefinitions of groups and attributeGroups can be handled).
    protected void buildGlobalNameRegistries() {
        /* Starting with fRoot, we examine each child of the schema
         * element.  Skipping all imports and includes, we record the names
         * of all other global components (and children of <redefine>).  We
         * also put <redefine> names in a registry that we look through in
         * case something needs renaming.  Once we're done with a schema we
         * set its Document node to hidden so that we don't try to traverse
         * it again; then we look to its Dependency map entry.  We keep a
         * stack of schemas that we haven't yet finished processing; this
         * is a depth-first traversal.
         */
        Stack schemasToProcess = new Stack();
        schemasToProcess.push(fRoot);
        while (!schemasToProcess.empty()) {
            XSDocumentInfo currSchemaDoc =
            (XSDocumentInfo)schemasToProcess.pop();
            Document currDoc = currSchemaDoc.fSchemaDoc;
            if (DOMUtil.isHidden(currDoc)) {
                // must have processed this already!
                continue;
            }
            Element currRoot = DOMUtil.getRoot(currDoc);

            // process this schema's global decls
            boolean dependenciesCanOccur = true;
            for (Element globalComp =
                 DOMUtil.getFirstChildElement(currRoot);
                globalComp != null;
                globalComp = DOMUtil.getNextSiblingElement(globalComp)) {
                // this loop makes sure the <schema> element ordering is
                // also valid.
                if (DOMUtil.getLocalName(globalComp).equals(SchemaSymbols.ELT_ANNOTATION)) {
                    //skip it; traverse it later
                    continue;
                }
                else if (DOMUtil.getLocalName(globalComp).equals(SchemaSymbols.ELT_INCLUDE) ||
                         DOMUtil.getLocalName(globalComp).equals(SchemaSymbols.ELT_IMPORT)) {
                    if (!dependenciesCanOccur) {
                        reportSchemaError("sch-props-correct.1", new Object [] {DOMUtil.getLocalName(globalComp)});
                    }
                    // we've dealt with this; mark as traversed
                    DOMUtil.setHidden(globalComp);
                }
                else if (DOMUtil.getLocalName(globalComp).equals(SchemaSymbols.ELT_REDEFINE)) {
                    if (!dependenciesCanOccur) {
                        reportSchemaError("sch-props-correct.1", new Object [] {DOMUtil.getLocalName(globalComp)});
                    }
                    for (Element redefineComp = DOMUtil.getFirstChildElement(globalComp);
                        redefineComp != null;
                        redefineComp = DOMUtil.getNextSiblingElement(redefineComp)) {
                        String lName = DOMUtil.getAttrValue(redefineComp, SchemaSymbols.ATT_NAME);
                        if (lName.length() == 0) // an error we'll catch later
                            continue;
                        String qName = currSchemaDoc.fTargetNamespace == null ?
                                       ","+lName:
                                       currSchemaDoc.fTargetNamespace +","+lName;
                        String componentType = DOMUtil.getLocalName(redefineComp);
                        if (componentType.equals(SchemaSymbols.ELT_ATTRIBUTEGROUP)) {
                            checkForDuplicateNames(qName, fUnparsedAttributeGroupRegistry, redefineComp, currSchemaDoc);
                            // the check will have changed our name;
                            String targetLName = DOMUtil.getAttrValue(redefineComp, SchemaSymbols.ATT_NAME)+REDEF_IDENTIFIER;
                            // and all we need to do is error-check+rename our kkids:
                            renameRedefiningComponents(currSchemaDoc, redefineComp, SchemaSymbols.ELT_ATTRIBUTEGROUP,
                                                       lName, targetLName);
                        }
                        else if ((componentType.equals(SchemaSymbols.ELT_COMPLEXTYPE)) ||
                                 (componentType.equals(SchemaSymbols.ELT_SIMPLETYPE))) {
                            checkForDuplicateNames(qName, fUnparsedTypeRegistry, redefineComp, currSchemaDoc);
                            // the check will have changed our name;
                            String targetLName = DOMUtil.getAttrValue(redefineComp, SchemaSymbols.ATT_NAME) + REDEF_IDENTIFIER;
                            // and all we need to do is error-check+rename our kkids:
                            if (componentType.equals(SchemaSymbols.ELT_COMPLEXTYPE)) {
                                renameRedefiningComponents(currSchemaDoc, redefineComp, SchemaSymbols.ELT_COMPLEXTYPE,
                                                           lName, targetLName);
                            }
                            else { // must be simpleType
                                renameRedefiningComponents(currSchemaDoc, redefineComp, SchemaSymbols.ELT_SIMPLETYPE,
                                                           lName, targetLName);
                            }
                        }
                        else if (componentType.equals(SchemaSymbols.ELT_GROUP)) {
                            checkForDuplicateNames(qName, fUnparsedGroupRegistry, redefineComp, currSchemaDoc);
                            // the check will have changed our name;
                            String targetLName = DOMUtil.getAttrValue(redefineComp, SchemaSymbols.ATT_NAME)+REDEF_IDENTIFIER;
                            // and all we need to do is error-check+rename our kids:
                            renameRedefiningComponents(currSchemaDoc, redefineComp, SchemaSymbols.ELT_GROUP,
                                                       lName, targetLName);
                        }
                    } // end march through <redefine> children
                    // and now set as traversed
                    //DOMUtil.setHidden(globalComp);
                }
                else {
                    dependenciesCanOccur = false;
                    String lName = DOMUtil.getAttrValue(globalComp, SchemaSymbols.ATT_NAME);
                    if (lName.length() == 0) // an error we'll catch later
                        continue;
                    String qName = currSchemaDoc.fTargetNamespace == null?
                                   ","+lName:
                                   currSchemaDoc.fTargetNamespace +","+lName;
                    String componentType = DOMUtil.getLocalName(globalComp);
                    if (componentType.equals(SchemaSymbols.ELT_ATTRIBUTE)) {
                        checkForDuplicateNames(qName, fUnparsedAttributeRegistry, globalComp, currSchemaDoc);
                    }
                    else if (componentType.equals(SchemaSymbols.ELT_ATTRIBUTEGROUP)) {
                        checkForDuplicateNames(qName, fUnparsedAttributeGroupRegistry, globalComp, currSchemaDoc);
                    }
                    else if ((componentType.equals(SchemaSymbols.ELT_COMPLEXTYPE)) ||
                             (componentType.equals(SchemaSymbols.ELT_SIMPLETYPE))) {
                        checkForDuplicateNames(qName, fUnparsedTypeRegistry, globalComp, currSchemaDoc);
                    }
                    else if (componentType.equals(SchemaSymbols.ELT_ELEMENT)) {
                        checkForDuplicateNames(qName, fUnparsedElementRegistry, globalComp, currSchemaDoc);
                    }
                    else if (componentType.equals(SchemaSymbols.ELT_GROUP)) {
                        checkForDuplicateNames(qName, fUnparsedGroupRegistry, globalComp, currSchemaDoc);
                    }
                    else if (componentType.equals(SchemaSymbols.ELT_NOTATION)) {
                        checkForDuplicateNames(qName, fUnparsedNotationRegistry, globalComp, currSchemaDoc);
                    }
                }
            } // end for

            // now we're done with this one!
            DOMUtil.setHidden(currDoc);
            // now add the schemas this guy depends on
            Vector currSchemaDepends = (Vector)fDependencyMap.get(currSchemaDoc);
            for (int i = 0; i < currSchemaDepends.size(); i++) {
                schemasToProcess.push(currSchemaDepends.elementAt(i));
            }
        } // while
    } // end buildGlobalNameRegistries

    // Beginning at the first schema processing was requested for
    // (fRoot), this method
    // examines each child (global schema information item) of each
    // schema document (and of each <redefine> element)
    // corresponding to an XSDocumentInfo object.  If the
    // readOnly field on that node has not been set, it calls an
    // appropriate traverser to traverse it.  Once all global decls in
    // an XSDocumentInfo object have been traversed, it marks that object
    // as traversed (or hidden) in order to avoid infinite loops.  It completes
    // when it has visited all XSDocumentInfo objects in the
    // DependencyMap and marked them as traversed.
    protected void traverseSchemas() {
        // the process here is very similar to that in
        // buildGlobalRegistries, except we can't set our schemas as
        // hidden for a second time; so make them all visible again
        // first!
        setSchemasVisible(fRoot);
        Stack schemasToProcess = new Stack();
        schemasToProcess.push(fRoot);
        while (!schemasToProcess.empty()) {
            XSDocumentInfo currSchemaDoc =
            (XSDocumentInfo)schemasToProcess.pop();
            Document currDoc = currSchemaDoc.fSchemaDoc;
            SchemaGrammar currSG = fGrammarResolver.getGrammar(currSchemaDoc.fTargetNamespace);
            if (DOMUtil.isHidden(currDoc)) {
                // must have processed this already!
                continue;
            }
            Element currRoot = DOMUtil.getRoot(currDoc);

            // traverse this schema's global decls
            for (Element globalComp =
                 DOMUtil.getFirstVisibleChildElement(currRoot);
                globalComp != null;
                globalComp = DOMUtil.getNextVisibleSiblingElement(globalComp)) {
                // We'll always want to set this as hidden!
                DOMUtil.setHidden(globalComp);
                String componentType = DOMUtil.getLocalName(globalComp);
                // includes and imports will not show up here!
                if (DOMUtil.getLocalName(globalComp).equals(SchemaSymbols.ELT_REDEFINE)) {
                    for (Element redefinedComp = DOMUtil.getFirstVisibleChildElement(globalComp);
                        redefinedComp != null;
                        redefinedComp = DOMUtil.getNextVisibleSiblingElement(redefinedComp)) {
                        String redefinedComponentType = DOMUtil.getLocalName(redefinedComp);
                        DOMUtil.setHidden(redefinedComp);
                        if (redefinedComponentType.equals(SchemaSymbols.ELT_ATTRIBUTEGROUP)) {
                            fAttributeGroupTraverser.traverseGlobal(redefinedComp, currSchemaDoc, currSG);
                        }
                        else if (redefinedComponentType.equals(SchemaSymbols.ELT_COMPLEXTYPE)) {
                            fComplexTypeTraverser.traverseGlobal(redefinedComp, currSchemaDoc, currSG);
                        }
                        else if (redefinedComponentType.equals(SchemaSymbols.ELT_GROUP)) {
                            fGroupTraverser.traverseGlobal(redefinedComp, currSchemaDoc, currSG);
                        }
                        else if (redefinedComponentType.equals(SchemaSymbols.ELT_SIMPLETYPE)) {
                            fSimpleTypeTraverser.traverseGlobal(redefinedComp, currSchemaDoc, currSG);
                        }
                        else if (redefinedComponentType.equals(SchemaSymbols.ELT_ANNOTATION)) {
                            // REVISIT:  according to 3.13.2 the PSVI needs the parent's attributes;
                            // thus this should be done in buildGlobalNameRegistries not here...
                            fElementTraverser.traverseAnnotationDecl(redefinedComp, null, true, currSchemaDoc);
                        }
                        else {
                            reportSchemaError("src-redefine", new Object [] {componentType});
                        }
                    } // end march through <redefine> children
                }
                else if (componentType.equals(SchemaSymbols.ELT_ATTRIBUTE)) {
                    fAttributeTraverser.traverseGlobal(globalComp, currSchemaDoc, currSG);
                }
                else if (componentType.equals(SchemaSymbols.ELT_ATTRIBUTEGROUP)) {
                    fAttributeGroupTraverser.traverseGlobal(globalComp, currSchemaDoc, currSG);
                }
                else if (componentType.equals(SchemaSymbols.ELT_COMPLEXTYPE)) {
                    fComplexTypeTraverser.traverseGlobal(globalComp, currSchemaDoc, currSG);
                }
                else if (componentType.equals(SchemaSymbols.ELT_ELEMENT)) {
                    fElementTraverser.traverseGlobal(globalComp, currSchemaDoc, currSG);
                }
                else if (componentType.equals(SchemaSymbols.ELT_GROUP)) {
                    fGroupTraverser.traverseGlobal(globalComp, currSchemaDoc, currSG);
                }
                else if (componentType.equals(SchemaSymbols.ELT_NOTATION)) {
                    fNotationTraverser.traverse(globalComp, currSchemaDoc, currSG);
                }
                else if (componentType.equals(SchemaSymbols.ELT_SIMPLETYPE)) {
                    fSimpleTypeTraverser.traverseGlobal(globalComp, currSchemaDoc, currSG);
                }
                else if (componentType.equals(SchemaSymbols.ELT_ANNOTATION)) {
                    // REVISIT:  according to 3.13.2 the PSVI needs the parent's attributes;
                    // thus this should be done in buildGlobalNameRegistries not here...
                    fElementTraverser.traverseAnnotationDecl(globalComp, null, true, currSchemaDoc);
                }
                else {
                    reportSchemaError("sch-props-correct.1", new Object [] {DOMUtil.getLocalName(globalComp)});
                }
            } // end for

            // now we're done with this one!
            DOMUtil.setHidden(currDoc);
            // now add the schemas this guy depends on
            Vector currSchemaDepends = (Vector)fDependencyMap.get(currSchemaDoc);
            for (int i = 0; i < currSchemaDepends.size(); i++) {
                schemasToProcess.push(currSchemaDepends.elementAt(i));
            }
        } // while
    } // end traverseSchemas

    private static final String[] COMP_TYPE = {
        null,               // index 0
        "attribute declaration",
        "attribute group",
        "elment declaration",
        "group",
        "identity constraint",
        "notation",
        "type definition",
    };

    // since it is forbidden for traversers to talk to each other
    // directly (except wen a traverser encounters a local declaration),
    // this provides a generic means for a traverser to call
    // for the traversal of some declaration.  An XSDocumentInfo is
    // required because the XSDocumentInfo that the traverser is traversing
    // may bear no relation to the one the handler is operating on.
    // This method will:
    // 1.  See if a global definition matching declToTraverse exists;
    // 2. if so, determine if there is a path from currSchema to the
    // schema document where declToTraverse lives (i.e., do a lookup
    // in DependencyMap);
    // 3. depending on declType (which will be relevant to step 1 as
    // well), call the appropriate traverser with the appropriate
    // XSDocumentInfo object.
    // This method returns whatever the traverser it called returned;
    // this will be an Object of some kind
    // that lives in the Grammar.
    protected Object getGlobalDecl(XSDocumentInfo currSchema,
                                   int declType,
                                   QName declToTraverse) {

        // from the schema spec, all built-in types are present in all schemas,
        // so if the requested component is a type, and could be found in the
        // default schema grammar, we should return that type.
        // otherwise (since we would support user-defined schema grammar) we'll
        // use the normal way to get the decl
        if (declToTraverse.uri != null &&
            declToTraverse.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA) {
            if (declType == TYPEDECL_TYPE) {
                Object retObj = SchemaGrammar.SG_SchemaNS.getGlobalTypeDecl(declToTraverse.localpart);
                if (retObj != null)
                    return retObj;
            }
        }

        XSDocumentInfo schemaWithDecl = null;
        Element decl = null;

        String declKey = declToTraverse.uri == null? ","+declToTraverse.localpart:
                         declToTraverse.uri+","+declToTraverse.localpart;
        switch (declType) {
        case ATTRIBUTE_TYPE :
            decl = (Element)fUnparsedAttributeRegistry.get(declKey);
            break;
        case ATTRIBUTEGROUP_TYPE :
            decl = (Element)fUnparsedAttributeGroupRegistry.get(declKey);
            break;
        case ELEMENT_TYPE :
            decl = (Element)fUnparsedElementRegistry.get(declKey);
            break;
        case GROUP_TYPE :
            decl = (Element)fUnparsedGroupRegistry.get(declKey);
            break;
        case IDENTITYCONSTRAINT_TYPE :
            decl = (Element)fUnparsedIdentityConstraintRegistry.get(declKey);
            break;
        case NOTATION_TYPE :
            decl = (Element)fUnparsedNotationRegistry.get(declKey);
            break;
        case TYPEDECL_TYPE :
            decl = (Element)fUnparsedTypeRegistry.get(declKey);
            break;
        default:
            // REVISIT: report internal error...
            reportGenericSchemaError("XSDHandler asked to locate component of type " + declType + "; it does not recognize this type (internal error!)");
        }
        if (decl != null)
            schemaWithDecl = findXSDocumentForDecl(currSchema, decl);
        else {
            reportSchemaError("src-resolve", new Object[]{declToTraverse.rawname, COMP_TYPE[declType]});
            return null;
        }

        if (schemaWithDecl == null) {
            // cannot get to this schema from the one containing the requesting decl
            reportSchemaError("src-resolve.4", new Object[]{currSchema, declToTraverse.uri});
            return null;
        }
        SchemaGrammar sGrammar = fGrammarResolver.getGrammar(schemaWithDecl.fTargetNamespace);

        Object retObj = null;
        switch (declType) {
        case ATTRIBUTE_TYPE :
            retObj = sGrammar.getGlobalAttributeDecl(declToTraverse.localpart);
            break;
        case ATTRIBUTEGROUP_TYPE :
            retObj = sGrammar.getGlobalAttributeGroupDecl(declToTraverse.localpart);
            break;
        case ELEMENT_TYPE :
            retObj = sGrammar.getGlobalElementDecl(declToTraverse.localpart);
            break;
        case GROUP_TYPE :
            retObj = sGrammar.getGlobalGroupDecl(declToTraverse.localpart);
            break;
        case IDENTITYCONSTRAINT_TYPE :
            retObj = sGrammar.getIDConstraintDecl(declToTraverse.localpart);
            break;
        case NOTATION_TYPE :
            retObj = sGrammar.getNotationDecl(declToTraverse.localpart);
            break;
        case TYPEDECL_TYPE :
            retObj = sGrammar.getGlobalTypeDecl(declToTraverse.localpart);
            break;
        }

        if (retObj != null)
            return retObj;

        if (DOMUtil.isHidden(decl)) {
            // decl must not be null if we're here...
            //REVISIT: report an error: circular reference
            reportGenericSchemaError("Circular reference detected in schema component named " + declToTraverse.prefix+":"+declToTraverse.localpart);
            return null;
        }

        DOMUtil.setHidden(decl);

        // back up the current SchemaNamespaceSupport, because we need to provide
        // a fresh one to the traverseGlobal methods.
        schemaWithDecl.backupNSSupport();

        switch (declType) {
        case ATTRIBUTE_TYPE :
            retObj = fAttributeTraverser.traverseGlobal(decl, schemaWithDecl, sGrammar);
            break;
        case ATTRIBUTEGROUP_TYPE :
            retObj = fAttributeGroupTraverser.traverseGlobal(decl, schemaWithDecl, sGrammar);
            break;
        case ELEMENT_TYPE :
            retObj = fElementTraverser.traverseGlobal(decl, schemaWithDecl, sGrammar);
            break;
        case GROUP_TYPE :
            retObj = fGroupTraverser.traverseGlobal(decl, schemaWithDecl, sGrammar);
            break;
        case IDENTITYCONSTRAINT_TYPE :
            // identity constraints should have been parsed already...
            // we should never get here
            retObj = null;
            break;
        case NOTATION_TYPE :
            retObj = fNotationTraverser.traverse(decl, schemaWithDecl, sGrammar);
            break;
        case TYPEDECL_TYPE :
            if (DOMUtil.getLocalName(decl).equals(SchemaSymbols.ELT_COMPLEXTYPE))
                retObj = fComplexTypeTraverser.traverseGlobal(decl, schemaWithDecl, sGrammar);
            else
                retObj = fSimpleTypeTraverser.traverseGlobal(decl, schemaWithDecl, sGrammar);
        }

        // restore the previous SchemaNamespaceSupport, so that the caller can get
        // proper namespace binding.
        schemaWithDecl.restoreNSSupport();

        return retObj;
    } // getGlobalDecl(XSDocumentInfo, int, QName):  Object

    // This method determines whether there is a group
    // (attributeGroup) which the given one has redefined by
    // restriction.  If so, it returns it; else it returns null.
    // @param type:  whether what's been redefined is an
    // attributeGroup or a group;
    // @param name:  the QName of the component doing the redefining.
    // @param currSchema:  schema doc in which the redefining component lives.
    // @return:  Object representing decl redefined if present, null
    // otherwise.
    Object getGrpOrAttrGrpRedefinedByRestriction(int type, QName name, XSDocumentInfo currSchema) {
        String realName = name.uri != null?name.uri+","+name.localpart:
                ","+name.localpart;
        String nameToFind = null;
        switch (type) {
        case ATTRIBUTEGROUP_TYPE:
            nameToFind = (String)fRedefinedRestrictedAttributeGroupRegistry.get(realName);
            break;
        case GROUP_TYPE:
            nameToFind = (String)fRedefinedRestrictedGroupRegistry.get(realName);
            break;
        default:
            return null;
        }
        if (nameToFind == null) return null;
        int commaPos = nameToFind.indexOf(",");
        QName qNameToFind = new QName(EMPTY_STRING, nameToFind.substring(commaPos+1),
            nameToFind.substring(commaPos), (commaPos == 0)? null : nameToFind.substring(0, commaPos));
        Object retObj = getGlobalDecl(currSchema, type, qNameToFind);
        if(retObj == null) {
            switch (type) {
            case ATTRIBUTEGROUP_TYPE:
                reportSchemaError("src-redefine.7.2.1", new Object []{name.localpart});
                break;
            case GROUP_TYPE:
                reportSchemaError("src-redefine.6.2.1", new Object []{name.localpart});
                break;
            }
            return null;
        }
        return retObj;
    } // getGrpOrAttrGrpRedefinedByRestriction(int, QName, XSDocumentInfo):  Object

    // Since ID constraints can occur in local elements, unless we
    // wish to completely traverse all our DOM trees looking for ID
    // constraints while we're building our global name registries,
    // which seems terribly inefficient, we need to resolve keyrefs
    // after all parsing is complete.  This we can simply do by running through
    // fIdentityConstraintRegistry and calling traverseKeyRef on all
    // of the KeyRef nodes.  This unfortunately removes this knowledge
    // from the elementTraverser class (which must ignore keyrefs),
    // but there seems to be no efficient way around this...
    protected void resolveKeyRefs() {
        for (int i=0; i<fKeyrefStackPos; i++) {
            Document keyrefDoc = DOMUtil.getDocument(fKeyrefs[i]);
            XSDocumentInfo keyrefSchemaDoc = (XSDocumentInfo)fDoc2XSDocumentMap.get(keyrefDoc);
            keyrefSchemaDoc.fNamespaceSupport.makeGlobal();
            keyrefSchemaDoc.fNamespaceSupport.setEffectiveContext( fKeyrefNamespaceContext[i] );
            SchemaGrammar keyrefGrammar = fGrammarResolver.getGrammar(keyrefSchemaDoc.fTargetNamespace);
            fKeyrefTraverser.traverse(fKeyrefs[i], fKeyrefElems[i], keyrefSchemaDoc, keyrefGrammar);
        }
    } // end resolveKeyRefs

    // an accessor method.  Just makes sure callers
    // who want the Identity constraint registry vaguely know what they're about.
    protected Hashtable getIDRegistry() {
        return fUnparsedIdentityConstraintRegistry;
    }

    // This method squirrels away <keyref> declarations--along with the element
    // decls and namespace bindings they might find handy.
    protected void storeKeyRef (Element keyrefToStore, XSDocumentInfo schemaDoc,
                                XSElementDecl currElemDecl) {
        String keyrefName = DOMUtil.getAttrValue(keyrefToStore, SchemaSymbols.ATT_NAME);
        if (keyrefName.length() != 0) {
            String keyrefQName = schemaDoc.fTargetNamespace == null?
                                 "," + keyrefName: schemaDoc.fTargetNamespace+","+keyrefName;
            checkForDuplicateNames(keyrefQName, fUnparsedIdentityConstraintRegistry, keyrefToStore, schemaDoc);
        }
        // now set up all the registries we'll need...

        // check array sizes
        if (fKeyrefStackPos == fKeyrefs.length) {
            Element [] elemArray = new Element [fKeyrefStackPos + INC_KEYREF_STACK_AMOUNT];
            System.arraycopy(fKeyrefs, 0, elemArray, 0, fKeyrefStackPos);
            fKeyrefs = elemArray;
            XSElementDecl [] declArray = new XSElementDecl [fKeyrefStackPos + INC_KEYREF_STACK_AMOUNT];
            System.arraycopy(fKeyrefElems, 0, declArray, 0, fKeyrefStackPos);
            fKeyrefElems = declArray;
            String[][] stringArray = new String [fKeyrefStackPos + INC_KEYREF_STACK_AMOUNT][];
            System.arraycopy(fKeyrefNamespaceContext, 0, stringArray, 0, fKeyrefStackPos);
            fKeyrefNamespaceContext = stringArray;
        }
        fKeyrefs[fKeyrefStackPos] = keyrefToStore;
        fKeyrefElems[fKeyrefStackPos] = currElemDecl;
        fKeyrefNamespaceContext[fKeyrefStackPos++] = schemaDoc.fNamespaceSupport.getEffectiveLocalContext();
    } // storeKeyref (Element, XSDocumentInfo, XSElementDecl): void

    // This method is responsible for schema resolution.  If it finds
    // a schema document that the XMLEntityResolver resolves to with
    // the given namespace and hint, it returns it.  It returns true
    // if this is the first time it's seen this document, false
    // otherwise.  schemaDoc is null if and only if no schema document
    // was resolved to.
    private Document getSchema(String schemaNamespace, String schemaHint,
                               String baseSystemId, boolean useProperties) {
        // contents of this method will depend on the system we adopt for entity resolution--i.e., XMLEntityHandler, EntityHandler, etc.
        XMLInputSource schemaSource=null;
        Document schemaDoc = null;
        try {
            schemaSource = fLocationResolver.resolveEntity(schemaNamespace, schemaHint, baseSystemId, useProperties);
            // REVISIT: when the system id and byte stream and character stream
            //          of the input source are all null, it's
            //          impossible to find the schema document. so we skip in
            //          this case. otherwise we'll receive some NPE or
            //          file not found errors. but schemaHint=="" is perfectly
            //          legal for import.
            if (schemaSource != null &&
                (schemaSource.getSystemId() != null ||
                 schemaSource.getByteStream() != null ||
                 schemaSource.getCharacterStream() != null)) {

                // When the system id of the input source is used, first try to
                // expand it, and check whether the same document has been
                // parsed before. If so, return the document corresponding to
                // that system id.
                String schemaId = null;
                if (schemaSource.getByteStream() == null &&
                    schemaSource.getCharacterStream() == null) {
                    schemaId = XMLEntityManager.expandSystemId(schemaSource.getSystemId(), schemaSource.getBaseSystemId());
                    if (fTraversed.get(schemaId) != null) {
                        fLastSchemaWasDuplicate = true;
                        return(Document)(fTraversed.get(schemaId));
                    }
                }
                fSchemaParser.reset();
                fSchemaParser.parse(schemaSource);
                schemaDoc = fSchemaParser.getDocument();
                // now we need to store the mapping information from system id
                // to the document. also from the document to the system id.
                if (schemaId != null) {
                    fTraversed.put(schemaId, schemaDoc );
                    fDoc2SystemId.put(schemaDoc, schemaId );
                }
                fLastSchemaWasDuplicate = false;
                return schemaDoc;
            }

        }
        catch (java.io.FileNotFoundException ex) {
            // REVISIT: how to report an error for missing files
            fErrorReporter.reportError(XSMessageFormatter.SCHEMA_DOMAIN,
                                       "General",
                                       new Object[]{"file not found: " + schemaHint},
                                       XMLErrorReporter.SEVERITY_WARNING);
        }
        catch (IOException ex) {
            // REVISIT: report an error!
            reportGenericSchemaError("error reading schema document: " + schemaHint);
        }

        schemaDoc = null;
        fLastSchemaWasDuplicate = false;
        return null;
    } // getSchema(String, String):  Document

    // initialize all the traversers.
    // this should only need to be called once during the construction
    // of this object; it creates the traversers that will be used to

    // construct schemaGrammars.
    private void createTraversers() {
        fAttributeChecker = new XSAttributeChecker(this);
        fAttributeGroupTraverser = new XSDAttributeGroupTraverser(this, fAttributeChecker);
        fAttributeTraverser = new XSDAttributeTraverser(this, fAttributeChecker);
        fComplexTypeTraverser = new XSDComplexTypeTraverser(this, fAttributeChecker);
        fElementTraverser = new XSDElementTraverser(this, fAttributeChecker);
        fGroupTraverser = new XSDGroupTraverser(this, fAttributeChecker);
        fKeyrefTraverser = new XSDKeyrefTraverser(this, fAttributeChecker);
        fNotationTraverser = new XSDNotationTraverser(this, fAttributeChecker);
        fSimpleTypeTraverser = new XSDSimpleTypeTraverser(this, fAttributeChecker);
        fUniqueOrKeyTraverser = new XSDUniqueOrKeyTraverser(this, fAttributeChecker);
        fWildCardTraverser = new XSDWildcardTraverser(this, fAttributeChecker);
    } // createTraversers()

    // this method clears all the global structs of this object
    // (except those passed in via the constructor).
    public void reset(XMLErrorReporter errorReporter,
                      XMLEntityResolver entityResolver,
                      SymbolTable symbolTable,
                      String externalSchemaLocation,
                      String externalNoNSSchemaLocation) {

        fErrorReporter = errorReporter;
        fSymbolTable = symbolTable;

        EMPTY_STRING = fSymbolTable.addSymbol(SchemaSymbols.EMPTY_STRING);

        fLocationResolver.reset(entityResolver, externalSchemaLocation, externalNoNSSchemaLocation);

        try {
            fSchemaParser.setProperty(XMLSchemaValidator.ERROR_REPORTER, fErrorReporter);
        }
        catch (Exception e) {
        }

        fUnparsedAttributeRegistry.clear();
        fUnparsedAttributeGroupRegistry.clear();
        fUnparsedElementRegistry.clear();
        fUnparsedGroupRegistry.clear();
        fUnparsedIdentityConstraintRegistry.clear();
        fUnparsedNotationRegistry.clear();
        fUnparsedTypeRegistry.clear();

        fXSDocumentInfoRegistry.clear();
        fDependencyMap.clear();
        fTraversed.clear();
        fDoc2SystemId.clear();
        fDoc2XSDocumentMap.clear();
        fRedefine2XSDMap.clear();
        fRoot = null;
        fLastSchemaWasDuplicate = false;

        fLocalElemStackPos = 0;
        fParticle = new XSParticleDecl[INIT_STACK_SIZE];
        fLocalElementDecl = new Element[INIT_STACK_SIZE];
        fAllContext = new int[INIT_STACK_SIZE];
        // err on the small side for num. of local namespaces declared...
        fLocalElemNamespaceContext = new String [INIT_STACK_SIZE][1];

        // and do same for keyrefs.
        fKeyrefStackPos = 0;
        fKeyrefs = new Element[INIT_KEYREF_STACK];
        fKeyrefElems = new XSElementDecl [INIT_KEYREF_STACK];
        fKeyrefNamespaceContext = new String[INIT_KEYREF_STACK][1];

        // reset traversers
        fAttributeChecker.reset(fErrorReporter, fSymbolTable);
        fAttributeGroupTraverser.reset(fErrorReporter, fSymbolTable);
        fAttributeTraverser.reset(fErrorReporter, fSymbolTable);
        fComplexTypeTraverser.reset(fErrorReporter, fSymbolTable);
        fElementTraverser.reset(fErrorReporter, fSymbolTable);
        fGroupTraverser.reset(fErrorReporter, fSymbolTable);
        fKeyrefTraverser.reset(fErrorReporter, fSymbolTable);
        fNotationTraverser.reset(fErrorReporter, fSymbolTable);
        fSimpleTypeTraverser.reset(fErrorReporter, fSymbolTable);
        fUniqueOrKeyTraverser.reset(fErrorReporter, fSymbolTable);
        fWildCardTraverser.reset(fErrorReporter, fSymbolTable);

        fRedefinedRestrictedAttributeGroupRegistry.clear();
        fRedefinedRestrictedGroupRegistry.clear();
    } // reset(ErrorReporter, EntityResolver, SymbolTable)

    /**
     * Traverse all the deferred local elements. This method should be called
     * by traverseSchemas after we've done with all the global declarations.
     */
    void traverseLocalElements() {
        fElementTraverser.fDeferTraversingLocalElements = false;

        for (int i = 0; i < fLocalElemStackPos; i++) {
            Element currElem = fLocalElementDecl[i];
            XSDocumentInfo currSchema = (XSDocumentInfo)fDoc2XSDocumentMap.get(DOMUtil.getDocument(currElem));
            SchemaGrammar currGrammar = fGrammarResolver.getGrammar(currSchema.fTargetNamespace);
            fElementTraverser.traverseLocal (fParticle[i], currElem, currSchema, currGrammar, fAllContext[i]);
        }
    }

    // the purpose of this method is to keep up-to-date structures
    // we'll need for the feferred traversal of local elements.
    void fillInLocalElemInfo(Element elmDecl,
                             XSDocumentInfo schemaDoc,
                             int allContextFlags,
                             XSParticleDecl particle) {

        // if the stack is full, increase the size
        if (fParticle.length == fLocalElemStackPos) {
            // increase size
            XSParticleDecl[] newStackP = new XSParticleDecl[fLocalElemStackPos+INC_STACK_SIZE];
            System.arraycopy(fParticle, 0, newStackP, 0, fLocalElemStackPos);
            fParticle = newStackP;
            Element[] newStackE = new Element[fLocalElemStackPos+INC_STACK_SIZE];
            System.arraycopy(fLocalElementDecl, 0, newStackE, 0, fLocalElemStackPos);
            fLocalElementDecl = newStackE;
            int[] newStackI = new int[fLocalElemStackPos+INC_STACK_SIZE];
            System.arraycopy(fAllContext, 0, newStackI, 0, fLocalElemStackPos);
            fAllContext = newStackI;
            String [][] newStackN = new String [fLocalElemStackPos+INC_STACK_SIZE][];
            System.arraycopy(fLocalElemNamespaceContext, 0, newStackN, 0, fLocalElemStackPos);
            fLocalElemNamespaceContext = newStackN;
        }

        fParticle[fLocalElemStackPos] = particle;
        fLocalElementDecl[fLocalElemStackPos] = elmDecl;
        fAllContext[fLocalElemStackPos] = allContextFlags;
        fLocalElemNamespaceContext[fLocalElemStackPos++] = schemaDoc.fNamespaceSupport.getEffectiveLocalContext();
    } // end fillInLocalElemInfo(...)

    /** This method makes sure that
     * if this component is being redefined that it lives in the
     * right schema.  It then renames the component correctly.  If it
     * detects a collision--a duplicate definition--then it complains.
     * Note that redefines must be handled carefully:  if there
     * is a collision, it may be because we're redefining something we know about
     * or because we've found the thing we're redefining.
     */
    void checkForDuplicateNames(String qName,
                                Hashtable registry, Element currComp,
                                XSDocumentInfo currSchema) {
        Object objElem = null;
        // REVISIT:  when we add derivation checking, we'll have to make
        // sure that ID constraint collisions don't necessarily result in error messages.
        if ((objElem = registry.get(qName)) == null) {
            // just add it in!
            registry.put(qName, currComp);
        }
        else {
            Element collidingElem = (Element)objElem;
            if (collidingElem == currComp) return;
            Element elemParent = null;
            XSDocumentInfo redefinedSchema = null;
            // case where we've collided with a redefining element
            // (the parent of the colliding element is a redefine)
            boolean collidedWithRedefine = true;
            if ((DOMUtil.getLocalName((elemParent = DOMUtil.getParent(collidingElem))).equals(SchemaSymbols.ELT_REDEFINE))) {
                redefinedSchema = (XSDocumentInfo)(fRedefine2XSDMap.get(elemParent));
                // case where we're a redefining element.
            }
            else if ((DOMUtil.getLocalName(DOMUtil.getParent(currComp)).equals(SchemaSymbols.ELT_REDEFINE))) {
                redefinedSchema = (XSDocumentInfo)(fDoc2XSDocumentMap.get(DOMUtil.getDocument(collidingElem)));
                collidedWithRedefine = false;
            }
            if (redefinedSchema != null) { //redefinition involved somehow
                String newName = qName.substring(qName.lastIndexOf(',')+1)+REDEF_IDENTIFIER;
                if (redefinedSchema == currSchema) { // object comp. okay here
                    // now have to do some renaming...
                    currComp.setAttribute(SchemaSymbols.ATT_NAME, newName);
                    if (currSchema.fTargetNamespace == null)
                        registry.put(","+newName, currComp);
                    else
                        registry.put(currSchema.fTargetNamespace+","+newName, currComp);
                    // and take care of nested redefines by calling recursively:
                    if (currSchema.fTargetNamespace == null)
                        checkForDuplicateNames(","+newName, registry, currComp, currSchema);
                    else
                        checkForDuplicateNames(currSchema.fTargetNamespace+","+newName, registry, currComp, currSchema);
                }
                else { // we may be redefining the wrong schema
                    if (collidedWithRedefine) {
                        if (currSchema.fTargetNamespace == null)
                            checkForDuplicateNames(","+newName, registry, currComp, currSchema);
                        else
                            checkForDuplicateNames(currSchema.fTargetNamespace+","+newName, registry, currComp, currSchema);
                    }
                    else {
                        // REVISIT:  error that redefined element in wrong schema
                        reportSchemaError("src-redefine.1", new Object [] {qName});
                    }
                }
            }
            else { // we've just got a flat-out collision
                reportSchemaError("sch-props-correct.2", new Object []{qName});
            }
        }
    } // checkForDuplicateNames(String, Hashtable, Element, XSDocumentInfo):void


    //
    //!!!!!!!!!!!!!!!! IMPLEMENT the following functions !!!!!!!!!!
    //
    //REVISIT: implement namescope support!!!
    protected String resolvePrefixToURI (String prefix) {
        //String uriStr = fStringPool.toString(fNamespacesScope.getNamespaceForPrefix(fStringPool.addSymbol(prefix)));
        //if (uriStr.length() == 0 && prefix.length() > 0) {
        // REVISIT: Localize
        //// REVISIT:  reportGenericSchemaError("prefix : [" + prefix +"] cannot be resolved to a URI");
        //return "";
        //}

        return null;
    }

    // the purpose of this method is to take the component of the
    // specified type and rename references to itself so that they
    // refer to the object being redefined.  It takes special care of
    // <group>s and <attributeGroup>s to ensure that information
    // relating to implicit restrictions is preserved for those
    // traversers.
    private void renameRedefiningComponents(XSDocumentInfo currSchema,
                                            Element child, String componentType,
                                            String oldName, String newName) {

        SchemaNamespaceSupport currNSMap = currSchema.fNamespaceSupport;
        if (componentType.equals(SchemaSymbols.ELT_SIMPLETYPE)) {
            String processedTypeName = currSchema.fTargetNamespace == null?
                                       ","+oldName:currSchema.fTargetNamespace+","+oldName;
            Element grandKid = DOMUtil.getFirstChildElement(child);
            if (grandKid == null) {
                // fRedefineSucceeded = false;
                reportSchemaError("src-redefine.5", null);
            }
            else {
                String grandKidName = grandKid.getLocalName();
                if (grandKidName.equals(SchemaSymbols.ELT_ANNOTATION)) {
                    grandKid = DOMUtil.getNextSiblingElement(grandKid);
                    grandKidName = grandKid.getLocalName();
                }
                if (grandKid == null) {
                    // fRedefineSucceeded = false;
                    reportSchemaError("src-redefine.5", null);
                }
                else if (!grandKidName.equals(SchemaSymbols.ELT_RESTRICTION)) {
                    // fRedefineSucceeded = false;
                    reportSchemaError("src-redefine.5", null);
                }
                else {
                    String derivedBase = grandKid.getAttribute( SchemaSymbols.ATT_BASE );
                    String processedDerivedBase = findQName(derivedBase, currSchema);
                    if (!processedTypeName.equals(processedDerivedBase)) {
                        // fRedefineSucceeded = false;
                        reportSchemaError("src-redefine.5", null);
                    }
                    else {
                        // now we have to do the renaming...
                        int colonptr = derivedBase.indexOf(":");
                        if (colonptr > 0)
                            grandKid.setAttribute( SchemaSymbols.ATT_BASE,
                                                   derivedBase.substring(0,colonptr) + ":" + newName );
                        else
                            grandKid.setAttribute( SchemaSymbols.ATT_BASE, newName );
//                        return true;
                    }
                }
            }
        }
        else if (componentType.equals(SchemaSymbols.ELT_COMPLEXTYPE)) {
            String processedTypeName = currSchema.fTargetNamespace == null?
                                       ","+oldName:currSchema.fTargetNamespace+","+oldName;
            Element grandKid = DOMUtil.getFirstChildElement(child);
            if (grandKid == null) {
                // fRedefineSucceeded = false;
                reportSchemaError("src-redefine.5", null);
            }
            else {
                if (grandKid.getLocalName().equals(SchemaSymbols.ELT_ANNOTATION)) {
                    grandKid = DOMUtil.getNextSiblingElement(grandKid);
                }
                if (grandKid == null) {
                    // fRedefineSucceeded = false;
                    reportSchemaError("src-redefine.5", null);
                }
                else {
                    // have to go one more level down; let another pass worry whether complexType is valid.
                    Element greatGrandKid = DOMUtil.getFirstChildElement(grandKid);
                    if (greatGrandKid == null) {
                        // fRedefineSucceeded = false;
                        reportSchemaError("src-redefine.5", null);
                    }
                    else {
                        String greatGrandKidName = greatGrandKid.getLocalName();
                        if (greatGrandKidName.equals(SchemaSymbols.ELT_ANNOTATION)) {
                            greatGrandKid = DOMUtil.getNextSiblingElement(greatGrandKid);
                            greatGrandKidName = greatGrandKid.getLocalName();
                        }
                        if (greatGrandKid == null) {
                            // fRedefineSucceeded = false;
                            reportSchemaError("src-redefine.5", null);
                        }
                        else if (!greatGrandKidName.equals(SchemaSymbols.ELT_RESTRICTION) &&
                                 !greatGrandKidName.equals(SchemaSymbols.ELT_EXTENSION)) {
                            // fRedefineSucceeded = false;
                            reportSchemaError("src-redefine.5", null);
                        }
                        else {
                            String derivedBase = greatGrandKid.getAttribute( SchemaSymbols.ATT_BASE );
                            String processedDerivedBase = findQName(derivedBase, currSchema);
                            if (!processedTypeName.equals(processedDerivedBase)) {
                                // fRedefineSucceeded = false;
                                reportSchemaError("src-redefine.5", null);
                            }
                            else {
                                // now we have to do the renaming...
                                int colonptr = derivedBase.indexOf(":");
                                if (colonptr > 0)
                                    greatGrandKid.setAttribute( SchemaSymbols.ATT_BASE,
                                                                derivedBase.substring(0,colonptr) + ":" + newName );
                                else
                                    greatGrandKid.setAttribute( SchemaSymbols.ATT_BASE,
                                                                newName );
//                                return true;
                            }
                        }
                    }
                }
            }
        }
        else if (componentType.equals(SchemaSymbols.ELT_ATTRIBUTEGROUP)) {
            String processedBaseName = (currSchema.fTargetNamespace == null)?
                                       ","+oldName:currSchema.fTargetNamespace+","+oldName;
            int attGroupRefsCount = changeRedefineGroup(processedBaseName, componentType, newName, child, currSchema);
            if (attGroupRefsCount > 1) {
                // fRedefineSucceeded = false;
                reportSchemaError("src-redefine.7.1", new Object []{new Integer(attGroupRefsCount)});
            }
            else if (attGroupRefsCount == 1) {
//                return true;
            }
            else
                if (currSchema.fTargetNamespace == null)
                fRedefinedRestrictedAttributeGroupRegistry.put(processedBaseName, ","+newName);
            else
                fRedefinedRestrictedAttributeGroupRegistry.put(processedBaseName, currSchema.fTargetNamespace+","+newName);
        }
        else if (componentType.equals(SchemaSymbols.ELT_GROUP)) {
            String processedBaseName = (currSchema.fTargetNamespace == null)?
                                       ","+oldName:currSchema.fTargetNamespace+","+oldName;
            int groupRefsCount = changeRedefineGroup(processedBaseName, componentType, newName, child, currSchema);
            if (groupRefsCount > 1) {
                // fRedefineSucceeded = false;
                reportSchemaError("src-redefine.6.1.1", new Object []{new Integer(groupRefsCount)});
            }
            else if (groupRefsCount == 1) {
//                return true;
            }
            else {
                if (currSchema.fTargetNamespace == null)
                    fRedefinedRestrictedGroupRegistry.put(processedBaseName, ","+newName);
                else
                    fRedefinedRestrictedGroupRegistry.put(processedBaseName, currSchema.fTargetNamespace+","+newName);
            }
        }
        else {
            // fRedefineSucceeded = false;
            // REVISIT: Localize
            reportGenericSchemaError("internal Xerces error; please submit a bug with schema as testcase");
        }
        // if we get here then we must have reported an error and failed somewhere...
//        return false;
    } // renameRedefiningComponents(XSDocumentInfo, Element, String, String, String):void

    // this method takes a name of the form a:b, determines the URI mapped
    // to by a in the current SchemaNamespaceSupport object, and returns this
    // information in the form (nsURI,b) suitable for lookups in the global
    // decl Hashtables.
    // REVISIT: should have it return QName, instead of String. this would
    //          save lots of string concatenation time. we can use
    //          QName#equals() to compare two QNames, and use QName directly
    //          as a key to the SymbolHash.
    //          And when the DV's are ready to return compiled values from
    //          validate() method, we should just call QNameDV.validate()
    //          in this method.
    private String findQName(String name, XSDocumentInfo schemaDoc) {
        SchemaNamespaceSupport currNSMap = schemaDoc.fNamespaceSupport;
        int colonPtr = name.indexOf(':');
        String prefix = EMPTY_STRING;
        if (colonPtr > 0)
            prefix = name.substring(0, colonPtr);
        String uri = currNSMap.getURI(fSymbolTable.addSymbol(prefix));
        String localpart = (colonPtr == 0)?name:name.substring(colonPtr+1);
        if (prefix == this.EMPTY_STRING && uri == null && schemaDoc.fIsChameleonSchema)
            uri = schemaDoc.fTargetNamespace;
        if (uri == null)
            return ","+localpart;
        return uri+","+localpart;
    } // findQName(String, XSDocumentInfo):  String

    // This function looks among the children of curr for an element of type elementSought.
    // If it finds one, it evaluates whether its ref attribute contains a reference
    // to originalQName.  If it does, it returns 1 + the value returned by
    // calls to itself on all other children.  In all other cases it returns 0 plus
    // the sum of the values returned by calls to itself on curr's children.
    // It also resets the value of ref so that it will refer to the renamed type from the schema
    // being redefined.
    private int changeRedefineGroup(String originalQName, String elementSought,
                                    String newName, Element curr, XSDocumentInfo schemaDoc) {
        SchemaNamespaceSupport currNSMap = schemaDoc.fNamespaceSupport;
        int result = 0;
        for (Element child = DOMUtil.getFirstChildElement(curr);
            child != null; child = DOMUtil.getNextSiblingElement(child)) {
            String name = child.getLocalName();
            if (!name.equals(elementSought))
                result += changeRedefineGroup(originalQName, elementSought, newName, child, schemaDoc);
            else {
                String ref = child.getAttribute( SchemaSymbols.ATT_REF );
                if (ref.length() != 0) {
                    String processedRef = findQName(ref, schemaDoc);
                    if (originalQName.equals(processedRef)) {
                        String prefix = EMPTY_STRING;
                        String localpart = ref;
                        int colonptr = ref.indexOf(":");
                        if (colonptr > 0) {
                            prefix = ref.substring(0,colonptr);
                            child.setAttribute(SchemaSymbols.ATT_REF, prefix + ":" + newName);
                        }
                        else
                            child.setAttribute(SchemaSymbols.ATT_REF, newName);
                        result++;
                        if (elementSought.equals(SchemaSymbols.ELT_GROUP)) {
                            String minOccurs = child.getAttribute( SchemaSymbols.ATT_MINOCCURS );
                            String maxOccurs = child.getAttribute( SchemaSymbols.ATT_MAXOCCURS );
                            if (!((maxOccurs.length() == 0 || maxOccurs.equals("1"))
                                  && (minOccurs.length() == 0 || minOccurs.equals("1")))) {
                                reportSchemaError("src-redefine.6.1.2", new Object [] {ref});
                            }
                        }
                    }
                } // if ref was null some other stage of processing will flag the error
            }
        }
        return result;
    } // changeRedefineGroup

    // this method returns the XSDocumentInfo object that contains the
    // component corresponding to decl.  If components from this
    // document cannot be referred to from those of currSchema, this
    // method returns null; it's up to the caller to throw an error.
    // @param:  currSchema:  the XSDocumentInfo object containing the
    // decl ref'ing us.
    // @param:  decl:  the declaration being ref'd.
    private XSDocumentInfo findXSDocumentForDecl(XSDocumentInfo currSchema,
                                                 Element decl) {
        Document declDoc = DOMUtil.getDocument(decl);
        Object temp = fDoc2XSDocumentMap.get(declDoc);
        if (temp == null) {
            // something went badly wrong; we don't know this doc?
            return null;
        }
        XSDocumentInfo declDocInfo = (XSDocumentInfo)temp;
        return declDocInfo;
        /*********
        Logic here is unnecessary after schema WG's recent decision to allow
        schema components from one document to refer to components of any other,
        so long as there's some include/import/redefine path amongst them.
        If they rver reverse this decision the code's right here though...  - neilg
        // now look in fDependencyMap to see if this is reachable
        if(((Vector)fDependencyMap.get(currSchema)).contains(declDocInfo)) {
            return declDocInfo;
        }
        // obviously the requesting doc didn't include, redefine or
        // import the one containing decl...
        return null;
        **********/
    } // findXSDocumentForDecl(XSDocumentInfo, Element):  XSDocumentInfo

    private void setSchemasVisible(XSDocumentInfo startSchema) {
        if (DOMUtil.isHidden(startSchema.fSchemaDoc)) {
            // make it visible
            DOMUtil.setVisible(startSchema.fSchemaDoc);
            Vector dependingSchemas = (Vector)fDependencyMap.get(startSchema);
            for (int i = 0; i < dependingSchemas.size(); i++) {
                setSchemasVisible((XSDocumentInfo)dependingSchemas.elementAt(i));
            }
        }
        // if it's visible already than so must be its children
    } // setSchemasVisible(XSDocumentInfo): void

    // report schema error
    void reportSchemaError (String key, Object[] args) {
        fErrorReporter.reportError(XSMessageFormatter.SCHEMA_DOMAIN,
                                   key, args,
                                   XMLErrorReporter.SEVERITY_ERROR);
    }

    // REVISIT: is it how we want to handle error reporting?
    void reportGenericSchemaError (String error) {
        fErrorReporter.reportError(XSMessageFormatter.SCHEMA_DOMAIN,
                                   "General",
                                   new Object[]{error},
                                   XMLErrorReporter.SEVERITY_ERROR);
    }

} // XSDHandler
TOP

Related Classes of org.apache.xerces.impl.xs.traversers.XSDHandler$LocationResolver

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.