File: System\Xml\Schema\XmlSchemaComplexType.cs
Project: ndp\fx\src\Xml\System.Xml.csproj (System.Xml)
//------------------------------------------------------------------------------
// <copyright file="XmlSchemaComplexType.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>  
// <owner current="true" primary="true">Microsoft</owner>                                                              
//------------------------------------------------------------------------------
 
namespace System.Xml.Schema {
 
    using System.Collections;
    using System.ComponentModel;
    using System.Xml.Serialization;
 
    /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType"]/*' />
    /// <devdoc>
    ///    <para>[To be supplied.]</para>
    /// </devdoc>
    public class XmlSchemaComplexType : XmlSchemaType {
        XmlSchemaDerivationMethod block = XmlSchemaDerivationMethod.None;
 
        XmlSchemaContentModel contentModel;
        XmlSchemaParticle particle;
        XmlSchemaObjectCollection attributes;
        XmlSchemaAnyAttribute anyAttribute;
 
        XmlSchemaParticle contentTypeParticle = XmlSchemaParticle.Empty;
        XmlSchemaDerivationMethod blockResolved;
        XmlSchemaObjectTable localElements;
        XmlSchemaObjectTable attributeUses;
        XmlSchemaAnyAttribute attributeWildcard;
 
        static XmlSchemaComplexType anyTypeLax;
        static XmlSchemaComplexType anyTypeSkip;
        static XmlSchemaComplexType untypedAnyType;
 
        //additional info for Partial validation
        byte pvFlags;
        const byte wildCardMask = 0x01;
        const byte isMixedMask = 0x02;
        const byte isAbstractMask = 0x04;
        //const byte dupDeclMask = 0x08;
 
        static XmlSchemaComplexType() {
            anyTypeLax = CreateAnyType(XmlSchemaContentProcessing.Lax);
            anyTypeSkip = CreateAnyType(XmlSchemaContentProcessing.Skip);
 
            // Create xdt:untypedAny
            untypedAnyType = new XmlSchemaComplexType();
            untypedAnyType.SetQualifiedName(new XmlQualifiedName("untypedAny", XmlReservedNs.NsXQueryDataType));
            untypedAnyType.IsMixed = true;
            untypedAnyType.SetContentTypeParticle(anyTypeLax.ContentTypeParticle);
            untypedAnyType.SetContentType(XmlSchemaContentType.Mixed);
 
            untypedAnyType.ElementDecl = SchemaElementDecl.CreateAnyTypeElementDecl();
            untypedAnyType.ElementDecl.SchemaType = untypedAnyType;
            untypedAnyType.ElementDecl.ContentValidator = AnyTypeContentValidator;
 
        }
 
        static XmlSchemaComplexType CreateAnyType(XmlSchemaContentProcessing processContents) {
            XmlSchemaComplexType localAnyType = new XmlSchemaComplexType();
            localAnyType.SetQualifiedName(DatatypeImplementation.QnAnyType);
 
            XmlSchemaAny anyElement = new XmlSchemaAny();
            anyElement.MinOccurs = decimal.Zero;
            anyElement.MaxOccurs = decimal.MaxValue;
 
            anyElement.ProcessContents = processContents;
            anyElement.BuildNamespaceList(null);
            XmlSchemaSequence seq = new XmlSchemaSequence();
            seq.Items.Add(anyElement);
 
            localAnyType.SetContentTypeParticle(seq);
            localAnyType.SetContentType(XmlSchemaContentType.Mixed);
 
            localAnyType.ElementDecl = SchemaElementDecl.CreateAnyTypeElementDecl();
            localAnyType.ElementDecl.SchemaType = localAnyType;
 
            //Create contentValidator for Any
            ParticleContentValidator contentValidator = new ParticleContentValidator(XmlSchemaContentType.Mixed);
            contentValidator.Start();
            contentValidator.OpenGroup();
            contentValidator.AddNamespaceList(anyElement.NamespaceList, anyElement);
            contentValidator.AddStar();
            contentValidator.CloseGroup();
            ContentValidator anyContentValidator = contentValidator.Finish(true);
            localAnyType.ElementDecl.ContentValidator = anyContentValidator;
 
            XmlSchemaAnyAttribute anyAttribute = new XmlSchemaAnyAttribute();
            anyAttribute.ProcessContents = processContents;
            anyAttribute.BuildNamespaceList(null);
            localAnyType.SetAttributeWildcard(anyAttribute);
            localAnyType.ElementDecl.AnyAttribute = anyAttribute;
            return localAnyType;
        }
 
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.XmlSchemaComplexType"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public XmlSchemaComplexType() {
        }
 
 
        [XmlIgnore]
        internal static XmlSchemaComplexType AnyType {
             get { return anyTypeLax; }
        }
 
