386 lines
18 KiB
C#
386 lines
18 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="SoapSchemaExporter.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
// <owner current="true" primary="true">[....]</owner>
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.Xml.Serialization {
|
||
|
|
||
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Xml.Schema;
|
||
|
using System.Xml;
|
||
|
using System.ComponentModel;
|
||
|
using System.Diagnostics;
|
||
|
|
||
|
/// <include file='doc\SoapSchemaExporter.uex' path='docs/doc[@for="SoapSchemaExporter"]/*' />
|
||
|
/// <internalonly/>
|
||
|
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);
|
||
|
|
||
|
/// <include file='doc\SoapSchemaExporter.uex' path='docs/doc[@for="SoapSchemaExporter.SoapSchemaExporter"]/*' />
|
||
|
/// <devdoc>
|
||
|
/// <para>[To be supplied.]</para>
|
||
|
/// </devdoc>
|
||
|
public SoapSchemaExporter(XmlSchemas schemas) {
|
||
|
this.schemas = schemas;
|
||
|
}
|
||
|
|
||
|
/// <include file='doc\SoapSchemaExporter.uex' path='docs/doc[@for="SoapSchemaExporter.ExportTypeMapping"]/*' />
|
||
|
/// <devdoc>
|
||
|
/// <para>[To be supplied.]</para>
|
||
|
/// </devdoc>
|
||
|
public void ExportTypeMapping(XmlTypeMapping xmlTypeMapping) {
|
||
|
CheckScope(xmlTypeMapping.Scope);
|
||
|
ExportTypeMapping(xmlTypeMapping.Mapping, null);
|
||
|
}
|
||
|
|
||
|
/// <include file='doc\SoapSchemaExporter.uex' path='docs/doc[@for="SoapSchemaExporter.ExportMembersMapping"]/*' />
|
||
|
/// <devdoc>
|
||
|
/// <para>[To be supplied.]</para>
|
||
|
/// </devdoc>
|
||
|
public void ExportMembersMapping(XmlMembersMapping xmlMembersMapping) {
|
||
|
ExportMembersMapping(xmlMembersMapping, false);
|
||
|
}
|
||
|
|
||
|
/// <include file='doc\SoapSchemaExporter.uex' path='docs/doc[@for="SoapSchemaExporter.ExportMembersMapping1"]/*' />
|
||
|
/// <devdoc>
|
||
|
/// <para>[To be supplied.]</para>
|
||
|
/// </devdoc>
|
||
|
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);
|
||
|
}
|
||
|
//<attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:float[]"/>
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
}
|