1 //------------------------------------------------------------------------------
2 // <copyright file="SoapSchemaExporter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
8 namespace System
.Xml
.Serialization
{
11 using System
.Collections
;
12 using System
.Xml
.Schema
;
14 using System
.ComponentModel
;
15 using System
.Diagnostics
;
17 /// <include file='doc\SoapSchemaExporter.uex' path='docs/doc[@for="SoapSchemaExporter"]/*' />
19 public class SoapSchemaExporter
{
20 internal const XmlSchemaForm elementFormDefault
= XmlSchemaForm
.Qualified
;
22 Hashtable types
= new Hashtable(); // StructMapping/EnumMapping -> XmlSchemaComplexType/XmlSchemaSimpleType
27 static XmlQualifiedName ArrayQName
= new XmlQualifiedName(Soap
.Array
, Soap
.Encoding
);
28 static XmlQualifiedName ArrayTypeQName
= new XmlQualifiedName(Soap
.ArrayType
, Soap
.Encoding
);
30 /// <include file='doc\SoapSchemaExporter.uex' path='docs/doc[@for="SoapSchemaExporter.SoapSchemaExporter"]/*' />
32 /// <para>[To be supplied.]</para>
34 public SoapSchemaExporter(XmlSchemas schemas
) {
35 this.schemas
= schemas
;
38 /// <include file='doc\SoapSchemaExporter.uex' path='docs/doc[@for="SoapSchemaExporter.ExportTypeMapping"]/*' />
40 /// <para>[To be supplied.]</para>
42 public void ExportTypeMapping(XmlTypeMapping xmlTypeMapping
) {
43 CheckScope(xmlTypeMapping
.Scope
);
44 ExportTypeMapping(xmlTypeMapping
.Mapping
, null);
47 /// <include file='doc\SoapSchemaExporter.uex' path='docs/doc[@for="SoapSchemaExporter.ExportMembersMapping"]/*' />
49 /// <para>[To be supplied.]</para>
51 public void ExportMembersMapping(XmlMembersMapping xmlMembersMapping
) {
52 ExportMembersMapping(xmlMembersMapping
, false);
55 /// <include file='doc\SoapSchemaExporter.uex' path='docs/doc[@for="SoapSchemaExporter.ExportMembersMapping1"]/*' />
57 /// <para>[To be supplied.]</para>
59 public void ExportMembersMapping(XmlMembersMapping xmlMembersMapping
, bool exportEnclosingType
) {
60 CheckScope(xmlMembersMapping
.Scope
);
61 MembersMapping membersMapping
= (MembersMapping
)xmlMembersMapping
.Accessor
.Mapping
;
62 if (exportEnclosingType
) {
63 ExportTypeMapping(membersMapping
, null);
66 foreach (MemberMapping memberMapping
in membersMapping
.Members
) {
67 if (memberMapping
.Elements
.Length
> 0)
68 ExportTypeMapping(memberMapping
.Elements
[0].Mapping
, null);
73 void CheckScope(TypeScope scope
) {
74 if (this.scope
== null) {
77 else if (this.scope
!= scope
) {
78 throw new InvalidOperationException(Res
.GetString(Res
.XmlMappingsScopeMismatch
));
82 internal XmlDocument Document
{
84 if (document
== null) document
= new XmlDocument();
89 void CheckForDuplicateType(string newTypeName
, string newNamespace
){
90 XmlSchema schema
= schemas
[newNamespace
];
92 foreach (XmlSchemaObject o
in schema
.Items
) {
93 XmlSchemaType type
= o
as XmlSchemaType
;
94 if ( type
!= null && type
.Name
== newTypeName
)
95 throw new InvalidOperationException(Res
.GetString(Res
.XmlDuplicateTypeName
, newTypeName
, newNamespace
));
101 void AddSchemaItem(XmlSchemaObject item
, string ns
, string referencingNs
) {
102 if (!SchemaContainsItem(item
, ns
)) {
103 XmlSchema schema
= schemas
[ns
];
104 if (schema
== null) {
105 schema
= new XmlSchema();
106 schema
.TargetNamespace
= ns
== null || ns
.Length
== 0 ? null : ns
;
108 #pragma warning disable 429 // unreachable code detected: elementFormDefault is const so it will never be Unqualified
109 schema
.ElementFormDefault
= elementFormDefault
== XmlSchemaForm
.Unqualified
? XmlSchemaForm
.None
: elementFormDefault
;
110 #pragma warning restore 429
113 schema
.Items
.Add(item
);
115 if (referencingNs
!= null)
116 AddSchemaImport(ns
, referencingNs
);
119 void AddSchemaImport(string ns
, string referencingNs
) {
120 if (referencingNs
== null || ns
== null) return;
121 if (ns
== referencingNs
) return;
122 XmlSchema schema
= schemas
[referencingNs
];
123 if (schema
== null) throw new InvalidOperationException(Res
.GetString(Res
.XmlMissingSchema
, referencingNs
));
124 if (ns
!= null && ns
.Length
> 0 && FindImport(schema
, ns
) == null) {
125 XmlSchemaImport import
= new XmlSchemaImport();
126 import
.Namespace
= ns
;
127 schema
.Includes
.Add(import
);
131 bool SchemaContainsItem(XmlSchemaObject item
, string ns
) {
132 XmlSchema schema
= schemas
[ns
];
133 if (schema
!= null) {
134 return schema
.Items
.Contains(item
);
139 XmlSchemaImport
FindImport(XmlSchema schema
, string ns
) {
140 foreach (object item
in schema
.Includes
) {
141 if (item
is XmlSchemaImport
) {
142 XmlSchemaImport import
= (XmlSchemaImport
)item
;
143 if (import
.Namespace
== ns
) {
151 XmlQualifiedName
ExportTypeMapping(TypeMapping mapping
, string ns
) {
152 if (mapping
is ArrayMapping
)
153 return ExportArrayMapping((ArrayMapping
)mapping
, ns
);
154 else if (mapping
is EnumMapping
)
155 return ExportEnumMapping((EnumMapping
)mapping
, ns
);
156 else if (mapping
is PrimitiveMapping
) {
157 PrimitiveMapping pm
= (PrimitiveMapping
)mapping
;
158 if (pm
.TypeDesc
.IsXsdType
) {
159 return ExportPrimitiveMapping(pm
);
162 return ExportNonXsdPrimitiveMapping(pm
, ns
);
165 else if (mapping
is StructMapping
)
166 return ExportStructMapping((StructMapping
)mapping
, ns
);
167 else if (mapping
is NullableMapping
)
168 return ExportTypeMapping(((NullableMapping
)mapping
).BaseMapping
, ns
);
169 else if (mapping
is MembersMapping
)
170 return ExportMembersMapping((MembersMapping
)mapping
, ns
);
172 throw new ArgumentException(Res
.GetString(Res
.XmlInternalError
), "mapping");
175 XmlQualifiedName
ExportNonXsdPrimitiveMapping(PrimitiveMapping mapping
, string ns
) {
176 XmlSchemaType type
= mapping
.TypeDesc
.DataType
;
177 if (!SchemaContainsItem(type
, UrtTypes
.Namespace
)) {
178 AddSchemaItem(type
, UrtTypes
.Namespace
, ns
);
181 AddSchemaImport(UrtTypes
.Namespace
, ns
);
183 return new XmlQualifiedName(mapping
.TypeDesc
.DataType
.Name
, UrtTypes
.Namespace
);
186 XmlQualifiedName
ExportPrimitiveMapping(PrimitiveMapping mapping
) {
187 return new XmlQualifiedName(mapping
.TypeDesc
.DataType
.Name
, XmlSchema
.Namespace
);
190 XmlQualifiedName
ExportArrayMapping(ArrayMapping mapping
, string ns
) {
191 // for the Rpc ArrayMapping different mappings could have the same schema type
192 // we link all mappings corresponding to the same type together
193 // loop through all mapping that will map to the same complexType, and export only one,
194 // the obvious choice is the last one.
195 while (mapping
.Next
!= null) {
196 mapping
= mapping
.Next
;
199 XmlSchemaComplexType type
= (XmlSchemaComplexType
)types
[mapping
];
201 CheckForDuplicateType(mapping
.TypeName
, mapping
.Namespace
);
202 type
= new XmlSchemaComplexType();
203 type
.Name
= mapping
.TypeName
;
204 types
.Add(mapping
, type
);
206 // we need to add the type first, to make sure that the schema get created
207 AddSchemaItem(type
, mapping
.Namespace
, ns
);
208 AddSchemaImport(Soap
.Encoding
, mapping
.Namespace
);
209 AddSchemaImport(Wsdl
.Namespace
, mapping
.Namespace
);
211 XmlSchemaComplexContentRestriction restriction
= new XmlSchemaComplexContentRestriction();
212 XmlQualifiedName qname
= ExportTypeMapping(mapping
.Elements
[0].Mapping
, mapping
.Namespace
);
215 // this is a root mapping
216 qname
= new XmlQualifiedName(Soap
.UrType
, XmlSchema
.Namespace
);
218 //<attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:float[]"/>
219 XmlSchemaAttribute attr
= new XmlSchemaAttribute();
220 attr
.RefName
= ArrayTypeQName
;
221 XmlAttribute attribute
= new XmlAttribute("wsdl", Wsdl
.ArrayType
, Wsdl
.Namespace
, Document
);
222 attribute
.Value
= qname
.Namespace
+ ":" + qname
.Name
+ "[]";
224 attr
.UnhandledAttributes
= new XmlAttribute
[] {attribute}
;
225 restriction
.Attributes
.Add(attr
);
226 restriction
.BaseTypeName
= ArrayQName
;
227 XmlSchemaComplexContent model
= new XmlSchemaComplexContent();
228 model
.Content
= restriction
;
229 type
.ContentModel
= model
;
230 if (qname
.Namespace
!= XmlSchema
.Namespace
)
231 AddSchemaImport(qname
.Namespace
, mapping
.Namespace
);
234 AddSchemaImport(mapping
.Namespace
, ns
);
236 return new XmlQualifiedName(mapping
.TypeName
, mapping
.Namespace
);
239 void ExportElementAccessors(XmlSchemaGroupBase
group, ElementAccessor
[] accessors
, bool repeats
, bool valueTypeOptional
, string ns
) {
240 if (accessors
.Length
== 0) return;
241 if (accessors
.Length
== 1) {
242 ExportElementAccessor(group, accessors
[0], repeats
, valueTypeOptional
, ns
);
245 XmlSchemaChoice choice
= new XmlSchemaChoice();
246 choice
.MaxOccurs
= repeats
? decimal.MaxValue
: 1;
247 choice
.MinOccurs
= repeats
? 0 : 1;
248 for (int i
= 0; i
< accessors
.Length
; i
++)
249 ExportElementAccessor(choice
, accessors
[i
], false, valueTypeOptional
, ns
);
251 if (choice
.Items
.Count
> 0) group.Items
.Add(choice
);
256 void ExportElementAccessor(XmlSchemaGroupBase
group, ElementAccessor accessor
, bool repeats
, bool valueTypeOptional
, string ns
) {
257 XmlSchemaElement element
= new XmlSchemaElement();
258 element
.MinOccurs
= repeats
|| valueTypeOptional
? 0 : 1;
259 element
.MaxOccurs
= repeats
? decimal.MaxValue
: 1;
260 element
.Name
= accessor
.Name
;
261 element
.IsNillable
= accessor
.IsNullable
|| accessor
.Mapping
is NullableMapping
;
262 element
.Form
= XmlSchemaForm
.Unqualified
;
263 element
.SchemaTypeName
= ExportTypeMapping(accessor
.Mapping
, accessor
.Namespace
);
265 group.Items
.Add(element
);
268 XmlQualifiedName
ExportRootMapping(StructMapping mapping
) {
271 ExportDerivedMappings(mapping
);
273 return new XmlQualifiedName(Soap
.UrType
, XmlSchema
.Namespace
);
276 XmlQualifiedName
ExportStructMapping(StructMapping mapping
, string ns
) {
277 if (mapping
.TypeDesc
.IsRoot
) return ExportRootMapping(mapping
);
278 XmlSchemaComplexType type
= (XmlSchemaComplexType
)types
[mapping
];
280 if (!mapping
.IncludeInSchema
) throw new InvalidOperationException(Res
.GetString(Res
.XmlSoapCannotIncludeInSchema
, mapping
.TypeDesc
.Name
));
281 CheckForDuplicateType(mapping
.TypeName
, mapping
.Namespace
);
282 type
= new XmlSchemaComplexType();
283 type
.Name
= mapping
.TypeName
;
284 types
.Add(mapping
, type
);
285 AddSchemaItem(type
, mapping
.Namespace
, ns
);
286 type
.IsAbstract
= mapping
.TypeDesc
.IsAbstract
;
288 if (mapping
.BaseMapping
!= null && mapping
.BaseMapping
.IncludeInSchema
) {
289 XmlSchemaComplexContentExtension extension
= new XmlSchemaComplexContentExtension();
290 extension
.BaseTypeName
= ExportStructMapping(mapping
.BaseMapping
, mapping
.Namespace
);
291 XmlSchemaComplexContent model
= new XmlSchemaComplexContent();
292 model
.Content
= extension
;
293 type
.ContentModel
= model
;
295 ExportTypeMembers(type
, mapping
.Members
, mapping
.Namespace
);
296 ExportDerivedMappings(mapping
);
299 AddSchemaImport(mapping
.Namespace
, ns
);
301 return new XmlQualifiedName(type
.Name
, mapping
.Namespace
);
304 XmlQualifiedName
ExportMembersMapping(MembersMapping mapping
, string ns
) {
305 XmlSchemaComplexType type
= (XmlSchemaComplexType
)types
[mapping
];
307 CheckForDuplicateType(mapping
.TypeName
, mapping
.Namespace
);
308 type
= new XmlSchemaComplexType();
309 type
.Name
= mapping
.TypeName
;
310 types
.Add(mapping
, type
);
311 AddSchemaItem(type
, mapping
.Namespace
, ns
);
312 ExportTypeMembers(type
, mapping
.Members
, mapping
.Namespace
);
315 AddSchemaImport(mapping
.Namespace
, ns
);
317 return new XmlQualifiedName(type
.Name
, mapping
.Namespace
);
320 void ExportTypeMembers(XmlSchemaComplexType type
, MemberMapping
[] members
, string ns
) {
321 XmlSchemaGroupBase seq
= new XmlSchemaSequence();
322 for (int i
= 0; i
< members
.Length
; i
++) {
323 MemberMapping member
= members
[i
];
324 if (member
.Elements
.Length
> 0) {
325 bool valueTypeOptional
= member
.CheckSpecified
!= SpecifiedAccessor
.None
|| member
.CheckShouldPersist
|| !member
.TypeDesc
.IsValueType
;
326 ExportElementAccessors(seq
, member
.Elements
, false, valueTypeOptional
, ns
);
329 if (seq
.Items
.Count
> 0) {
330 if (type
.ContentModel
!= null) {
331 if (type
.ContentModel
.Content
is XmlSchemaComplexContentExtension
)
332 ((XmlSchemaComplexContentExtension
)type
.ContentModel
.Content
).Particle
= seq
;
333 else if (type
.ContentModel
.Content
is XmlSchemaComplexContentRestriction
)
334 ((XmlSchemaComplexContentRestriction
)type
.ContentModel
.Content
).Particle
= seq
;
336 throw new InvalidOperationException(Res
.GetString(Res
.XmlInvalidContent
, type
.ContentModel
.Content
.GetType().Name
));
344 void ExportDerivedMappings(StructMapping mapping
) {
345 for (StructMapping derived
= mapping
.DerivedMappings
; derived
!= null; derived
= derived
.NextDerivedMapping
) {
346 if (derived
.IncludeInSchema
) ExportStructMapping(derived
, mapping
.TypeDesc
.IsRoot
? null : mapping
.Namespace
);
350 XmlQualifiedName
ExportEnumMapping(EnumMapping mapping
, string ns
) {
351 XmlSchemaSimpleType dataType
= (XmlSchemaSimpleType
)types
[mapping
];
352 if (dataType
== null) {
353 CheckForDuplicateType(mapping
.TypeName
, mapping
.Namespace
);
354 dataType
= new XmlSchemaSimpleType();
355 dataType
.Name
= mapping
.TypeName
;
356 types
.Add(mapping
, dataType
);
357 AddSchemaItem(dataType
, mapping
.Namespace
, ns
);
359 XmlSchemaSimpleTypeRestriction restriction
= new XmlSchemaSimpleTypeRestriction();
360 restriction
.BaseTypeName
= new XmlQualifiedName("string", XmlSchema
.Namespace
);
362 for (int i
= 0; i
< mapping
.Constants
.Length
; i
++) {
363 ConstantMapping constant
= mapping
.Constants
[i
];
364 XmlSchemaEnumerationFacet enumeration
= new XmlSchemaEnumerationFacet();
365 enumeration
.Value
= constant
.XmlName
;
366 restriction
.Facets
.Add(enumeration
);
368 if (!mapping
.IsFlags
) {
369 dataType
.Content
= restriction
;
372 XmlSchemaSimpleTypeList list
= new XmlSchemaSimpleTypeList();
373 XmlSchemaSimpleType enumType
= new XmlSchemaSimpleType();
374 enumType
.Content
= restriction
;
375 list
.ItemType
= enumType
;
376 dataType
.Content
= list
;
380 AddSchemaImport(mapping
.Namespace
, ns
);
382 return new XmlQualifiedName(mapping
.TypeName
, mapping
.Namespace
);