/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999-2004 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
 * .
 */
package com.sun.org.apache.xerces.internal.impl.xs;
import com.sun.org.apache.xerces.internal.impl.dv.XSSimpleType;
import com.sun.org.apache.xerces.internal.impl.dv.InvalidDatatypeValueException;
import com.sun.org.apache.xerces.internal.impl.dv.ValidatedInfo;
import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
import com.sun.org.apache.xerces.internal.impl.xs.models.CMBuilder;
import com.sun.org.apache.xerces.internal.impl.xs.models.XSCMValidator;
import com.sun.org.apache.xerces.internal.impl.xs.util.SimpleLocator;
import com.sun.org.apache.xerces.internal.xs.XSConstants;
import com.sun.org.apache.xerces.internal.xs.XSObjectList;
import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition;
import com.sun.org.apache.xerces.internal.impl.dv.ValidationContext;
import com.sun.org.apache.xerces.internal.util.SymbolHash;
import java.util.Vector;
/**
 * Constaints shared by traversers and validator
 *
 * @author Sandy Gao, IBM
 *
 * @version $Id: XSConstraints.java,v 1.37 2004/02/03 17:27:45 sandygao Exp $
 */
public class XSConstraints {
    static final int OCCURRENCE_UNKNOWN = SchemaSymbols.OCCURRENCE_UNBOUNDED-1;
    static final XSSimpleType STRING_TYPE = (XSSimpleType)SchemaGrammar.SG_SchemaNS.getGlobalTypeDecl(SchemaSymbols.ATTVAL_STRING);
    /**
     * check whether derived is valid derived from base, given a subset
     * of {restriction, extension}.B
     */
    public static boolean checkTypeDerivationOk(XSTypeDefinition derived, XSTypeDefinition base, short block) {
        // if derived is anyType, then it's valid only if base is anyType too
        if (derived == SchemaGrammar.fAnyType)
            return derived == base;
        // if derived is anySimpleType, then it's valid only if the base
        // is ur-type
        if (derived == SchemaGrammar.fAnySimpleType) {
            return (base == SchemaGrammar.fAnyType ||
                    base == SchemaGrammar.fAnySimpleType);
        }
        // if derived is simple type
        if (derived.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) {
            // if base is complex type
            if (base.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
                // if base is anyType, change base to anySimpleType,
                // otherwise, not valid
                if (base == SchemaGrammar.fAnyType)
                    base = SchemaGrammar.fAnySimpleType;
                else
                    return false;
            }
            return checkSimpleDerivation((XSSimpleType)derived,
                                         (XSSimpleType)base, block);
        } else {
            return checkComplexDerivation((XSComplexTypeDecl)derived, base, block);
        }
    }
    /**
     * check whether simple type derived is valid derived from base,
     * given a subset of {restriction, extension}.
     */
    public static boolean checkSimpleDerivationOk(XSSimpleType derived, XSTypeDefinition base, short block) {
        // if derived is anySimpleType, then it's valid only if the base
        // is ur-type
        if (derived == SchemaGrammar.fAnySimpleType) {
            return (base == SchemaGrammar.fAnyType ||
                    base == SchemaGrammar.fAnySimpleType);
        }
        // if base is complex type
        if (base.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
            // if base is anyType, change base to anySimpleType,
            // otherwise, not valid
            if (base == SchemaGrammar.fAnyType)
                base = SchemaGrammar.fAnySimpleType;
            else
                return false;
        }
        return checkSimpleDerivation((XSSimpleType)derived,
                                     (XSSimpleType)base, block);
    }
    /**
     * check whether complex type derived is valid derived from base,
     * given a subset of {restriction, extension}.
     */
    public static boolean checkComplexDerivationOk(XSComplexTypeDecl derived, XSTypeDefinition base, short block) {
        // if derived is anyType, then it's valid only if base is anyType too
        if (derived == SchemaGrammar.fAnyType)
            return derived == base;
        return checkComplexDerivation((XSComplexTypeDecl)derived, base, block);
    }
    /**
     * Note: this will be a private method, and it assumes that derived is not
     *       anySimpleType, and base is not anyType. Another method will be
     *       introduced for public use, which will call this method.
     */
    private static boolean checkSimpleDerivation(XSSimpleType derived, XSSimpleType base, short block) {
        // 1 They are the same type definition.
        if (derived == base)
            return true;
        // 2 All of the following must be true:
        // 2.1 restriction is not in the subset, or in the {final} of its own {base type definition};
        if ((block & XSConstants.DERIVATION_RESTRICTION) != 0 ||
            (derived.getBaseType().getFinal() & XSConstants.DERIVATION_RESTRICTION) != 0) {
            return false;
        }
        // 2.2 One of the following must be true:
        // 2.2.1 D's base type definition is B.
        XSSimpleType directBase = (XSSimpleType)derived.getBaseType();
        if (directBase == base)
            return true;
        // 2.2.2 D's base type definition is not the simple ur-type definition and is validly derived from B given the subset, as defined by this constraint.
        if (directBase != SchemaGrammar.fAnySimpleType &&
            checkSimpleDerivation(directBase, base, block)) {
            return true;
        }
        // 2.2.3 D's {variety} is list or union and B is the simple ur-type definition.
        if ((derived.getVariety() == XSSimpleType.VARIETY_LIST ||
             derived.getVariety() == XSSimpleType.VARIETY_UNION) &&
            base == SchemaGrammar.fAnySimpleType) {
            return true;
        }
        // 2.2.4 B's {variety} is union and D is validly derived from a type definition in B's {member type definitions} given the subset, as defined by this constraint.
        if (base.getVariety() == XSSimpleType.VARIETY_UNION) {
            XSObjectList subUnionMemberDV = base.getMemberTypes();
            int subUnionSize = subUnionMemberDV.getLength();
            for (int i=0; i= 0; i--) {
            SGHandler.addSubstitutionGroup(grammars[i].getSubstitutionGroups());
        }
        XSParticleDecl fakeDerived = new XSParticleDecl();
        XSParticleDecl fakeBase = new XSParticleDecl();
        fakeDerived.fType = XSParticleDecl.PARTICLE_MODELGROUP;
        fakeBase.fType = XSParticleDecl.PARTICLE_MODELGROUP;
        // before worrying about complexTypes, let's get
        // groups redefined by restriction out of the way.
        for (int g = grammars.length-1; g >= 0; g--) {
            XSGroupDecl [] redefinedGroups = grammars[g].getRedefinedGroupDecls();
            SimpleLocator [] rgLocators = grammars[g].getRGLocators();
            for(int i=0; i= 0; i--) {
            // get whether to skip EDC, and types need to be checked
            keepType = 0;
            fullChecked = grammars[i].fFullChecked;
            types = grammars[i].getUncheckedComplexTypeDecls();
            ctLocators = grammars[i].getUncheckedCTLocators();
            // for each type
            for (j = types.length-1; j >= 0; j--) {
                // if we've already full-checked this grammar, then
                // skip the EDC constraint
                if (!fullChecked) {
                // 1. Element Decl Consistent
                  if (types[j].fParticle!=null) {
                    elemTable.clear();
                    try {
                      checkElementDeclsConsistent(types[j], types[j].fParticle,
                                                  elemTable, SGHandler);
                    }
                    catch (XMLSchemaException e) {
                      reportSchemaError(errorReporter, ctLocators[j],
                                        e.getKey(),
                                        e.getArgs());
                    }
                  }
                }
                // 2. Particle Derivation
                
                if (types[j].fBaseType != null &&
                    types[j].fBaseType != SchemaGrammar.fAnyType &&
                    types[j].fDerivedBy == XSConstants.DERIVATION_RESTRICTION &&
                    (types[j].fBaseType instanceof XSComplexTypeDecl)) {  
                  XSParticleDecl derivedParticle=types[j].fParticle;  
                  XSParticleDecl baseParticle=  
                    ((XSComplexTypeDecl)(types[j].fBaseType)).fParticle;
                  if (derivedParticle==null && (!(baseParticle==null ||
                                               baseParticle.emptiable()))) {
                    reportSchemaError(errorReporter,ctLocators[j],
                                      "derivation-ok-restriction.5.3.2",
                                      new Object[]{types[j].fName, types[j].fBaseType.getName()});
                  }
                  else if (derivedParticle!=null &&
                           baseParticle!=null) 
                    try {
                      particleValidRestriction(types[j].fParticle,
                                               SGHandler,
                                               ((XSComplexTypeDecl)(types[j].fBaseType)).fParticle,
                                               SGHandler);
                    } catch (XMLSchemaException e) {
                      reportSchemaError(errorReporter, ctLocators[j],
                                        e.getKey(),
                                        e.getArgs());
                      reportSchemaError(errorReporter, ctLocators[j],
                                        "derivation-ok-restriction.5.4.2",
                                        new Object[]{types[j].fName});
                    }
                }
                // 3. UPA
                // get the content model and check UPA
                XSCMValidator cm = types[j].getContentModel(cmBuilder);
                further = false;
                if (cm != null) {
                    try {
                        further = cm.checkUniqueParticleAttribution(SGHandler);
                    } catch (XMLSchemaException e) {
                        reportSchemaError(errorReporter, ctLocators[j],
                                          e.getKey(),
                                          e.getArgs());
                    }
                }
                // now report all errors
                // REVISIT: do we want to report all errors? or just one?
                /*for (k = errors.getErrorCodeNum()-1; k >= 0; k--) {
                    reportSchemaError(errorReporter, ctLocators[j],
                                      errors.getErrorCode(k),
                                      errors.getArgs(k));
                }*/
                // if we are doing all checkings, and this one needs further
                // checking, store it in the type array.
                if (!fullChecked && further)
                    types[keepType++] = types[j];
                // clear errors for the next type.
                // REVISIT: do we want to report all errors? or just one?
                //errors.clear();
            }
            // we've done with the types in this grammar. if we are checking
            // all constraints, need to trim type array to a proper size:
            // only contain those need further checking.
            // and mark this grammar that it only needs UPA checking.
            if (!fullChecked) {
                grammars[i].setUncheckedTypeNum(keepType);
                grammars[i].fFullChecked = true;
            }
        }
    }
    /*
       Check that a given particle is a valid restriction of a base particle.
    */
    public static void checkElementDeclsConsistent(XSComplexTypeDecl type,
                                     XSParticleDecl particle,
                                     SymbolHash elemDeclHash,
                                     SubstitutionGroupHandler sgHandler) 
                                     throws XMLSchemaException {
       // check for elements in the tree with the same name and namespace           
       int pType = particle.fType;
       if (pType == XSParticleDecl.PARTICLE_WILDCARD)
          return;
       if (pType == XSParticleDecl.PARTICLE_ELEMENT) {
          XSElementDecl elem = (XSElementDecl)(particle.fValue);
          findElemInTable(type, elem, elemDeclHash);
          if (elem.fScope == XSConstants.SCOPE_GLOBAL) {
             // Check for subsitution groups.  
             XSElementDecl[] subGroup = sgHandler.getSubstitutionGroup(elem);
             for (int i = 0; i < subGroup.length; i++) {
               findElemInTable(type, subGroup[i], elemDeclHash); 
             }
          }
          return;
       }
       XSModelGroupImpl group = (XSModelGroupImpl)particle.fValue;
       for (int i = 0; i < group.fParticleCount; i++)
           checkElementDeclsConsistent(type, group.fParticles[i], elemDeclHash, sgHandler);
    }
    public static void findElemInTable(XSComplexTypeDecl type, XSElementDecl elem, 
                                       SymbolHash elemDeclHash) 
                                       throws XMLSchemaException {
        // How can we avoid this concat?  LM. 
        String name = elem.fName + "," + elem.fTargetNamespace;
        XSElementDecl existingElem = null;
        if ((existingElem = (XSElementDecl)(elemDeclHash.get(name))) == null) {
          // just add it in
          elemDeclHash.put(name, elem);
        }
        else {
          // If this is the same check element, we're O.K. 
          if (elem == existingElem) 
            return;
          if (elem.fType != existingElem.fType) {
            // Types are not the same 
            throw new XMLSchemaException("cos-element-consistent", 
                      new Object[] {type.fName, elem.fName});
          }
        }
    }
    /*
       Check that a given particle is a valid restriction of a base particle.
    */
    private static void particleValidRestriction(XSParticleDecl dParticle,
                                     SubstitutionGroupHandler dSGHandler,
                                     XSParticleDecl bParticle,
                                     SubstitutionGroupHandler bSGHandler)
                                     throws XMLSchemaException {
       particleValidRestriction(dParticle, dSGHandler, bParticle, bSGHandler, true);
    }
    private static void particleValidRestriction(XSParticleDecl dParticle,
                                     SubstitutionGroupHandler dSGHandler,
                                     XSParticleDecl bParticle,
                                     SubstitutionGroupHandler bSGHandler,
                                     boolean checkWCOccurrence)
                                     throws XMLSchemaException {
       Vector dChildren = null;
       Vector bChildren = null;
       int dMinEffectiveTotalRange=OCCURRENCE_UNKNOWN;
       int dMaxEffectiveTotalRange=OCCURRENCE_UNKNOWN;
       // Check for empty particles.   If either base or derived particle is empty,
       // (and the other isn't) it's an error.
       if (dParticle.isEmpty() && !bParticle.emptiable()) {
         throw new XMLSchemaException("cos-particle-restrict.a", null);
       }
       else if (!dParticle.isEmpty() && bParticle.isEmpty()) {
         throw new XMLSchemaException("cos-particle-restrict.b", null);
       }
       //
       // Do setup prior to invoking the Particle (Restriction) cases.
       // This involves:
       //   - removing pointless occurrences for groups, and retrieving a vector of
       //     non-pointless children
       //   - turning top-level elements with substitution groups into CHOICE groups.
       //
       short dType = dParticle.fType;
       //
       // Handle pointless groups for the derived particle
       //
       if (dType == XSParticleDecl.PARTICLE_MODELGROUP) {
         dType = ((XSModelGroupImpl)dParticle.fValue).fCompositor;
         // Find a group, starting with this particle, with more than 1 child.   There
         // may be none, and the particle of interest trivially becomes an element or
         // wildcard.
         XSParticleDecl dtmp = getNonUnaryGroup(dParticle);
         if (dtmp != dParticle) {
            // Particle has been replaced.   Retrieve new type info.
            dParticle = dtmp;
            dType = dParticle.fType;
            if (dType == XSParticleDecl.PARTICLE_MODELGROUP)
               dType = ((XSModelGroupImpl)dParticle.fValue).fCompositor;
         }
         // Fill in a vector with the children of the particle, removing any
         // pointless model groups in the process.
         dChildren = removePointlessChildren(dParticle);
       }
       int dMinOccurs = dParticle.fMinOccurs;
       int dMaxOccurs = dParticle.fMaxOccurs;
       //
       // For elements which are the heads of substitution groups, treat as CHOICE
       //
       if (dSGHandler != null && dType == XSParticleDecl.PARTICLE_ELEMENT) {
           XSElementDecl dElement = (XSElementDecl)dParticle.fValue;
           if (dElement.fScope == XSConstants.SCOPE_GLOBAL) {
             // Check for subsitution groups.   Treat any element that has a
             // subsitution group as a choice.   Fill in the children vector with the
             // members of the substitution group
             XSElementDecl[] subGroup = dSGHandler.getSubstitutionGroup(dElement);
             if (subGroup.length >0 ) {
                // Now, set the type to be CHOICE.  The "group" will have the same
                // occurrence information as the original particle.
                dType = XSModelGroupImpl.MODELGROUP_CHOICE;
                dMinEffectiveTotalRange = dMinOccurs;
                dMaxEffectiveTotalRange = dMaxOccurs;
                // Fill in the vector of children
                dChildren = new Vector(subGroup.length+1);
                for (int i = 0; i < subGroup.length; i++) {
                  addElementToParticleVector(dChildren, subGroup[i]);
                }
                addElementToParticleVector(dChildren, dElement);
                // Set the handler to null, to indicate that we've finished handling
                // substitution groups for this particle.
                dSGHandler = null;
             }
           }
       }
       short bType = bParticle.fType;
       //
       // Handle pointless groups for the base particle
       //
       if (bType == XSParticleDecl.PARTICLE_MODELGROUP) {
         bType = ((XSModelGroupImpl)bParticle.fValue).fCompositor;
         // Find a group, starting with this particle, with more than 1 child.   There
         // may be none, and the particle of interest trivially becomes an element or
         // wildcard.
         XSParticleDecl btmp = getNonUnaryGroup(bParticle);
         if (btmp != bParticle) {
            // Particle has been replaced.   Retrieve new type info.
            bParticle = btmp;
            bType = bParticle.fType;
            if (bType == XSParticleDecl.PARTICLE_MODELGROUP)
               bType = ((XSModelGroupImpl)bParticle.fValue).fCompositor;
         }
         // Fill in a vector with the children of the particle, removing any
         // pointless model groups in the process.
         bChildren = removePointlessChildren(bParticle);
       }
       int bMinOccurs = bParticle.fMinOccurs;
       int bMaxOccurs = bParticle.fMaxOccurs;
       if (bSGHandler != null && bType == XSParticleDecl.PARTICLE_ELEMENT) {
           XSElementDecl bElement = (XSElementDecl)bParticle.fValue;
           if (bElement.fScope == XSConstants.SCOPE_GLOBAL) {
             // Check for subsitution groups.   Treat any element that has a
             // subsitution group as a choice.   Fill in the children vector with the
             // members of the substitution group
             XSElementDecl[] bsubGroup = bSGHandler.getSubstitutionGroup(bElement);
             if (bsubGroup.length >0 ) {
                // Now, set the type to be CHOICE
                bType = XSModelGroupImpl.MODELGROUP_CHOICE;
                bChildren = new Vector(bsubGroup.length+1);
                for (int i = 0; i < bsubGroup.length; i++) {
                  addElementToParticleVector(bChildren, bsubGroup[i]);
                }
                addElementToParticleVector(bChildren, bElement);
                // Set the handler to null, to indicate that we've finished handling
                // substitution groups for this particle.
                bSGHandler = null;
             }
           }
       }
       //
       // O.K. - Figure out which particle derivation rule applies and call it
       //
       switch (dType) {
         case XSParticleDecl.PARTICLE_ELEMENT:
         {
            switch (bType) {
              // Elt:Elt NameAndTypeOK
              case XSParticleDecl.PARTICLE_ELEMENT:
              {
                 checkNameAndTypeOK((XSElementDecl)dParticle.fValue,dMinOccurs,dMaxOccurs,
                                    (XSElementDecl)bParticle.fValue,bMinOccurs,bMaxOccurs);
                 return;
              }
              // Elt:Any NSCompat
              case XSParticleDecl.PARTICLE_WILDCARD:
              {
                 checkNSCompat((XSElementDecl)dParticle.fValue,dMinOccurs,dMaxOccurs,
                               (XSWildcardDecl)bParticle.fValue,bMinOccurs,bMaxOccurs,
                               checkWCOccurrence);
                 return;
              }
              // Elt:All RecurseAsIfGroup
              case XSModelGroupImpl.MODELGROUP_CHOICE:
              {
                 // Treat the element as if it were in a group of the same type
                 // as the base Particle
                 dChildren = new Vector();
                 dChildren.addElement(dParticle);
                 checkRecurseLax(dChildren, 1, 1, dSGHandler,
                                 bChildren, bMinOccurs, bMaxOccurs, bSGHandler);
                 return;
              }
              case XSModelGroupImpl.MODELGROUP_SEQUENCE:
              case XSModelGroupImpl.MODELGROUP_ALL:
              {
                 // Treat the element as if it were in a group of the same type
                 // as the base Particle
                 dChildren = new Vector();
                 dChildren.addElement(dParticle);
                 checkRecurse(dChildren, 1, 1, dSGHandler,
                              bChildren, bMinOccurs, bMaxOccurs, bSGHandler);
                 return;
              }
              default:
              {
                throw new XMLSchemaException("Internal-Error",
                                             new Object[]{"in particleValidRestriction"});
              }
            }
         }
         case XSParticleDecl.PARTICLE_WILDCARD:
         {
            switch (bType) {
              // Any:Any NSSubset
              case XSParticleDecl.PARTICLE_WILDCARD:
              {
                 checkNSSubset((XSWildcardDecl)dParticle.fValue, dMinOccurs, dMaxOccurs,
                               (XSWildcardDecl)bParticle.fValue, bMinOccurs, bMaxOccurs);
                 return;
              }
              case XSModelGroupImpl.MODELGROUP_CHOICE:
              case XSModelGroupImpl.MODELGROUP_SEQUENCE:
              case XSModelGroupImpl.MODELGROUP_ALL:
              case XSParticleDecl.PARTICLE_ELEMENT:
              {
                 throw new XMLSchemaException("cos-particle-restrict.2",
                                        new Object[]{"any:choice,sequence,all,elt"});
              }
              default:
              {
                throw new XMLSchemaException("Internal-Error",
                                             new Object[]{"in particleValidRestriction"});
              }
            }
         }
         case XSModelGroupImpl.MODELGROUP_ALL:
         {
            switch (bType) {
              // All:Any NSRecurseCheckCardinality
              case XSParticleDecl.PARTICLE_WILDCARD:
              {
                 if (dMinEffectiveTotalRange == OCCURRENCE_UNKNOWN)
                    dMinEffectiveTotalRange = dParticle.minEffectiveTotalRange();
                 if (dMaxEffectiveTotalRange == OCCURRENCE_UNKNOWN)
                    dMaxEffectiveTotalRange = dParticle.maxEffectiveTotalRange();
                 checkNSRecurseCheckCardinality(dChildren, dMinEffectiveTotalRange,
                                                dMaxEffectiveTotalRange,
                                                dSGHandler,
                                                bParticle,bMinOccurs,bMaxOccurs,
                                                checkWCOccurrence);
                 return;
              }
              case XSModelGroupImpl.MODELGROUP_ALL:
              {
                 checkRecurse(dChildren, dMinOccurs, dMaxOccurs, dSGHandler,
                              bChildren, bMinOccurs, bMaxOccurs, bSGHandler);
                 return;
              }
              case XSModelGroupImpl.MODELGROUP_CHOICE:
              case XSModelGroupImpl.MODELGROUP_SEQUENCE:
              case XSParticleDecl.PARTICLE_ELEMENT:
              {
                 throw new XMLSchemaException("cos-particle-restrict.2",
                                        new Object[]{"all:choice,sequence,elt"});
              }
              default:
              {
                throw new XMLSchemaException("Internal-Error",
                                             new Object[]{"in particleValidRestriction"});
              }
            }
         }
         case XSModelGroupImpl.MODELGROUP_CHOICE:
         {
            switch (bType) {
              // Choice:Any NSRecurseCheckCardinality
              case XSParticleDecl.PARTICLE_WILDCARD:
              {
                 if (dMinEffectiveTotalRange == OCCURRENCE_UNKNOWN)
                    dMinEffectiveTotalRange = dParticle.minEffectiveTotalRange();
                 if (dMaxEffectiveTotalRange == OCCURRENCE_UNKNOWN)
                    dMaxEffectiveTotalRange = dParticle.maxEffectiveTotalRange();
                 checkNSRecurseCheckCardinality(dChildren, dMinEffectiveTotalRange,
                                                dMaxEffectiveTotalRange,
                                                dSGHandler,
                                                bParticle,bMinOccurs,bMaxOccurs,
                                                checkWCOccurrence);
                 return;
              }
              case XSModelGroupImpl.MODELGROUP_CHOICE:
              {
                 checkRecurseLax(dChildren, dMinOccurs, dMaxOccurs, dSGHandler,
                                 bChildren, bMinOccurs, bMaxOccurs, bSGHandler);
                 return;
              }
              case XSModelGroupImpl.MODELGROUP_ALL:
              case XSModelGroupImpl.MODELGROUP_SEQUENCE:
              case XSParticleDecl.PARTICLE_ELEMENT:
              {
                 throw new XMLSchemaException("cos-particle-restrict.2",
                                        new Object[]{"choice:all,sequence,elt"});
              }
              default:
              {
                throw new XMLSchemaException("Internal-Error",
                                             new Object[]{"in particleValidRestriction"});
              }
            }
         }
         case XSModelGroupImpl.MODELGROUP_SEQUENCE:
         {
            switch (bType) {
              // Choice:Any NSRecurseCheckCardinality
              case XSParticleDecl.PARTICLE_WILDCARD:
              {
                 if (dMinEffectiveTotalRange == OCCURRENCE_UNKNOWN)
                    dMinEffectiveTotalRange = dParticle.minEffectiveTotalRange();
                 if (dMaxEffectiveTotalRange == OCCURRENCE_UNKNOWN)
                    dMaxEffectiveTotalRange = dParticle.maxEffectiveTotalRange();
                 checkNSRecurseCheckCardinality(dChildren, dMinEffectiveTotalRange,
                                                dMaxEffectiveTotalRange,
                                                dSGHandler,
                                                bParticle,bMinOccurs,bMaxOccurs,
                                                checkWCOccurrence);
                 return;
              }
              case XSModelGroupImpl.MODELGROUP_ALL:
              {
                 checkRecurseUnordered(dChildren, dMinOccurs, dMaxOccurs, dSGHandler,
                                       bChildren, bMinOccurs, bMaxOccurs, bSGHandler);
                 return;
              }
              case XSModelGroupImpl.MODELGROUP_SEQUENCE:
              {
                 checkRecurse(dChildren, dMinOccurs, dMaxOccurs, dSGHandler,
                              bChildren, bMinOccurs, bMaxOccurs, bSGHandler);
                 return;
              }
              case XSModelGroupImpl.MODELGROUP_CHOICE:
              {
                 int min1 = dMinOccurs * dChildren.size();
                 int max1 = (dMaxOccurs == SchemaSymbols.OCCURRENCE_UNBOUNDED)?
                             dMaxOccurs : dMaxOccurs * dChildren.size();
                 checkMapAndSum(dChildren, min1, max1, dSGHandler,
                                bChildren, bMinOccurs, bMaxOccurs, bSGHandler);
                 return;
              }
              case XSParticleDecl.PARTICLE_ELEMENT:
              {
                 throw new XMLSchemaException("cos-particle-restrict.2",
                                        new Object[]{"seq:elt"});
              }
              default:
              {
                throw new XMLSchemaException("Internal-Error",
                                             new Object[]{"in particleValidRestriction"});
              }
            }
         }
       }
    }
    private static void addElementToParticleVector (Vector v, XSElementDecl d)  {
       XSParticleDecl p = new XSParticleDecl();
       p.fValue = d;
       p.fType = XSParticleDecl.PARTICLE_ELEMENT;
       v.addElement(p);
    }
    private static XSParticleDecl getNonUnaryGroup(XSParticleDecl p) {
       if (p.fType == XSParticleDecl.PARTICLE_ELEMENT ||
           p.fType == XSParticleDecl.PARTICLE_WILDCARD)
         return p;
       if (p.fMinOccurs==1 && p.fMaxOccurs==1 &&
           p.fValue!=null && ((XSModelGroupImpl)p.fValue).fParticleCount == 1)
         return getNonUnaryGroup(((XSModelGroupImpl)p.fValue).fParticles[0]);
       else
         return p;
    }
    private static Vector removePointlessChildren(XSParticleDecl p)  {
       if (p.fType == XSParticleDecl.PARTICLE_ELEMENT ||
           p.fType == XSParticleDecl.PARTICLE_WILDCARD)
         return null;
       Vector children = new Vector();
       XSModelGroupImpl group = (XSModelGroupImpl)p.fValue;
       for (int i = 0; i < group.fParticleCount; i++)
           gatherChildren(group.fCompositor, group.fParticles[i], children);
       return children;
    }
    private static void gatherChildren(int parentType, XSParticleDecl p, Vector children) {
       int min = p.fMinOccurs;
       int max = p.fMaxOccurs;
       int type = p.fType;
       if (type == XSParticleDecl.PARTICLE_MODELGROUP)
          type = ((XSModelGroupImpl)p.fValue).fCompositor;
       if (type == XSParticleDecl.PARTICLE_ELEMENT ||
           type== XSParticleDecl.PARTICLE_WILDCARD) {
          children.addElement(p);
          return;
       }
       if (! (min==1 && max==1)) {
          children.addElement(p);
       }
       else if (parentType == type) {
          XSModelGroupImpl group = (XSModelGroupImpl)p.fValue;
          for (int i = 0; i < group.fParticleCount; i++)
             gatherChildren(type, group.fParticles[i], children);
       }
       else if (!p.isEmpty()) {
          children.addElement(p);
       }
    }
    private static void checkNameAndTypeOK(XSElementDecl dElement, int dMin, int dMax,
                                           XSElementDecl bElement, int bMin, int bMax)
                                   throws XMLSchemaException {
      //
      // Check that the names are the same
      //
      if (dElement.fName != bElement.fName ||
          dElement.fTargetNamespace != bElement.fTargetNamespace) {
          throw new XMLSchemaException(
              "rcase-NameAndTypeOK.1",new Object[]{dElement.fName,
               dElement.fTargetNamespace, bElement.fName, bElement.fTargetNamespace});
      }
      //
      // Check nillable
      //
      if (!bElement.getNillable() && dElement.getNillable()) {
        throw new XMLSchemaException("rcase-NameAndTypeOK.2",
                                     new Object[]{dElement.fName});
      }
      //
      // Check occurrence range
      //
      if (!checkOccurrenceRange(dMin, dMax, bMin, bMax)) {
        throw new XMLSchemaException("rcase-NameAndTypeOK.3",
                                      new Object[]{
                                        dElement.fName,
                                        Integer.toString(dMin),
                                        dMax==SchemaSymbols.OCCURRENCE_UNBOUNDED?"unbounded":Integer.toString(dMax),
                                        Integer.toString(bMin),
                                        bMax==SchemaSymbols.OCCURRENCE_UNBOUNDED?"unbounded":Integer.toString(bMax)});
      }
      //
      // Check for consistent fixed values
      //
      if (bElement.getConstraintType() == XSConstants.VC_FIXED) {
         // derived one has to have a fixed value
         if (dElement.getConstraintType() != XSConstants.VC_FIXED) {
            throw new XMLSchemaException("rcase-NameAndTypeOK.4.a",
                                         new Object[]{dElement.fName, bElement.fDefault.stringValue()});
         }
         // get simple type
         boolean isSimple = false;
         if (dElement.fType.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE ||
             ((XSComplexTypeDecl)dElement.fType).fContentType == XSComplexTypeDecl.CONTENTTYPE_SIMPLE) {
             isSimple = true;
         }
         // if there is no simple type, then compare based on string
         if (!isSimple && !bElement.fDefault.normalizedValue.equals(dElement.fDefault.normalizedValue) ||
             isSimple && !bElement.fDefault.actualValue.equals(dElement.fDefault.actualValue)) {
            throw new XMLSchemaException("rcase-NameAndTypeOK.4.b",
                                         new Object[]{dElement.fName,
                                                      dElement.fDefault.stringValue(),
                                                      bElement.fDefault.stringValue()});
         }
      }
      //
      // Check identity constraints
      //
      checkIDConstraintRestriction(dElement, bElement);
      //
      // Check for disallowed substitutions
      //
      int blockSet1 = dElement.fBlock;
      int blockSet2 = bElement.fBlock;
      if (((blockSet1 & blockSet2)!=blockSet2) ||
            (blockSet1==XSConstants.DERIVATION_NONE && blockSet2!=XSConstants.DERIVATION_NONE))
        throw new XMLSchemaException("rcase-NameAndTypeOK.6",
                                  new Object[]{dElement.fName});
      //
      // Check that the derived element's type is derived from the base's.
      //
      if (!checkTypeDerivationOk(dElement.fType, bElement.fType,
                                 (short)(XSConstants.DERIVATION_EXTENSION|XSConstants.DERIVATION_LIST|XSConstants.DERIVATION_UNION))) {
          throw new XMLSchemaException("rcase-NameAndTypeOK.7",
                                  new Object[]{dElement.fName, dElement.fType.getName(), bElement.fType.getName()});
      }
    }
    private static void checkIDConstraintRestriction(XSElementDecl derivedElemDecl,
                                                     XSElementDecl baseElemDecl)
                                             throws XMLSchemaException {
        // TODO
    } // checkIDConstraintRestriction
    private static boolean checkOccurrenceRange(int min1, int max1, int min2, int max2) {
      if ((min1 >= min2) &&
          ((max2==SchemaSymbols.OCCURRENCE_UNBOUNDED) ||
           (max1!=SchemaSymbols.OCCURRENCE_UNBOUNDED && max1<=max2)))
        return true;
      else
        return false;
    }
    private static void checkNSCompat(XSElementDecl elem, int min1, int max1,
                                      XSWildcardDecl wildcard, int min2, int max2,
                                      boolean checkWCOccurrence)
                              throws XMLSchemaException {
      // check Occurrence ranges
      if (checkWCOccurrence && !checkOccurrenceRange(min1,max1,min2,max2)) {
        throw new XMLSchemaException("rcase-NSCompat.2",
                                  new Object[]{
                                    elem.fName,
                                    Integer.toString(min1),
                                    max1==SchemaSymbols.OCCURRENCE_UNBOUNDED?"unbounded":Integer.toString(max1),
                                    Integer.toString(min2),
                                    max2==SchemaSymbols.OCCURRENCE_UNBOUNDED?"unbounded":Integer.toString(max2)});
      }
      // check wildcard allows namespace of element
      if (!wildcard.allowNamespace(elem.fTargetNamespace))  {
        throw new XMLSchemaException("rcase-NSCompat.1",
                                  new Object[]{elem.fName,elem.fTargetNamespace});
      }
    }
    private static void checkNSSubset(XSWildcardDecl dWildcard, int min1, int max1,
                                      XSWildcardDecl bWildcard, int min2, int max2)
                              throws XMLSchemaException {
      // check Occurrence ranges
      if (!checkOccurrenceRange(min1,max1,min2,max2)) {
        throw new XMLSchemaException("rcase-NSSubset.2", new Object[]{
                                     Integer.toString(min1),
                                     max1==SchemaSymbols.OCCURRENCE_UNBOUNDED?"unbounded":Integer.toString(max1),
                                     Integer.toString(min2),
                                     max2==SchemaSymbols.OCCURRENCE_UNBOUNDED?"unbounded":Integer.toString(max2)});
      }
      // check wildcard subset
      if (!dWildcard.isSubsetOf(bWildcard)) {
         throw new XMLSchemaException("rcase-NSSubset.1", null);
      }
      if (dWildcard.weakerProcessContents(bWildcard)) {
          throw new XMLSchemaException("rcase-NSSubset.3",
                                       new Object[]{dWildcard.getProcessContentsAsString(),
                                                    bWildcard.getProcessContentsAsString()});
      }
    }
    private static void checkNSRecurseCheckCardinality(Vector children, int min1, int max1,
                                          SubstitutionGroupHandler dSGHandler,
                                          XSParticleDecl wildcard, int min2, int max2,
                                          boolean checkWCOccurrence)
                                          throws XMLSchemaException {
      // check Occurrence ranges
      if (checkWCOccurrence && !checkOccurrenceRange(min1,max1,min2,max2)) {
         throw new XMLSchemaException("rcase-NSRecurseCheckCardinality.2", new Object[]{
                                        Integer.toString(min1),
                                        max1==SchemaSymbols.OCCURRENCE_UNBOUNDED?"unbounded":Integer.toString(max1),
                                        Integer.toString(min2),
                                        max2==SchemaSymbols.OCCURRENCE_UNBOUNDED?"unbounded":Integer.toString(max2)});
      }
      // Check that each member of the group is a valid restriction of the wildcard
      int count = children.size();
      try {
        for (int i = 0; i < count; i++) {
           XSParticleDecl particle1 = (XSParticleDecl)children.elementAt(i);
           particleValidRestriction(particle1, dSGHandler, wildcard, null, false);
        }
      }
      // REVISIT: should we really just ignore original cause of this error?
      //          how can we report it?
      catch (XMLSchemaException e) {
         throw new XMLSchemaException("rcase-NSRecurseCheckCardinality.1", null);
      }
    }
    private static void checkRecurse(Vector dChildren, int min1, int max1,
                                     SubstitutionGroupHandler dSGHandler,
                                     Vector bChildren, int min2, int max2,
                                     SubstitutionGroupHandler bSGHandler)
                                     throws XMLSchemaException {
      // check Occurrence ranges
      if (!checkOccurrenceRange(min1,max1,min2,max2)) {
        throw new XMLSchemaException("rcase-Recurse.1", new Object[]{
                                       Integer.toString(min1),
                                       max1==SchemaSymbols.OCCURRENCE_UNBOUNDED?"unbounded":Integer.toString(max1),
                                       Integer.toString(min2),
                                       max2==SchemaSymbols.OCCURRENCE_UNBOUNDED?"unbounded":Integer.toString(max2)});
      }
      int count1= dChildren.size();
      int count2= bChildren.size();
      int current = 0;
      label: for (int i = 0; i
      //       
      //       
      //       
      //   
      //
      //   
      //        
      //        
      //   
      // check Occurrence ranges
      if (!checkOccurrenceRange(min1,max1,min2,max2)) {
        throw new XMLSchemaException("rcase-MapAndSum.2",
                                     new Object[]{Integer.toString(min1),
                                        max1==SchemaSymbols.OCCURRENCE_UNBOUNDED?"unbounded":Integer.toString(max1),
                                        Integer.toString(min2),
                                        max2==SchemaSymbols.OCCURRENCE_UNBOUNDED?"unbounded":Integer.toString(max2)});
      }
      int count1 = dChildren.size();
      int count2 = bChildren.size();
      label: for (int i = 0; i= 0; i--) {
            if (subGroup[i].fName == element2.fName &&
                subGroup[i].fTargetNamespace == element2.fTargetNamespace) {
                return true;
            }
        }
        // or if there is an element decl in element2's substitution group,
        // who has the same name/namespace with element1
        subGroup = sgHandler.getSubstitutionGroup(element2);
        for (int i = subGroup.length-1; i >= 0; i--) {
            if (subGroup[i].fName == element1.fName &&
                subGroup[i].fTargetNamespace == element1.fTargetNamespace) {
                return true;
            }
        }
        return false;
    }
    // to check whether an element overlaps with a wildcard,
    // as defined in constraint UPA
    public static boolean overlapUPA(XSElementDecl element,
                                     XSWildcardDecl wildcard,
                                     SubstitutionGroupHandler sgHandler) {
        // if the wildcard allows the element
        if (wildcard.allowNamespace(element.fTargetNamespace))
            return true;
        // or if the wildcard allows any element in the substitution group
        XSElementDecl[] subGroup = sgHandler.getSubstitutionGroup(element);
        for (int i = subGroup.length-1; i >= 0; i--) {
            if (wildcard.allowNamespace(subGroup[i].fTargetNamespace))
                return true;
        }
        return false;
    }
    public static boolean overlapUPA(XSWildcardDecl wildcard1,
                                     XSWildcardDecl wildcard2) {
        // if the intersection of the two wildcard is not empty list
        XSWildcardDecl intersect = wildcard1.performIntersectionWith(wildcard2, wildcard1.fProcessContents);
        if (intersect == null ||
            intersect.fType != XSWildcardDecl.NSCONSTRAINT_LIST ||
            intersect.fNamespaceList.length != 0) {
            return true;
        }
        return false;
    }
    // call one of the above methods according to the type of decls
    public static boolean overlapUPA(Object decl1, Object decl2,
                                     SubstitutionGroupHandler sgHandler) {
        if (decl1 instanceof XSElementDecl) {
            if (decl2 instanceof XSElementDecl) {
                return overlapUPA((XSElementDecl)decl1,
                                  (XSElementDecl)decl2,
                                  sgHandler);
            } else {
                return overlapUPA((XSElementDecl)decl1,
                                  (XSWildcardDecl)decl2,
                                  sgHandler);
            }
        } else {
            if (decl2 instanceof XSElementDecl) {
                return overlapUPA((XSElementDecl)decl2,
                                  (XSWildcardDecl)decl1,
                                  sgHandler);
            } else {
                return overlapUPA((XSWildcardDecl)decl1,
                                  (XSWildcardDecl)decl2);
            }
        }
    }
} // class XSContraints