//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // Microsoft //------------------------------------------------------------------------------ namespace System.Xml.Serialization { using System; using System.Collections; using System.Xml.Schema; using System.Xml; using System.ComponentModel; using System.Diagnostics; /// /// public class SoapSchemaExporter { internal const XmlSchemaForm elementFormDefault = XmlSchemaForm.Qualified; XmlSchemas schemas; Hashtable types = new Hashtable(); // StructMapping/EnumMapping -> XmlSchemaComplexType/XmlSchemaSimpleType bool exportedRoot; TypeScope scope; XmlDocument document; static XmlQualifiedName ArrayQName = new XmlQualifiedName(Soap.Array, Soap.Encoding); static XmlQualifiedName ArrayTypeQName = new XmlQualifiedName(Soap.ArrayType, Soap.Encoding); /// /// /// [To be supplied.] /// public SoapSchemaExporter(XmlSchemas schemas) { this.schemas = schemas; } /// /// /// [To be supplied.] /// public void ExportTypeMapping(XmlTypeMapping xmlTypeMapping) { CheckScope(xmlTypeMapping.Scope); ExportTypeMapping(xmlTypeMapping.Mapping, null); } /// /// /// [To be supplied.] /// public void ExportMembersMapping(XmlMembersMapping xmlMembersMapping) { ExportMembersMapping(xmlMembersMapping, false); } /// /// /// [To be supplied.] /// public void ExportMembersMapping(XmlMembersMapping xmlMembersMapping, bool exportEnclosingType) { CheckScope(xmlMembersMapping.Scope); MembersMapping membersMapping = (MembersMapping)xmlMembersMapping.Accessor.Mapping; if (exportEnclosingType) { ExportTypeMapping(membersMapping, null); } else { foreach (MemberMapping memberMapping in membersMapping.Members) { if (memberMapping.Elements.Length > 0) ExportTypeMapping(memberMapping.Elements[0].Mapping, null); } } } void CheckScope(TypeScope scope) { if (this.scope == null) { this.scope = scope; } else if (this.scope != scope) { throw new InvalidOperationException(Res.GetString(Res.XmlMappingsScopeMismatch)); } } internal XmlDocument Document { get { if (document == null) document = new XmlDocument(); return document; } } void CheckForDuplicateType(string newTypeName, string newNamespace){ XmlSchema schema = schemas[newNamespace]; if (schema != null){ foreach (XmlSchemaObject o in schema.Items) { XmlSchemaType type = o as XmlSchemaType; if ( type != null && type.Name == newTypeName) throw new InvalidOperationException(Res.GetString(Res.XmlDuplicateTypeName, newTypeName, newNamespace)); } } } void AddSchemaItem(XmlSchemaObject item, string ns, string referencingNs) { if (!SchemaContainsItem(item, ns)) { XmlSchema schema = schemas[ns]; if (schema == null) { schema = new XmlSchema(); schema.TargetNamespace = ns == null || ns.Length == 0 ? null : ns; #pragma warning disable 429 // unreachable code detected: elementFormDefault is const so it will never be Unqualified schema.ElementFormDefault = elementFormDefault == XmlSchemaForm.Unqualified ? XmlSchemaForm.None : elementFormDefault; #pragma warning restore 429 schemas.Add(schema); } schema.Items.Add(item); } if (referencingNs != null) AddSchemaImport(ns, referencingNs); } void AddSchemaImport(string ns, string referencingNs) { if (referencingNs == null || ns == null) return; if (ns == referencingNs) return; XmlSchema schema = schemas[referencingNs]; if (schema == null) throw new InvalidOperationException(Res.GetString(Res.XmlMissingSchema, referencingNs)); if (ns != null && ns.Length > 0 && FindImport(schema, ns) == null) { XmlSchemaImport import = new XmlSchemaImport(); import.Namespace = ns; schema.Includes.Add(import); } } bool SchemaContainsItem(XmlSchemaObject item, string ns) { XmlSchema schema = schemas[ns]; if (schema != null) { return schema.Items.Contains(item); } return false; } XmlSchemaImport FindImport(XmlSchema schema, string ns) { foreach (object item in schema.Includes) { if (item is XmlSchemaImport) { XmlSchemaImport import = (XmlSchemaImport)item; if (import.Namespace == ns) { return import; } } } return null; } XmlQualifiedName ExportTypeMapping(TypeMapping mapping, string ns) { if (mapping is ArrayMapping) return ExportArrayMapping((ArrayMapping)mapping, ns); else if (mapping is EnumMapping) return ExportEnumMapping((EnumMapping)mapping, ns); else if (mapping is PrimitiveMapping) { PrimitiveMapping pm = (PrimitiveMapping)mapping; if (pm.TypeDesc.IsXsdType) { return ExportPrimitiveMapping(pm); } else { return ExportNonXsdPrimitiveMapping(pm, ns); } } else if (mapping is StructMapping) return ExportStructMapping((StructMapping)mapping, ns); else if (mapping is NullableMapping) return ExportTypeMapping(((NullableMapping)mapping).BaseMapping, ns); else if (mapping is MembersMapping) return ExportMembersMapping((MembersMapping)mapping, ns); else throw new ArgumentException(Res.GetString(Res.XmlInternalError), "mapping"); } XmlQualifiedName ExportNonXsdPrimitiveMapping(PrimitiveMapping mapping, string ns) { XmlSchemaType type = mapping.TypeDesc.DataType; if (!SchemaContainsItem(type, UrtTypes.Namespace)) { AddSchemaItem(type, UrtTypes.Namespace, ns); } else { AddSchemaImport(UrtTypes.Namespace, ns); } return new XmlQualifiedName(mapping.TypeDesc.DataType.Name, UrtTypes.Namespace); } XmlQualifiedName ExportPrimitiveMapping(PrimitiveMapping mapping) { return new XmlQualifiedName(mapping.TypeDesc.DataType.Name, XmlSchema.Namespace); } XmlQualifiedName ExportArrayMapping(ArrayMapping mapping, string ns) { // for the Rpc ArrayMapping different mappings could have the same schema type // we link all mappings corresponding to the same type together // loop through all mapping that will map to the same complexType, and export only one, // the obvious choice is the last one. while (mapping.Next != null) { mapping = mapping.Next; } XmlSchemaComplexType type = (XmlSchemaComplexType)types[mapping]; if (type == null) { CheckForDuplicateType(mapping.TypeName, mapping.Namespace); type = new XmlSchemaComplexType(); type.Name = mapping.TypeName; types.Add(mapping, type); // we need to add the type first, to make sure that the schema get created AddSchemaItem(type, mapping.Namespace, ns); AddSchemaImport(Soap.Encoding, mapping.Namespace); AddSchemaImport(Wsdl.Namespace, mapping.Namespace); XmlSchemaComplexContentRestriction restriction = new XmlSchemaComplexContentRestriction(); XmlQualifiedName qname = ExportTypeMapping(mapping.Elements[0].Mapping, mapping.Namespace); if (qname.IsEmpty) { // this is a root mapping qname = new XmlQualifiedName(Soap.UrType, XmlSchema.Namespace); } // XmlSchemaAttribute attr = new XmlSchemaAttribute(); attr.RefName = ArrayTypeQName; XmlAttribute attribute = new XmlAttribute("wsdl", Wsdl.ArrayType, Wsdl.Namespace, Document); attribute.Value = qname.Namespace + ":" + qname.Name + "[]"; attr.UnhandledAttributes = new XmlAttribute[] {attribute}; restriction.Attributes.Add(attr); restriction.BaseTypeName = ArrayQName; XmlSchemaComplexContent model = new XmlSchemaComplexContent(); model.Content = restriction; type.ContentModel = model; if (qname.Namespace != XmlSchema.Namespace) AddSchemaImport(qname.Namespace, mapping.Namespace); } else { AddSchemaImport(mapping.Namespace, ns); } return new XmlQualifiedName(mapping.TypeName, mapping.Namespace); } void ExportElementAccessors(XmlSchemaGroupBase group, ElementAccessor[] accessors, bool repeats, bool valueTypeOptional, string ns) { if (accessors.Length == 0) return; if (accessors.Length == 1) { ExportElementAccessor(group, accessors[0], repeats, valueTypeOptional, ns); } else { XmlSchemaChoice choice = new XmlSchemaChoice(); choice.MaxOccurs = repeats ? decimal.MaxValue : 1; choice.MinOccurs = repeats ? 0 : 1; for (int i = 0; i < accessors.Length; i++) ExportElementAccessor(choice, accessors[i], false, valueTypeOptional, ns); if (choice.Items.Count > 0) group.Items.Add(choice); } } void ExportElementAccessor(XmlSchemaGroupBase group, ElementAccessor accessor, bool repeats, bool valueTypeOptional, string ns) { XmlSchemaElement element = new XmlSchemaElement(); element.MinOccurs = repeats || valueTypeOptional ? 0 : 1; element.MaxOccurs = repeats ? decimal.MaxValue : 1; element.Name = accessor.Name; element.IsNillable = accessor.IsNullable || accessor.Mapping is NullableMapping; element.Form = XmlSchemaForm.Unqualified; element.SchemaTypeName = ExportTypeMapping(accessor.Mapping, accessor.Namespace); group.Items.Add(element); } XmlQualifiedName ExportRootMapping(StructMapping mapping) { if (!exportedRoot) { exportedRoot = true; ExportDerivedMappings(mapping); } return new XmlQualifiedName(Soap.UrType, XmlSchema.Namespace); } XmlQualifiedName ExportStructMapping(StructMapping mapping, string ns) { if (mapping.TypeDesc.IsRoot) return ExportRootMapping(mapping); XmlSchemaComplexType type = (XmlSchemaComplexType)types[mapping]; if (type == null) { if (!mapping.IncludeInSchema) throw new InvalidOperationException(Res.GetString(Res.XmlSoapCannotIncludeInSchema, mapping.TypeDesc.Name)); CheckForDuplicateType(mapping.TypeName, mapping.Namespace); type = new XmlSchemaComplexType(); type.Name = mapping.TypeName; types.Add(mapping, type); AddSchemaItem(type, mapping.Namespace, ns); type.IsAbstract = mapping.TypeDesc.IsAbstract; if (mapping.BaseMapping != null && mapping.BaseMapping.IncludeInSchema) { XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension(); extension.BaseTypeName = ExportStructMapping(mapping.BaseMapping, mapping.Namespace); XmlSchemaComplexContent model = new XmlSchemaComplexContent(); model.Content = extension; type.ContentModel = model; } ExportTypeMembers(type, mapping.Members, mapping.Namespace); ExportDerivedMappings(mapping); } else { AddSchemaImport(mapping.Namespace, ns); } return new XmlQualifiedName(type.Name, mapping.Namespace); } XmlQualifiedName ExportMembersMapping(MembersMapping mapping, string ns) { XmlSchemaComplexType type = (XmlSchemaComplexType)types[mapping]; if(type == null) { CheckForDuplicateType(mapping.TypeName, mapping.Namespace); type = new XmlSchemaComplexType(); type.Name = mapping.TypeName; types.Add(mapping, type); AddSchemaItem(type, mapping.Namespace, ns); ExportTypeMembers(type, mapping.Members, mapping.Namespace); } else { AddSchemaImport(mapping.Namespace, ns); } return new XmlQualifiedName(type.Name, mapping.Namespace); } void ExportTypeMembers(XmlSchemaComplexType type, MemberMapping[] members, string ns) { XmlSchemaGroupBase seq = new XmlSchemaSequence(); for (int i = 0; i < members.Length; i++) { MemberMapping member = members[i]; if (member.Elements.Length > 0) { bool valueTypeOptional = member.CheckSpecified != SpecifiedAccessor.None || member.CheckShouldPersist || !member.TypeDesc.IsValueType; ExportElementAccessors(seq, member.Elements, false, valueTypeOptional, ns); } } if (seq.Items.Count > 0) { if (type.ContentModel != null) { if (type.ContentModel.Content is XmlSchemaComplexContentExtension) ((XmlSchemaComplexContentExtension)type.ContentModel.Content).Particle = seq; else if (type.ContentModel.Content is XmlSchemaComplexContentRestriction) ((XmlSchemaComplexContentRestriction)type.ContentModel.Content).Particle = seq; else throw new InvalidOperationException(Res.GetString(Res.XmlInvalidContent, type.ContentModel.Content.GetType().Name)); } else { type.Particle = seq; } } } void ExportDerivedMappings(StructMapping mapping) { for (StructMapping derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping) { if (derived.IncludeInSchema) ExportStructMapping(derived, mapping.TypeDesc.IsRoot ? null : mapping.Namespace); } } XmlQualifiedName ExportEnumMapping(EnumMapping mapping, string ns) { XmlSchemaSimpleType dataType = (XmlSchemaSimpleType)types[mapping]; if (dataType == null) { CheckForDuplicateType(mapping.TypeName, mapping.Namespace); dataType = new XmlSchemaSimpleType(); dataType.Name = mapping.TypeName; types.Add(mapping, dataType); AddSchemaItem(dataType, mapping.Namespace, ns); XmlSchemaSimpleTypeRestriction restriction = new XmlSchemaSimpleTypeRestriction(); restriction.BaseTypeName = new XmlQualifiedName("string", XmlSchema.Namespace); for (int i = 0; i < mapping.Constants.Length; i++) { ConstantMapping constant = mapping.Constants[i]; XmlSchemaEnumerationFacet enumeration = new XmlSchemaEnumerationFacet(); enumeration.Value = constant.XmlName; restriction.Facets.Add(enumeration); } if (!mapping.IsFlags) { dataType.Content = restriction; } else { XmlSchemaSimpleTypeList list = new XmlSchemaSimpleTypeList(); XmlSchemaSimpleType enumType = new XmlSchemaSimpleType(); enumType.Content = restriction; list.ItemType = enumType; dataType.Content = list; } } else { AddSchemaImport(mapping.Namespace, ns); } return new XmlQualifiedName(mapping.TypeName, mapping.Namespace); } } }