Merge pull request #4540 from kumpera/android-changes-part1
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Description / WsdlExporter.cs
1 //
2 // WsdlExporter.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //      Ankit Jain <JAnkit@novell.com>
7 //      Martin Baulig <martin.baulig@xamarin.com>
8 //
9 // Copyright (C) 2005 Novell, Inc.  http://www.novell.com
10 // Copyright (c) 2012 Xamarin Inc. (http://www.xamarin.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 using System;
32 using System.Collections;
33 using System.Collections.Generic;
34 using System.Linq;
35 using System.ServiceModel;
36 using System.ServiceModel.Channels;
37 using System.Web.Services.Description;
38 using System.Xml;
39 using System.Xml.Schema;
40 using System.Xml.Serialization;
41 using System.Runtime.Serialization;
42
43 using SMBinding = System.ServiceModel.Channels.Binding;
44 using SMMessage = System.ServiceModel.Channels.Message;
45
46 using WSServiceDescription = System.Web.Services.Description.ServiceDescription;
47 using WSBinding = System.Web.Services.Description.Binding;
48 using WSMessage = System.Web.Services.Description.Message;
49 using QName = System.Xml.XmlQualifiedName;
50
51 namespace System.ServiceModel.Description
52 {
53         [MonoTODO]
54         public class WsdlExporter : MetadataExporter
55         {
56                 class ContractExportMap
57                 {
58                         public ContractExportMap (QName qname, ContractDescription contract, List<IWsdlExportExtension> results)
59                         {
60                                 QName = qname;
61                                 Contract = contract;
62                                 Results = results;
63                         }
64
65                         public QName QName { get; private set; }
66                         public ContractDescription Contract { get; private set; }
67                         public List<IWsdlExportExtension> Results { get; private set; }
68                 }
69
70                 class EndpointExportMap
71                 {
72                         public EndpointExportMap (string name, ServiceEndpoint endpoint)
73                         {
74                                 Name = name;
75                                 Endpoint = endpoint;
76                         }
77
78                         public string Name { get; private set; }
79                         public ServiceEndpoint Endpoint { get; private set; }
80                 }
81
82                 MetadataSet metadata;
83                 ServiceDescriptionCollection wsdl_colln;
84                 XsdDataContractExporter xsd_exporter;
85                 Dictionary<ContractDescription, ContractExportMap> exported_contracts;
86                 List<EndpointExportMap> exported_endpoints;
87
88                 public override MetadataSet GetGeneratedMetadata ()
89                 {
90                         if (metadata != null)
91                                 return metadata;
92
93                         metadata = new MetadataSet ();
94                         foreach (WSServiceDescription sd in GeneratedWsdlDocuments)
95                                 metadata.MetadataSections.Add (
96                                         MetadataSection.CreateFromServiceDescription (sd));
97
98                         foreach (XmlSchema xs in GeneratedXmlSchemas.Schemas ())
99                                 if (xs.TargetNamespace != XmlSchema.Namespace)
100                                         metadata.MetadataSections.Add (
101                                                 MetadataSection.CreateFromSchema (xs));
102                                 
103                         return metadata;
104                 }
105
106                 public override void ExportContract (ContractDescription contract)
107                 {
108                         ExportContractInternal (contract);
109                 }
110
111                 ContractExportMap ExportContractInternal (ContractDescription contract)
112                 {
113                         if (ExportedContracts.ContainsKey (contract))
114                                 return ExportedContracts [contract];
115
116                         QName qname = new QName (contract.Name, contract.Namespace);
117                         if (ExportedContracts.Any (m => m.Value.QName == qname))
118                                 throw new ArgumentException (String.Format (
119                                         "A ContractDescription with Namespace : {0} and Name : {1} has already been exported.",
120                                         contract.Namespace, contract.Name));
121
122                         WSServiceDescription sd = GetServiceDescription (contract.Namespace);
123
124                         List<IWsdlExportExtension> extensions = new List<IWsdlExportExtension> ();
125                         foreach (IWsdlExportExtension extn in contract.Behaviors.FindAll<IWsdlExportExtension> ())
126                                 extensions.Add (extn);
127
128                         XmlDocument xdoc = new XmlDocument ();
129
130                         PortType ws_port = new PortType ();
131                         ws_port.Name = contract.Name;
132
133                         foreach (OperationDescription sm_op in contract.Operations) {
134                                 Operation ws_op = new Operation ();
135                                 ws_op.Name = sm_op.Name;
136
137                                 foreach (MessageDescription sm_md in sm_op.Messages) {
138                                         //OperationMessage
139                                         OperationMessage ws_opmsg;
140                                         WSMessage ws_msg = new WSMessage ();
141                                         MessagePart ws_msgpart;
142                                         if (sm_md.Direction == MessageDirection.Input) {
143                                                 ws_opmsg = new OperationInput ();
144                                                 ws_msg.Name = String.Concat (ws_port.Name, "_", ws_op.Name, "_", "InputMessage");
145                                                 ws_msgpart = ExportMessageBodyDescription (sm_md.Body, ws_op.Name, sd.TargetNamespace);
146                                         } else {
147                                                 ws_opmsg = new OperationOutput ();
148                                                 ws_msg.Name = String.Concat (ws_port.Name, "_", ws_op.Name, "_", "OutputMessage");
149                                                 ws_msgpart = ExportMessageBodyDescription (sm_md.Body, ws_op.Name + "Response", sd.TargetNamespace);
150                                         }
151                                         ws_msg.Parts.Add (ws_msgpart);  
152
153                                         /* FIXME: Faults */
154
155                                         //Action
156                                         XmlAttribute attr = xdoc.CreateAttribute ("wsaw", "Action", "http://www.w3.org/2006/05/addressing/wsdl");
157                                         attr.Value = sm_md.Action;
158                                         ws_opmsg.ExtensibleAttributes = new XmlAttribute [] { attr };
159                                         
160                                         //FIXME: Set .Input & .Output
161
162                                         ws_opmsg.Message = new QName (ws_msg.Name, sd.TargetNamespace);
163                                         ws_op.Messages.Add (ws_opmsg);
164                                         sd.Messages.Add (ws_msg);
165                                 }
166
167                                 ws_port.Operations.Add (ws_op);
168
169                                 foreach (IWsdlExportExtension extn in sm_op.Behaviors.FindAll<IWsdlExportExtension> ())
170                                         extensions.Add (extn);
171                         }
172
173                         //Add Imports for <types
174                         XmlSchema xs_import = new XmlSchema ();
175                         xs_import.TargetNamespace = String.Concat (
176                                         contract.Namespace, 
177                                         contract.Namespace.EndsWith ("/") ? "" : "/",
178                                         "Imports");
179                         foreach (XmlSchema schema in GeneratedXmlSchemas.Schemas ()) {
180                                 XmlSchemaImport imp = new XmlSchemaImport ();
181                                 imp.Namespace = schema.TargetNamespace;
182                                 xs_import.Includes.Add (imp);
183                         }
184                         sd.Types.Schemas.Add (xs_import);
185
186                         sd.PortTypes.Add (ws_port);
187                         var map = new ContractExportMap (qname, contract, extensions);
188                         ExportedContracts.Add (contract, map);
189
190                         WsdlContractConversionContext context = new WsdlContractConversionContext (contract, ws_port);
191                         foreach (IWsdlExportExtension extn in extensions)
192                                 extn.ExportContract (this, context);
193
194                         return map;
195                 }
196
197                 public override void ExportEndpoint (ServiceEndpoint endpoint)
198                 {
199                         ExportEndpoint_Internal (endpoint);
200                 }
201
202                 EndpointExportMap ExportEndpoint_Internal (ServiceEndpoint endpoint)
203                 {
204                         var map = ExportedEndpoints.FirstOrDefault (m => m.Endpoint == endpoint);
205                         if (map != null)
206                                 return map;
207
208                         int index = 0;
209                         var baseName = String.Concat (endpoint.Binding.Name, "_", endpoint.Contract.Name);
210                         var name = baseName;
211                         while (ExportedEndpoints.Exists (m => m.Name == name))
212                                 name = String.Concat (baseName, (++index).ToString ());
213
214                         map = new EndpointExportMap (name, endpoint);
215                         ExportedEndpoints.Add (map);
216
217                         var contract = ExportContractInternal (endpoint.Contract);
218
219                         //FIXME: Namespace
220                         WSServiceDescription sd = GetServiceDescription ("http://tempuri.org/");
221                         if (sd.TargetNamespace != endpoint.Contract.Namespace) {
222                                 sd.Namespaces.Add ("i0", endpoint.Contract.Namespace);
223
224                                 //Import
225                                 Import import = new Import ();
226                                 import.Namespace = endpoint.Contract.Namespace;
227
228                                 sd.Imports.Add (import);
229                         }
230                         
231                         if (endpoint.Binding == null)
232                                 throw new ArgumentException (String.Format (
233                                         "Binding for ServiceEndpoint named '{0}' is null",
234                                         endpoint.Name));
235
236                         var extensions = new List<IWsdlExportExtension> ();
237                         var extensionTypes = new Dictionary<Type, IWsdlExportExtension> ();
238                         if (contract.Results != null) {
239                                 foreach (var extension in contract.Results) {
240                                         var type = extension.GetType ();
241                                         if (extensionTypes.ContainsKey (type))
242                                                 continue;
243                                         extensionTypes.Add (type, extension);
244                                         extensions.Add (extension);
245                                 }
246                         }
247
248                         var bindingElements = endpoint.Binding.CreateBindingElements ();
249                         foreach (var element in bindingElements) {
250                                 var extension = element as IWsdlExportExtension;
251                                 if (extension == null)
252                                         continue;
253                                 var type = extension.GetType ();
254                                 if (extensionTypes.ContainsKey (type))
255                                         continue;
256                                 extensionTypes.Add (type, extension);
257                                 extensions.Add (extension);
258                         }
259
260                         //ExportBinding
261                         WSBinding ws_binding = new WSBinding ();
262                         
263                         //<binding name = .. 
264                         ws_binding.Name = name;
265
266                         //<binding type = ..
267                         ws_binding.Type = new QName (endpoint.Contract.Name, endpoint.Contract.Namespace);
268                         sd.Bindings.Add (ws_binding);
269
270                         //      <operation
271                         foreach (OperationDescription sm_op in endpoint.Contract.Operations) {
272                                 var op_binding = CreateOperationBinding (endpoint, sm_op);
273                                 ws_binding.Operations.Add (op_binding);
274                         }
275
276                         //Add <service
277                         Port ws_port = ExportService (sd, ws_binding, endpoint.Address);
278
279                         //Call IWsdlExportExtension.ExportEndpoint
280                         WsdlContractConversionContext contract_context = new WsdlContractConversionContext (
281                                 endpoint.Contract, sd.PortTypes [endpoint.Contract.Name]);
282                         WsdlEndpointConversionContext endpoint_context = new WsdlEndpointConversionContext (
283                                 contract_context, endpoint, ws_port, ws_binding);
284
285                         foreach (var extension in extensions) {
286                                 try {
287                                         extension.ExportEndpoint (this, endpoint_context);
288                                 } catch (Exception ex) {
289                                         var error = AddError (
290                                                 "Failed to export endpoint '{0}': wsdl exporter '{1}' " +
291                                                 "threw an exception: {2}", endpoint.Name, extension.GetType (), ex);
292                                         throw new MetadataExportException (error, ex);
293                                 }
294                         }
295
296                         try {
297                                 ExportPolicy (endpoint, ws_binding);
298                         } catch (MetadataExportException) {
299                                 throw;
300                         } catch (Exception ex) {
301                                 var error = AddError (
302                                         "Failed to export endpoint '{0}': unhandled exception " +
303                                         "while exporting policy: {1}", endpoint.Name, ex);
304                                 throw new MetadataExportException (error, ex);
305                         }
306
307                         return map;
308                 }
309
310                 OperationBinding CreateOperationBinding (ServiceEndpoint endpoint, OperationDescription sm_op)
311                 {
312                         OperationBinding op_binding = new OperationBinding ();
313                         op_binding.Name = sm_op.Name;
314                         
315                         foreach (MessageDescription sm_md in sm_op.Messages) {
316                                 if (sm_md.Direction == MessageDirection.Input) {
317                                         //<input
318                                         CreateInputBinding (endpoint, op_binding, sm_md);
319                                 } else {
320                                         //<output
321                                         CreateOutputBinding (endpoint, op_binding, sm_md);
322                                 }
323                         }
324
325                         return op_binding;
326                 }
327
328                 void CreateInputBinding (ServiceEndpoint endpoint, OperationBinding op_binding,
329                                          MessageDescription sm_md)
330                 {
331                         var in_binding = new InputBinding ();
332                         op_binding.Input = in_binding;
333
334                         var message_version = endpoint.Binding.MessageVersion ?? MessageVersion.None;
335                         if (message_version == MessageVersion.None)
336                                 return;
337
338                         SoapBodyBinding soap_body_binding;
339                         SoapOperationBinding soap_operation_binding;
340                         if (message_version.Envelope == EnvelopeVersion.Soap11) {
341                                 soap_body_binding = new SoapBodyBinding ();
342                                 soap_operation_binding = new SoapOperationBinding ();
343                         } else if (message_version.Envelope == EnvelopeVersion.Soap12) {
344                                 soap_body_binding = new Soap12BodyBinding ();
345                                 soap_operation_binding = new Soap12OperationBinding ();
346                         } else {
347                                 throw new InvalidOperationException ();
348                         }
349
350                         soap_body_binding.Use = SoapBindingUse.Literal;
351                         in_binding.Extensions.Add (soap_body_binding);
352                                 
353                         //Set Action
354                         //<operation > <soap:operation soapAction .. >
355                         soap_operation_binding.SoapAction = sm_md.Action;
356                         soap_operation_binding.Style = SoapBindingStyle.Document;
357                         op_binding.Extensions.Add (soap_operation_binding);
358                 }
359
360                 void CreateOutputBinding (ServiceEndpoint endpoint, OperationBinding op_binding,
361                                           MessageDescription sm_md)
362                 {
363                         var out_binding = new OutputBinding ();
364                         op_binding.Output = out_binding;
365
366                         var message_version = endpoint.Binding.MessageVersion ?? MessageVersion.None;
367                         if (message_version == MessageVersion.None)
368                                 return;
369
370                         SoapBodyBinding soap_body_binding;
371                         if (message_version.Envelope == EnvelopeVersion.Soap11) {
372                                 soap_body_binding = new SoapBodyBinding ();
373                         } else if (message_version.Envelope == EnvelopeVersion.Soap12) {
374                                 soap_body_binding = new Soap12BodyBinding ();
375                         } else {
376                                 throw new InvalidOperationException ();
377                         }
378
379                         soap_body_binding.Use = SoapBindingUse.Literal;
380                         out_binding.Extensions.Add (soap_body_binding);
381                 }
382                 
383                 Port ExportService (WSServiceDescription sd, WSBinding ws_binding, EndpointAddress address)
384                 {
385                         if (address == null)
386                                 return null;
387
388                         Service ws_svc = GetService (sd, "service");
389                         sd.Name = "service";
390
391                         Port ws_port = new Port ();
392                         ws_port.Name = ws_binding.Name;
393                         ws_port.Binding = new QName (ws_binding.Name, sd.TargetNamespace);
394
395                         ws_svc.Ports.Add (ws_port);
396
397                         return ws_port;
398                 }
399
400                 Service GetService (WSServiceDescription sd, string name)
401                 {
402                         Service svc = sd.Services [name];
403                         if (svc != null)
404                                 return svc;
405
406                         svc = new Service ();
407                         svc.Name = name;
408                         sd.Services.Add (svc);
409
410                         return svc;
411                 }
412
413                 WSServiceDescription GetServiceDescription (string ns)
414                 {
415                         foreach (WSServiceDescription sd in GeneratedWsdlDocuments) {
416                                 if (sd.TargetNamespace == ns)
417                                         return sd;
418                         }
419
420                         WSServiceDescription ret = new WSServiceDescription ();
421                         ret.TargetNamespace = ns;
422                         ret.Namespaces = GetNamespaces (ns);
423                         GeneratedWsdlDocuments.Add (ret);
424
425                         metadata = null;
426
427                         return ret;
428                 }
429
430                 public ServiceDescriptionCollection GeneratedWsdlDocuments {
431                         get {
432                                 if (wsdl_colln == null)
433                                         wsdl_colln = new ServiceDescriptionCollection ();
434                                 return wsdl_colln;
435                         }
436                 }
437
438                 public XmlSchemaSet GeneratedXmlSchemas {
439                         get { return XsdExporter.Schemas; }
440                 }
441
442                 public void ExportEndpoints (
443                         IEnumerable<ServiceEndpoint> endpoints,
444                         XmlQualifiedName wsdlServiceQName)
445                 {
446                         if (endpoints == null)
447                                 throw new ArgumentNullException ("endpoints");
448                         if (wsdlServiceQName == null)
449                                 throw new ArgumentNullException ("wsdlServiceQName");
450
451                         foreach (ServiceEndpoint ep in endpoints) {
452                                 if (ep.Contract.Name == ServiceMetadataBehavior.MexContractName)
453                                         continue;
454
455                                 ExportEndpoint (ep);
456                         }
457                 }
458
459                 XsdDataContractExporter XsdExporter {
460                         get {
461                                 if (xsd_exporter == null)
462                                         xsd_exporter = new XsdDataContractExporter ();
463                                 return xsd_exporter;
464                         }
465                 }
466
467                 Dictionary<ContractDescription, ContractExportMap> ExportedContracts {
468                         get {
469                                 if (exported_contracts == null)
470                                         exported_contracts = new Dictionary<ContractDescription, ContractExportMap> ();
471                                 return exported_contracts;
472                         }
473                 }
474                 
475                 List<EndpointExportMap> ExportedEndpoints {
476                         get {
477                                 if (exported_endpoints == null)
478                                         exported_endpoints = new List<EndpointExportMap> ();
479                                 return exported_endpoints;
480                         }
481                 }
482                 
483                 XmlSerializerNamespaces GetNamespaces (string target_namespace)
484                 {
485                         XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces ();
486
487                         namespaces.Add ("soap", "http://schemas.xmlsoap.org/wsdl/soap/");
488                         namespaces.Add ("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
489                         namespaces.Add ("soapenc", "http://schemas.xmlsoap.org/soap/encoding/");
490                         namespaces.Add ("tns", target_namespace);
491                         namespaces.Add ("wsa", "http://schemas.xmlsoap.org/ws/2004/08/addressing");
492                         namespaces.Add ("wsp", "http://schemas.xmlsoap.org/ws/2004/09/policy");
493                         namespaces.Add ("wsap", "http://schemas.xmlsoap.org/ws/2004/08/addressing/policy");
494                         namespaces.Add ("msc", "http://schemas.microsoft.com/ws/2005/12/wsdl/contract");
495                         namespaces.Add ("wsaw", "http://www.w3.org/2006/05/addressing/wsdl");
496                         namespaces.Add ("soap12", "http://schemas.xmlsoap.org/wsdl/soap12/");
497                         namespaces.Add ("wsa10", "http://www.w3.org/2005/08/addressing");
498                         namespaces.Add ("wsdl", "http://schemas.xmlsoap.org/wsdl/");
499
500                         return namespaces;
501                 }
502
503                 MessagePart ExportMessageBodyDescription (MessageBodyDescription msgbody, string name, string ns)
504                 {
505                         MessagePart msgpart = new MessagePart ();
506                         string part_name = IsTypeMessage (msgbody);
507
508                         if (part_name != null) {
509                                 msgpart.Name = part_name;
510                                 msgpart.Type = ExportTypeMessage (); //FIXME: Cache this
511                         } else {
512                                 msgpart.Name = "parameters";
513                                 msgpart.Element = ExportParameters (msgbody, name, ns);
514                         }
515                         return msgpart;
516                 }
517
518                 /* Sets the @name if the param or return type is SMMessage */
519                 string IsTypeMessage (MessageBodyDescription msgbody)
520                 {
521                         MessagePartDescription part = null;
522
523                         if (msgbody.Parts.Count == 0)
524                                 part = msgbody.ReturnValue;
525                         else if (msgbody.Parts.Count == 1)
526                                 part = msgbody.Parts [0];
527
528                         if (part != null && (part.Type.FullName == typeof (SMMessage).FullName))
529                                 return part.Name;
530
531                         return null;
532                 }
533
534                 QName ExportParameters (MessageBodyDescription msgbody, string name, string ns)
535                 {
536                         XmlSchema xs = GetSchema (ns);
537                         //FIXME: Extract to a HasElement method ?
538                         foreach (XmlSchemaObject o in xs.Items) {
539                                 XmlSchemaElement e = o as XmlSchemaElement;
540                                 if (e == null)
541                                         continue;
542
543                                 if (e.Name == name)
544                                         throw new InvalidOperationException (String.Format (
545                                                 "Message element named '{0}:{1}' has already been exported.",
546                                                 ns, name));
547                         }
548                                 
549                         //Create the element for "parameters"
550                         XmlSchemaElement schema_element = new XmlSchemaElement ();
551                         schema_element.Name = name;
552
553                         XmlSchemaComplexType complex_type = new XmlSchemaComplexType ();
554                         //Generate Sequence representing the message/parameters
555                         //FIXME: MessageContractAttribute
556                 
557                         XmlSchemaSequence sequence = new XmlSchemaSequence ();
558                         XmlSchemaElement element = null;
559
560                         if (msgbody.ReturnValue == null) {
561                                 //parameters
562                                 foreach (MessagePartDescription part in msgbody.Parts) {
563                                         if (part.Type == null)
564                                                 //FIXME: Eg. when WsdlImporter is used to import a wsdl
565                                                 throw new NotImplementedException ();
566                                         
567                                         element = GetSchemaElementForPart (part, xs);
568                                         sequence.Items.Add (element);
569                                 }
570                         } else {
571                                 //ReturnValue
572                                 if (msgbody.ReturnValue.Type != typeof (void)) {
573                                         element = GetSchemaElementForPart (msgbody.ReturnValue, xs);
574                                         sequence.Items.Add (element);
575                                 }
576                         }
577
578                         complex_type.Particle = sequence;
579                         schema_element.SchemaType = complex_type;
580
581                         xs.Items.Add (schema_element);
582                         GeneratedXmlSchemas.Reprocess (xs);
583
584                         return new QName (schema_element.Name, xs.TargetNamespace);
585                 }
586
587                 //Exports <xs:type for SMMessage
588                 //FIXME: complex type for this can be made static
589                 QName ExportTypeMessage ()
590                 {
591                         XmlSchema xs = GetSchema ("http://schemas.microsoft.com/Message");
592                         QName qname = new QName ("MessageBody", xs.TargetNamespace);
593
594                         foreach (XmlSchemaObject o in xs.Items) {
595                                 XmlSchemaComplexType ct = o as XmlSchemaComplexType;
596                                 if (ct == null)
597                                         continue;
598
599                                 if (ct.Name == "MessageBody")
600                                         //Already exported
601                                         return qname;
602                         }
603
604                         XmlSchemaComplexType complex_type = new XmlSchemaComplexType ();
605                         complex_type.Name = "MessageBody";
606                         XmlSchemaSequence sequence = new XmlSchemaSequence ();
607
608                         XmlSchemaAny any = new XmlSchemaAny ();
609                         any.MinOccurs = 0;
610                         any.MaxOccursString = "unbounded";
611                         any.Namespace = "##any";
612
613                         sequence.Items.Add (any);
614                         complex_type.Particle = sequence;
615
616                         xs.Items.Add (complex_type);
617                         GeneratedXmlSchemas.Reprocess (xs);
618                         
619                         return qname;
620                 }
621
622                 XmlSchemaElement GetSchemaElementForPart (MessagePartDescription part, XmlSchema schema)
623                 {
624                         XmlSchemaElement element = new XmlSchemaElement ();
625
626                         element.Name = part.Name;
627                         XsdExporter.Export (part.Type);
628                         element.SchemaTypeName = XsdExporter.GetSchemaTypeName (part.Type);
629                         AddImport (schema, element.SchemaTypeName.Namespace);
630
631                         //FIXME: nillable, minOccurs
632                         if (XsdExporter.GetSchemaType (part.Type) is XmlSchemaComplexType ||
633                                 part.Type == typeof (string))
634                                 element.IsNillable = true;
635                         element.MinOccurs = 0;
636
637                         return element;
638                 }
639
640                 //FIXME: Replace with a dictionary ?
641                 void AddImport (XmlSchema schema, string ns)
642                 {
643                         if (ns == XmlSchema.Namespace || schema.TargetNamespace == ns)
644                                 return;
645
646                         foreach (XmlSchemaObject o in schema.Includes) {
647                                 XmlSchemaImport import = o as XmlSchemaImport;
648                                 if (import == null)
649                                         continue;
650                                 if (import.Namespace == ns)
651                                         return;
652                         }
653
654                         if (ns == string.Empty)
655                                 return;
656
657                         XmlSchemaImport imp = new XmlSchemaImport ();
658                         imp.Namespace = ns;
659                         schema.Includes.Add (imp);
660                 }
661
662                 XmlSchema GetSchema (string ns)
663                 {
664                         ICollection colln = GeneratedXmlSchemas.Schemas (ns);
665                         if (colln.Count > 0) { 
666                                 if (colln.Count > 1)
667                                         throw new Exception ("More than 1 schema found for ns = " + ns);
668                                 //FIXME: HORRIBLE!
669                                 foreach (object o in colln)
670                                         return (o as XmlSchema);
671                         }
672
673                         XmlSchema schema = new XmlSchema ();
674                         schema.TargetNamespace = ns;
675                         schema.ElementFormDefault = XmlSchemaForm.Qualified;
676                         GeneratedXmlSchemas.Add (schema);
677
678                         return schema;
679                 }
680
681                 PolicyConversionContext ExportPolicy (ServiceEndpoint endpoint, WSBinding binding)
682                 {
683                         var context = new CustomPolicyConversionContext (endpoint);
684
685                         var elements = endpoint.Binding.CreateBindingElements ();
686                         foreach (var element in elements) {
687                                 var exporter = element as IPolicyExportExtension;
688                                 if (exporter == null)
689                                         continue;
690
691                                 try {
692                                         exporter.ExportPolicy (this, context);
693                                 } catch (Exception ex) {
694                                         var error = AddError (
695                                                 "Failed to export endpoint '{0}': policy exporter " +
696                                                 "'{1}' threw an exception: {2}", endpoint.Name,
697                                                 element.GetType (), ex);
698                                         throw new MetadataExportException (error, ex);
699                                 }
700                         }
701
702                         var assertions = context.GetBindingAssertions ();
703                         if (assertions.Count == 0)
704                                 return context;
705
706                         var doc = new XmlDocument ();
707                         var policy = doc.CreateElement ("wsp", "Policy", PolicyImportHelper.PolicyNS);
708                         doc.AppendChild (policy);
709
710                         var exactlyOne = doc.CreateElement ("wsp", "ExactlyOne", PolicyImportHelper.PolicyNS);
711                         var all = doc.CreateElement ("wsp", "All", PolicyImportHelper.PolicyNS);
712
713                         policy.AppendChild (exactlyOne);
714                         exactlyOne.AppendChild (all);
715
716                         foreach (var assertion in assertions) {
717                                 var imported = doc.ImportNode (assertion, true);
718                                 all.AppendChild (imported);
719                         }
720
721                         binding.Extensions.Add (policy);
722                         return context;
723                 }
724
725         }
726 }