| File: System\Xml\Schema\XmlSchemaComplexType.cs | Web Access |
| Project: src\runtime\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Xml.Serialization; namespace System.Xml.Schema { public class XmlSchemaComplexType : XmlSchemaType { private static readonly XmlSchemaComplexType s_anyTypeLax = CreateAnyType(XmlSchemaContentProcessing.Lax); private static readonly XmlSchemaComplexType s_anyTypeSkip = CreateAnyType(XmlSchemaContentProcessing.Skip); private static readonly XmlSchemaComplexType s_untypedAnyType = CreateUntypedAnyType(); private XmlSchemaDerivationMethod _block = XmlSchemaDerivationMethod.None; private XmlSchemaContentModel? _contentModel; private XmlSchemaParticle? _particle; private XmlSchemaObjectCollection? _attributes; private XmlSchemaAnyAttribute? _anyAttribute; private XmlSchemaParticle _contentTypeParticle = XmlSchemaParticle.Empty; private XmlSchemaDerivationMethod _blockResolved; private XmlSchemaObjectTable? _localElements; private XmlSchemaObjectTable? _attributeUses; private XmlSchemaAnyAttribute? _attributeWildcard; //additional info for Partial validation private byte _pvFlags; private const byte wildCardMask = 0x01; private const byte isMixedMask = 0x02; private const byte isAbstractMask = 0x04; //const byte dupDeclMask = 0x08; private static XmlSchemaComplexType CreateUntypedAnyType() { // Create xdt:untypedAny var untypedAny = new XmlSchemaComplexType(); untypedAny.SetQualifiedName(new XmlQualifiedName("untypedAny", XmlReservedNs.NsXQueryDataType)); untypedAny.IsMixed = true; untypedAny.SetContentTypeParticle(s_anyTypeLax.ContentTypeParticle); untypedAny.SetContentType(XmlSchemaContentType.Mixed); untypedAny.ElementDecl = SchemaElementDecl.CreateAnyTypeElementDecl(); untypedAny.ElementDecl.SchemaType = untypedAny; untypedAny.ElementDecl.ContentValidator = AnyTypeContentValidator; return untypedAny; } private 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; } public XmlSchemaComplexType() { } [XmlIgnore] internal static XmlSchemaComplexType AnyType { get { return s_anyTypeLax; } } [XmlIgnore] internal static XmlSchemaComplexType UntypedAnyType { get { return s_untypedAnyType; } } [XmlIgnore] internal static XmlSchemaComplexType AnyTypeSkip { get { return s_anyTypeSkip; } } internal static ContentValidator AnyTypeContentValidator { get { return s_anyTypeLax.ElementDecl!.ContentValidator!; } } [XmlAttribute("abstract"), DefaultValue(false)] public bool IsAbstract { get { return (_pvFlags & isAbstractMask) != 0; } set { if (value) { _pvFlags = (byte)(_pvFlags | isAbstractMask); } else { _pvFlags = (byte)(_pvFlags & ~isAbstractMask); } } } [XmlAttribute("block"), DefaultValue(XmlSchemaDerivationMethod.None)] public XmlSchemaDerivationMethod Block { get { return _block; } set { _block = value; } } [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); } } } [XmlElement("simpleContent", typeof(XmlSchemaSimpleContent)), XmlElement("complexContent", typeof(XmlSchemaComplexContent))] public XmlSchemaContentModel? ContentModel { get { return _contentModel; } set { _contentModel = value; } } [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; } } [XmlElement("attribute", typeof(XmlSchemaAttribute)), XmlElement("attributeGroup", typeof(XmlSchemaAttributeGroupRef))] public XmlSchemaObjectCollection Attributes => _attributes ??= new XmlSchemaObjectCollection(); [XmlElement("anyAttribute")] public XmlSchemaAnyAttribute? AnyAttribute { get { return _anyAttribute; } set { _anyAttribute = value; } } [XmlIgnore] public XmlSchemaContentType ContentType { get { return SchemaContentType; } } [XmlIgnore] public XmlSchemaParticle ContentTypeParticle { get { return _contentTypeParticle; } } [XmlIgnore] public XmlSchemaDerivationMethod BlockResolved { get { return _blockResolved; } } [XmlIgnore] public XmlSchemaObjectTable AttributeUses => _attributeUses ??= new XmlSchemaObjectTable(); [XmlIgnore] public XmlSchemaAnyAttribute? AttributeWildcard { get { return _attributeWildcard; } } [XmlIgnore] internal XmlSchemaObjectTable LocalElements => _localElements ??= new XmlSchemaObjectTable(); 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.Fail("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 _attributeUses = null; _localElements = null; _attributeWildcard = null; _contentTypeParticle = XmlSchemaParticle.Empty; _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; } [return: NotNullIfNotNull(nameof(particle))] internal static XmlSchemaParticle? CloneParticle(XmlSchemaParticle? particle, XmlSchema? parentSchema) { XmlSchemaGroupBase? groupBase = particle as XmlSchemaGroupBase; if (groupBase != null) { //Choice or sequence XmlSchemaGroupBase newGroupBase; 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; } } }