        [XmlIgnore]
        internal static XmlSchemaComplexType UntypedAnyType {
             get { return untypedAnyType; }
        }
        
        [XmlIgnore]
        internal static XmlSchemaComplexType AnyTypeSkip {
             get { return anyTypeSkip; }
        }
 
        internal static ContentValidator AnyTypeContentValidator {
            get {
                return anyTypeLax.ElementDecl.ContentValidator;
            }
        }
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.IsAbstract"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [XmlAttribute("abstract"), DefaultValue(false)]
        public bool IsAbstract {
            get {
                return (pvFlags & isAbstractMask) != 0;
            }
            set {
                if (value) {
                    pvFlags = (byte)(pvFlags | isAbstractMask);
                }
                else {
                    pvFlags = (byte)(pvFlags & ~isAbstractMask);
                }
            }
        }
 
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.Block"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [XmlAttribute("block"), DefaultValue(XmlSchemaDerivationMethod.None)]
        public XmlSchemaDerivationMethod Block {
            get { return block; }
            set { block = value; }
        }
 
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.IsMixed"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [XmlAttribute("mixed"), DefaultValue(false)]
        public override bool IsMixed {
            get {
                return (pvFlags & isMixedMask) != 0;
            }
            set {
                if (value) {
                    pvFlags = (byte)(pvFlags | isMixedMask);
                }
                else {
                    pvFlags = (byte)(pvFlags & ~isMixedMask);
                }
            }
        }
 
 
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.ContentModel"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [XmlElement("simpleContent", typeof(XmlSchemaSimpleContent)),
         XmlElement("complexContent", typeof(XmlSchemaComplexContent))]
        public XmlSchemaContentModel ContentModel {
            get { return contentModel; }
            set { contentModel = value; }
        }
 
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.Particle"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [XmlElement("group", typeof(XmlSchemaGroupRef)),
         XmlElement("choice", typeof(XmlSchemaChoice)),
         XmlElement("all", typeof(XmlSchemaAll)),
         XmlElement("sequence", typeof(XmlSchemaSequence))]
        public XmlSchemaParticle Particle {
            get { return particle; }
            set { particle = value; }
        }
 
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.Attributes"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [XmlElement("attribute", typeof(XmlSchemaAttribute)),
         XmlElement("attributeGroup", typeof(XmlSchemaAttributeGroupRef))]
        public XmlSchemaObjectCollection Attributes {
            get {
                if (attributes == null) {
                    attributes = new XmlSchemaObjectCollection();
                }
                return attributes;
            }
        }
 
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.AnyAttribute"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [XmlElement("anyAttribute")]
        public XmlSchemaAnyAttribute AnyAttribute {
            get { return anyAttribute; }
            set { anyAttribute = value; }
        }
 
 
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.ContentType"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [XmlIgnore]
        public XmlSchemaContentType ContentType {
            get { return SchemaContentType; }
        }
 
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.ContentTypeParticle"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [XmlIgnore]
        public XmlSchemaParticle ContentTypeParticle {
            get { return contentTypeParticle; }
        }
 
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.BlockResolved"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [XmlIgnore]
        public XmlSchemaDerivationMethod BlockResolved {
             get { return blockResolved; }
        }
 
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.AttributeUses"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [XmlIgnore]
        public XmlSchemaObjectTable AttributeUses {
            get {
                if (attributeUses == null) {
                    attributeUses = new XmlSchemaObjectTable();
                }
                return attributeUses;
            }
        }
 
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.AttributeWildcard"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [XmlIgnore]
        public XmlSchemaAnyAttribute AttributeWildcard {
            get { return attributeWildcard; }
        }
 
        /// <include file='doc\XmlSchemaComplexType.uex' path='docs/doc[@for="XmlSchemaComplexType.LocalElements"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [XmlIgnore]
        internal XmlSchemaObjectTable LocalElements {
            get {
                if (localElements == null) {
                    localElements = new XmlSchemaObjectTable();
                }
                return localElements;
            }
        }
 
        internal void SetContentTypeParticle(XmlSchemaParticle value) { 
            contentTypeParticle = value; 
        }
 
        internal void SetBlockResolved(XmlSchemaDerivationMethod value) {
             blockResolved = value; 
        }
 
        internal void SetAttributeWildcard(XmlSchemaAnyAttribute value) {
             attributeWildcard = value; 
        }
 
        internal bool HasWildCard {
            get {
                return (pvFlags & wildCardMask) != 0;
            }
            set {
                if (value) {
                    pvFlags = (byte)(pvFlags | wildCardMask);
                }
                else {
                    pvFlags = (byte)(pvFlags & ~wildCardMask);
                }
            }
        }
 
        internal override XmlQualifiedName DerivedFrom {
            get {
                if (contentModel == null) {
                    // type derived from anyType
                    return XmlQualifiedName.Empty;
                }
                if (contentModel.Content is XmlSchemaComplexContentRestriction)
                    return ((XmlSchemaComplexContentRestriction)contentModel.Content).BaseTypeName;
                else if (contentModel.Content is XmlSchemaComplexContentExtension)
                    return ((XmlSchemaComplexContentExtension)contentModel.Content).BaseTypeName;
                else if (contentModel.Content is XmlSchemaSimpleContentRestriction)
                    return ((XmlSchemaSimpleContentRestriction)contentModel.Content).BaseTypeName;
                else if (contentModel.Content is XmlSchemaSimpleContentExtension)
                    return ((XmlSchemaSimpleContentExtension)contentModel.Content).BaseTypeName;
                else
                    return XmlQualifiedName.Empty;
            }
        }
        
        internal void SetAttributes(XmlSchemaObjectCollection newAttributes) {
            attributes = newAttributes;
        }
 
        internal bool ContainsIdAttribute(bool findAll) {
            int idCount = 0;
            foreach(XmlSchemaAttribute attribute in this.AttributeUses.Values) {
                if (attribute.Use != XmlSchemaUse.Prohibited) {
                    XmlSchemaDatatype datatype = attribute.Datatype;
                    if (datatype != null && datatype.TypeCode == XmlTypeCode.Id) {
                        idCount++;
                        if (idCount > 1) { //two or more attributes is error
                            break;
                        }
                    }
                }
            }
            return findAll ? (idCount > 1) : (idCount > 0);
        }
 
        internal override XmlSchemaObject Clone() {
            System.Diagnostics.Debug.Assert(false, "Should never call Clone() on XmlSchemaComplexType. Call Clone(XmlSchema) instead.");
            return Clone(null);
        }
 
        internal XmlSchemaObject Clone(XmlSchema parentSchema) {
            XmlSchemaComplexType complexType = (XmlSchemaComplexType)MemberwiseClone();
            
            //Deep clone the QNames as these will be updated on chameleon includes
            if (complexType.ContentModel != null) { //simpleContent or complexContent
 
                XmlSchemaSimpleContent simpleContent = complexType.ContentModel as XmlSchemaSimpleContent; 
                if (simpleContent != null) {
                    XmlSchemaSimpleContent newSimpleContent = (XmlSchemaSimpleContent)simpleContent.Clone();
                    
                    XmlSchemaSimpleContentExtension simpleExt =  simpleContent.Content as XmlSchemaSimpleContentExtension;   
                    if (simpleExt != null) {
                        XmlSchemaSimpleContentExtension newSimpleExt = (XmlSchemaSimpleContentExtension)simpleExt.Clone();
                        newSimpleExt.BaseTypeName = simpleExt.BaseTypeName.Clone();
                        newSimpleExt.SetAttributes(CloneAttributes(simpleExt.Attributes));
                        newSimpleContent.Content = newSimpleExt;
                    }
                    else { //simpleContent.Content is XmlSchemaSimpleContentRestriction
                        XmlSchemaSimpleContentRestriction simpleRest = (XmlSchemaSimpleContentRestriction)simpleContent.Content;
                        XmlSchemaSimpleContentRestriction newSimpleRest = (XmlSchemaSimpleContentRestriction)simpleRest.Clone();
                        newSimpleRest.BaseTypeName = simpleRest.BaseTypeName.Clone();
                        newSimpleRest.SetAttributes(CloneAttributes(simpleRest.Attributes));
                        newSimpleContent.Content = newSimpleRest;
                    }
                    
                    complexType.ContentModel = newSimpleContent;
                }
                else { // complexType.ContentModel is XmlSchemaComplexContent
                    XmlSchemaComplexContent complexContent = (XmlSchemaComplexContent)complexType.ContentModel;
                    XmlSchemaComplexContent newComplexContent = (XmlSchemaComplexContent)complexContent.Clone();
 
                    XmlSchemaComplexContentExtension complexExt = complexContent.Content as XmlSchemaComplexContentExtension;
                    if (complexExt != null) {
                        XmlSchemaComplexContentExtension newComplexExt = (XmlSchemaComplexContentExtension)complexExt.Clone();
                        newComplexExt.BaseTypeName = complexExt.BaseTypeName.Clone();
                        newComplexExt.SetAttributes(CloneAttributes(complexExt.Attributes));
                        if (HasParticleRef(complexExt.Particle, parentSchema)) {
                            newComplexExt.Particle = CloneParticle(complexExt.Particle, parentSchema);
                        }
                        newComplexContent.Content = newComplexExt;
                    }
                    else { // complexContent.Content is XmlSchemaComplexContentRestriction
                        XmlSchemaComplexContentRestriction complexRest = complexContent.Content as XmlSchemaComplexContentRestriction;
                        XmlSchemaComplexContentRestriction newComplexRest = (XmlSchemaComplexContentRestriction)complexRest.Clone();
                        newComplexRest.BaseTypeName = complexRest.BaseTypeName.Clone();
                        newComplexRest.SetAttributes(CloneAttributes(complexRest.Attributes));
                        if (HasParticleRef(newComplexRest.Particle, parentSchema)) {
                            newComplexRest.Particle = CloneParticle(newComplexRest.Particle, parentSchema);
                        }
                        newComplexContent.Content = newComplexRest;
                    }
                    complexType.ContentModel = newComplexContent;
                }
            }
            else { //equals XmlSchemaComplexContent with baseType is anyType
                if (HasParticleRef(complexType.Particle, parentSchema)) {
                    complexType.Particle = CloneParticle(complexType.Particle, parentSchema);
                }
                complexType.SetAttributes(CloneAttributes(complexType.Attributes));
            }
            complexType.ClearCompiledState();
            return complexType;
        }
 
        private void ClearCompiledState() {
            //Re-set post-compiled state for cloned object
            this.attributeUses = null;
            this.localElements = null;
            this.attributeWildcard = null;
            this.contentTypeParticle = XmlSchemaParticle.Empty;
            this.blockResolved = XmlSchemaDerivationMethod.None;
        }
 
        internal static XmlSchemaObjectCollection CloneAttributes(XmlSchemaObjectCollection attributes) {
            if (HasAttributeQNameRef(attributes)) {
                XmlSchemaObjectCollection newAttributes = attributes.Clone();
                XmlSchemaAttributeGroupRef attributeGroupRef;
                XmlSchemaAttributeGroupRef newAttGroupRef;
                XmlSchemaObject xso;
                XmlSchemaAttribute att;
 
                for (int i = 0; i < attributes.Count; i++) {
                    xso = attributes[i];
                    attributeGroupRef = xso as XmlSchemaAttributeGroupRef;
                    if (attributeGroupRef != null) {
                        newAttGroupRef = (XmlSchemaAttributeGroupRef)attributeGroupRef.Clone();
                        newAttGroupRef.RefName = attributeGroupRef.RefName.Clone();
                        newAttributes[i] = newAttGroupRef;
                    }
                    else { //Its XmlSchemaAttribute
                        att = xso as XmlSchemaAttribute;
                        if (!att.RefName.IsEmpty || !att.SchemaTypeName.IsEmpty) {
                            newAttributes[i] = att.Clone();     
                        }
                    }
                }
                return newAttributes;
            }
            return attributes;
        }
 
        private static XmlSchemaObjectCollection CloneGroupBaseParticles(XmlSchemaObjectCollection groupBaseParticles, XmlSchema parentSchema) {
            XmlSchemaObjectCollection newParticles = groupBaseParticles.Clone();
 
            for (int i = 0; i < groupBaseParticles.Count; i++) {
                XmlSchemaParticle p = (XmlSchemaParticle)groupBaseParticles[i];
                newParticles[i] = CloneParticle(p, parentSchema);
            }
            return newParticles; 
        }
 
        internal static XmlSchemaParticle CloneParticle(XmlSchemaParticle particle, XmlSchema parentSchema) {
            XmlSchemaGroupBase groupBase = particle as XmlSchemaGroupBase;
            if (groupBase != null) { //Choice or sequence
                XmlSchemaGroupBase newGroupBase = groupBase;
 
                XmlSchemaObjectCollection newGroupbaseParticles = CloneGroupBaseParticles(groupBase.Items, parentSchema);
                newGroupBase = (XmlSchemaGroupBase)groupBase.Clone();
                newGroupBase.SetItems(newGroupbaseParticles);
                return newGroupBase;
            }
            else if (particle is XmlSchemaGroupRef) { // group ref
                XmlSchemaGroupRef newGroupRef = (XmlSchemaGroupRef)particle.Clone();
                newGroupRef.RefName = newGroupRef.RefName.Clone();
                return newGroupRef;
            }
            else {
                XmlSchemaElement oldElem = particle as XmlSchemaElement;
                // If the particle is an element and one of the following is true:
                //   - it references another element by name
                //   - it references its type by name
                //   - it's form (effective) is qualified (meaning it will inherint namespace from chameleon includes if that happens)
                // then the element itself needs to be cloned.
                if (oldElem != null && (!oldElem.RefName.IsEmpty || !oldElem.SchemaTypeName.IsEmpty ||
                    GetResolvedElementForm(parentSchema, oldElem) == XmlSchemaForm.Qualified)) {
                    XmlSchemaElement newElem = (XmlSchemaElement)oldElem.Clone(parentSchema);
                    return newElem;
                }
            }
            return particle;
        }
 
        // This method returns the effective value of the "element form" for the specified element in the specified
        //   parentSchema. Element form is either qualified, unqualified or none. If it's qualified it means that
        //   if the element doesn't declare its own namespace the targetNamespace of the schema is used instead.
        // The element form can be either specified on the element itself via the "form" attribute or 
        //   if that one is not present its inheritted from the value of the elementFormDefault attribute on the owning 
        //   schema.
        private static XmlSchemaForm GetResolvedElementForm(XmlSchema parentSchema, XmlSchemaElement element) {
            if (element.Form == XmlSchemaForm.None && parentSchema != null) {
                return parentSchema.ElementFormDefault;
            }
            else {
                return element.Form;
            }
        }
 
        internal static bool HasParticleRef(XmlSchemaParticle particle, XmlSchema parentSchema) {
            XmlSchemaGroupBase groupBase = particle as XmlSchemaGroupBase;
            if (groupBase != null) {
                bool foundRef = false;
                int i = 0;
                while (i < groupBase.Items.Count && !foundRef) {
                    XmlSchemaParticle p = (XmlSchemaParticle)groupBase.Items[i++];
                    if (p is XmlSchemaGroupRef) {
                        foundRef = true;
                    }
                    else {
                        XmlSchemaElement elem = p as XmlSchemaElement;
                        // This is the same condition as in the CloneParticle method
                        //   that's on purpose. This method is used to determine if we need to clone the whole particle.
                        //   If we do, then the CloneParticle is called and it will try to clone only
                        //   those elements which need cloning - and those are the ones matching this condition.
                        if (elem != null && (!elem.RefName.IsEmpty || !elem.SchemaTypeName.IsEmpty ||
                            GetResolvedElementForm(parentSchema, elem) == XmlSchemaForm.Qualified)) {
                            foundRef = true;
                        }
                        else {
                            foundRef = HasParticleRef(p, parentSchema);
                        }
                    }
                }
                return foundRef;
            }
            else if (particle is XmlSchemaGroupRef) {
                return true;
            }
            return false;
        }
 
        internal static bool HasAttributeQNameRef(XmlSchemaObjectCollection attributes) {
            for (int i = 0; i < attributes.Count; ++i) {
                if (attributes[i] is XmlSchemaAttributeGroupRef) {
                    return true;
                }
                else {
                    XmlSchemaAttribute attribute = attributes[i] as XmlSchemaAttribute;
                    if (!attribute.RefName.IsEmpty || !attribute.SchemaTypeName.IsEmpty) {
                        return true;
                    }
                }
            }
            return false;
        }
    }
 
}