protected XSDocumentInfo constructTrees()

in src/org/apache/xerces/impl/xs/traversers/XSDHandler.java [776:1139]


    protected XSDocumentInfo constructTrees(Element schemaRoot, String locationHint, XSDDescription desc, boolean nsCollision) {
        if (schemaRoot == null) return null;
        String callerTNS = desc.getTargetNamespace();
        short referType = desc.getContextType();
        
        XSDocumentInfo currSchemaInfo = null;
        try {
            // note that attributes are freed at end of traverseSchemas()
            currSchemaInfo = new XSDocumentInfo(schemaRoot, fAttributeChecker, fSymbolTable);
        } catch (XMLSchemaException se) {
            reportSchemaError(ELE_ERROR_CODES[referType],
                    new Object[]{locationHint},
					  schemaRoot);
            return null;
        }
        // targetNamespace="" is not valid, issue a warning, and ignore it
        if (currSchemaInfo.fTargetNamespace != null &&
                currSchemaInfo.fTargetNamespace.length() == 0) {
            reportSchemaWarning("EmptyTargetNamespace",
                    new Object[]{locationHint},
					schemaRoot);
            currSchemaInfo.fTargetNamespace = null;
        }
        
        if (callerTNS != null) {
            // the second index to the NS_ERROR_CODES array
            // if the caller/expected NS is not absent, we use the first column
            int secondIdx = 0;
            // for include and redefine
            if (referType == XSDDescription.CONTEXT_INCLUDE ||
                    referType == XSDDescription.CONTEXT_REDEFINE) {
                // if the referred document has no targetNamespace,
                // it's a chameleon schema
                if (currSchemaInfo.fTargetNamespace == null) {
                    currSchemaInfo.fTargetNamespace = callerTNS;
                    currSchemaInfo.fIsChameleonSchema = true;
                }
                // if the referred document has a target namespace differing
                // from the caller, it's an error
                else if (callerTNS != currSchemaInfo.fTargetNamespace) {
                    reportSchemaError(NS_ERROR_CODES[referType][secondIdx],
                            new Object [] {callerTNS, currSchemaInfo.fTargetNamespace},
							schemaRoot);
                    return null;
                }
            }
            // for instance and import, the two NS's must be the same
            else if (referType != XSDDescription.CONTEXT_PREPARSE && callerTNS != currSchemaInfo.fTargetNamespace) {
                reportSchemaError(NS_ERROR_CODES[referType][secondIdx],
                        new Object [] {callerTNS, currSchemaInfo.fTargetNamespace},
						schemaRoot);
                return null;
            }
        }
        // now there is no caller/expected NS, it's an error for the referred
        // document to have a target namespace, unless we are preparsing a schema
        else if (currSchemaInfo.fTargetNamespace != null) {
            // set the target namespace of the description
            if (referType == XSDDescription.CONTEXT_PREPARSE) {
                desc.setTargetNamespace(currSchemaInfo.fTargetNamespace);
                callerTNS = currSchemaInfo.fTargetNamespace;
            }
            else {
                // the second index to the NS_ERROR_CODES array
                // if the caller/expected NS is absent, we use the second column
                int secondIdx = 1;
                reportSchemaError(NS_ERROR_CODES[referType][secondIdx],
                        new Object [] {callerTNS, currSchemaInfo.fTargetNamespace},
						schemaRoot);
                return null;
            }
        }
        // the other cases (callerTNS == currSchemaInfo.fTargetNamespce == null)
        // are valid
        
        // a schema document can always access it's own target namespace
        currSchemaInfo.addAllowedNS(currSchemaInfo.fTargetNamespace);
        
        SchemaGrammar sg = null;

        // we have a namespace collision
        if (nsCollision) {
            SchemaGrammar sg2 = fGrammarBucket.getGrammar(currSchemaInfo.fTargetNamespace);
            if (sg2.isImmutable()) {
                sg = new SchemaGrammar(sg2);
                fGrammarBucket.putGrammar(sg);
                // update all the grammars in the bucket to point to the new grammar.
                updateImportListWith(sg);
            }
            else {
                sg = sg2;
            }

            // update import list of the new grammar
            updateImportListFor(sg);
        }
        else if (referType == XSDDescription.CONTEXT_INCLUDE ||
                referType == XSDDescription.CONTEXT_REDEFINE) {
            sg = fGrammarBucket.getGrammar(currSchemaInfo.fTargetNamespace);
        }
        else if(fHonourAllSchemaLocations && referType == XSDDescription.CONTEXT_IMPORT) {
            sg = findGrammar(desc, false);
            if(sg == null) {
                sg = new SchemaGrammar(currSchemaInfo.fTargetNamespace, desc.makeClone(), fSymbolTable);
                fGrammarBucket.putGrammar(sg);
            }
        }
        else {
            sg = new SchemaGrammar(currSchemaInfo.fTargetNamespace, desc.makeClone(), fSymbolTable);
            fGrammarBucket.putGrammar(sg);
        }

        // store the document and its location
        // REVISIT: don't expose the DOM tree
        sg.addDocument(null, (String)fDoc2SystemId.get(currSchemaInfo.fSchemaElement));
        
        fDoc2XSDocumentMap.put(schemaRoot, currSchemaInfo);
        Vector dependencies = new Vector();
        Element rootNode = schemaRoot;
        
        Element 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);
            
            short refType = -1;
            boolean importCollision = false;
            
            if (localName.equals(SchemaSymbols.ELT_ANNOTATION))
                continue;
            else if (localName.equals(SchemaSymbols.ELT_IMPORT)) {
                refType = XSDDescription.CONTEXT_IMPORT;
                // have to handle some validation here too!
                // call XSAttributeChecker to fill in attrs
                Object[] importAttrs = fAttributeChecker.checkAttributes(child, true, currSchemaInfo);
                schemaHint = (String)importAttrs[XSAttributeChecker.ATTIDX_SCHEMALOCATION];
                schemaNamespace = (String)importAttrs[XSAttributeChecker.ATTIDX_NAMESPACE];
                if (schemaNamespace != null)
                    schemaNamespace = fSymbolTable.addSymbol(schemaNamespace);
                
                // check contents and process optional annotations
                Element importChild = DOMUtil.getFirstChildElement(child);
                if(importChild != null ) {
                    String importComponentType = DOMUtil.getLocalName(importChild);
                    if (importComponentType.equals(SchemaSymbols.ELT_ANNOTATION)) {
                        // promoting annotations to parent component
                        sg.addAnnotation(
                                fElementTraverser.traverseAnnotationDecl(importChild, importAttrs, true, currSchemaInfo));
                    } else {
                        reportSchemaError("s4s-elt-must-match.1", new Object [] {localName, "annotation?", importComponentType}, child);
                    }
                    if(DOMUtil.getNextSiblingElement(importChild) != null) {
                        reportSchemaError("s4s-elt-must-match.1", new Object [] {localName, "annotation?", DOMUtil.getLocalName(DOMUtil.getNextSiblingElement(importChild))}, child);
                    }
                }
                else {
                    String text = DOMUtil.getSyntheticAnnotation(child);
                    if (text != null) {
                        sg.addAnnotation(fElementTraverser.traverseSyntheticAnnotation(child, text, importAttrs, true, currSchemaInfo));
                    }
                }
                fAttributeChecker.returnAttrArray(importAttrs, currSchemaInfo);
                
                // a document can't import another document with the same namespace
                if (schemaNamespace == currSchemaInfo.fTargetNamespace) {
                    reportSchemaError(schemaNamespace != null ? 
                            "src-import.1.1" : "src-import.1.2", new Object [] {schemaNamespace}, child);
                    continue;
                }

                // if this namespace has not been imported by this document,
                //  then import if multiple imports support is enabled.
                if(currSchemaInfo.isAllowedNS(schemaNamespace)) {
                    if(!fHonourAllSchemaLocations && !fNamespaceGrowth)
                        continue;
                }
                else  {
                    currSchemaInfo.addAllowedNS(schemaNamespace);
                }
                // also record the fact that one namespace imports another one
                // convert null to ""
                String tns = null2EmptyString(currSchemaInfo.fTargetNamespace);
                // get all namespaces imported by this one
                Vector ins = (Vector)fImportMap.get(tns);
                // if no namespace was imported, create new Vector
                if (ins == null) {
                    // record that this one imports other(s)
                    fAllTNSs.addElement(tns);
                    ins = new Vector();
                    fImportMap.put(tns, ins);
                    ins.addElement(schemaNamespace);
                }
                else if (!ins.contains(schemaNamespace)){
                    ins.addElement(schemaNamespace);
                }

                fSchemaGrammarDescription.reset();
                fSchemaGrammarDescription.setContextType(XSDDescription.CONTEXT_IMPORT);
                fSchemaGrammarDescription.setBaseSystemId(doc2SystemId(schemaRoot));
                fSchemaGrammarDescription.setLiteralSystemId(schemaHint);
                fSchemaGrammarDescription.setLocationHints(new String[]{schemaHint});
                fSchemaGrammarDescription.setTargetNamespace(schemaNamespace);
                
                // if a grammar with the same namespace and location exists (or being
                // built), ignore this one (don't traverse it).
                SchemaGrammar isg = findGrammar(fSchemaGrammarDescription, fNamespaceGrowth);
                if (isg != null) {
                    if (fNamespaceGrowth) {
                        try {
                            if (isg.getDocumentLocations().contains(XMLEntityManager.expandSystemId(schemaHint, fSchemaGrammarDescription.getBaseSystemId(), false))) {
                                continue;
                            }
                            else {
                                importCollision = true;
                            }
                        } 
                        catch (MalformedURIException e) {
                        }
                    }
                    else if (!fHonourAllSchemaLocations || isExistingGrammar(fSchemaGrammarDescription, false)) {
                        continue;
                    }
                }
                //if ((!fHonourAllSchemaLocations && findGrammar(fSchemaGrammarDescription) != null) || isExistingGrammar(fSchemaGrammarDescription))
                //    continue;

                // If "findGrammar" returns a grammar, then this is not the
                // the first time we see a location for a given namespace.
                // Don't consult the location pair hashtable in this case,
                // otherwise the location will be ignored because it'll get
                // resolved to the same location as the first hint.
                newSchemaRoot = resolveSchema(fSchemaGrammarDescription, false, child, isg == null);
            }
            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];
                // store the namespace decls of the redefine element
                if (localName.equals(SchemaSymbols.ELT_REDEFINE)) {
                    fRedefine2NSSupport.put(child, new SchemaNamespaceSupport(currSchemaInfo.fNamespaceSupport));
                }
                
                // check annotations.  Must do this here to avoid having to
                // re-parse attributes later
                if(localName.equals(SchemaSymbols.ELT_INCLUDE)) {
                    Element includeChild = DOMUtil.getFirstChildElement(child);
                    if(includeChild != null ) {
                        String includeComponentType = DOMUtil.getLocalName(includeChild);
                        if (includeComponentType.equals(SchemaSymbols.ELT_ANNOTATION)) {
                            // promoting annotations to parent component
                            sg.addAnnotation(
                                    fElementTraverser.traverseAnnotationDecl(includeChild, includeAttrs, true, currSchemaInfo));
                        } else {
                            reportSchemaError("s4s-elt-must-match.1", new Object [] {localName, "annotation?", includeComponentType}, child);
                        }
                        if(DOMUtil.getNextSiblingElement(includeChild) != null) {
                            reportSchemaError("s4s-elt-must-match.1", new Object [] {localName, "annotation?", DOMUtil.getLocalName(DOMUtil.getNextSiblingElement(includeChild))}, child);
                        }
                    }
                    else {
                        String text = DOMUtil.getSyntheticAnnotation(child);
                        if (text != null) {
                            sg.addAnnotation(fElementTraverser.traverseSyntheticAnnotation(child, text, includeAttrs, true, currSchemaInfo));
                        }
                    }
                }
                else {
                    for (Element redefinedChild = DOMUtil.getFirstChildElement(child);
                    redefinedChild != null;
                    redefinedChild = DOMUtil.getNextSiblingElement(redefinedChild)) {
                        String redefinedComponentType = DOMUtil.getLocalName(redefinedChild);
                        if (redefinedComponentType.equals(SchemaSymbols.ELT_ANNOTATION)) {
                            // promoting annotations to parent component
                            sg.addAnnotation(
                                    fElementTraverser.traverseAnnotationDecl(redefinedChild, includeAttrs, true, currSchemaInfo));
                            DOMUtil.setHidden(redefinedChild, fHiddenNodes);
                        }
                        else {
                            String text = DOMUtil.getSyntheticAnnotation(child);
                            if (text != null) {
                                sg.addAnnotation(fElementTraverser.traverseSyntheticAnnotation(child, text, includeAttrs, true, currSchemaInfo));
                            }
                        }
                        // catch all other content errors later
                    }
                }
                fAttributeChecker.returnAttrArray(includeAttrs, currSchemaInfo);
                // schemaLocation is required on <include> and <redefine>
                if (schemaHint == null) {
                    reportSchemaError("s4s-att-must-appear", new Object [] {
                            "<include> or <redefine>", "schemaLocation"},
                            child);
                }
                // pass the systemId of the current document as the base systemId
                boolean mustResolve = false;
                refType = XSDDescription.CONTEXT_INCLUDE;
                if(localName.equals(SchemaSymbols.ELT_REDEFINE)) {
                    mustResolve = nonAnnotationContent(child);
                    refType = XSDDescription.CONTEXT_REDEFINE;
                }
                fSchemaGrammarDescription.reset();
                fSchemaGrammarDescription.setContextType(refType);
                fSchemaGrammarDescription.setBaseSystemId(doc2SystemId(schemaRoot));
                fSchemaGrammarDescription.setLocationHints(new String[]{schemaHint});
                fSchemaGrammarDescription.setTargetNamespace(callerTNS);
                
                boolean alreadyTraversed = false;
                XMLInputSource schemaSource = resolveSchemaSource(fSchemaGrammarDescription, mustResolve, child, true);
                if (fNamespaceGrowth && refType == XSDDescription.CONTEXT_INCLUDE) {
                    try {
                        final String schemaId = XMLEntityManager.expandSystemId(schemaSource.getSystemId(), schemaSource.getBaseSystemId(), false);
                        alreadyTraversed = sg.getDocumentLocations().contains(schemaId);
                    }
                    catch(MalformedURIException e) {
                        
                    }
                }

                if (!alreadyTraversed) {
                    newSchemaRoot = resolveSchema(schemaSource, fSchemaGrammarDescription, mustResolve, child);
                    schemaNamespace = currSchemaInfo.fTargetNamespace;
                }
                else {
                    fLastSchemaWasDuplicate = true;
                }
            }
            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
            XSDocumentInfo newSchemaInfo = null;
            if (fLastSchemaWasDuplicate) {
                newSchemaInfo = newSchemaRoot == null ? null : (XSDocumentInfo)fDoc2XSDocumentMap.get(newSchemaRoot);
            }
            else {
               	newSchemaInfo = constructTrees(newSchemaRoot, schemaHint, fSchemaGrammarDescription, importCollision);
            }

            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) {
                if (newSchemaInfo != null)
                    dependencies.addElement(newSchemaInfo);
                newSchemaRoot = null;
            }
        }
        
        fDependencyMap.put(currSchemaInfo, dependencies);
        return currSchemaInfo;
    } // end constructTrees