Merge pull request #495 from nicolas-raoul/fix-for-issue2907-with-no-formatting-changes
[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                                 metadata.MetadataSections.Add (
100                                         MetadataSection.CreateFromSchema (xs));
101                                 
102                         return metadata;
103                 }
104
105                 public override void ExportContract (ContractDescription contract)
106                 {
107                         ExportContractInternal (contract);
108                 }
109
110                 ContractExportMap ExportContractInternal (ContractDescription contract)
111                 {
112                         if (ExportedContracts.ContainsKey (contract))
113                                 return ExportedContracts [contract];
114
115                         QName qname = new QName (contract.Name, contract.Namespace);
116                         if (ExportedContracts.Any (m => m.Value.QName == qname))
117                                 throw new ArgumentException (String.Format (
118                                         "A ContractDescription with Namespace : {0} and Name : {1} has already been exported.",
119                                         contract.Namespace, contract.Name));
120
121                         WSServiceDescription sd = GetServiceDescription (contract.Namespace);
122
123                         List<IWsdlExportExtension> extensions = new List<IWsdlExportExtension> ();
124                         foreach (IWsdlExportExtension extn in contract.Behaviors.FindAll<IWsdlExportExtension> ())
125                                 extensions.Add (extn);
126
127                         XmlDocument xdoc = new XmlDocument ();
128
129                         PortType ws_port = new PortType ();
130                         ws_port.Name = contract.Name;
131
132                         foreach (OperationDescription sm_op in contract.Operations) {
133                                 Operation ws_op = new Operation ();
134                                 ws_op.Name = sm_op.Name;
135
136                                 foreach (MessageDescription sm_md in sm_op.Messages) {
137                                         //OperationMessage
138                                         OperationMessage ws_opmsg;
139                                         WSMessage ws_msg = new WSMessage ();
140                                         MessagePart ws_msgpart;
141                                         if (sm_md.Direction == MessageDirection.Input) {
142                                                 ws_opmsg = new OperationInput ();
143                                                 ws_msg.Name = String.Concat (ws_port.Name, "_", ws_op.Name, "_", "InputMessage");
144                                                 ws_msgpart = ExportMessageBodyDescription (sm_md.Body, ws_op.Name, sd.TargetNamespace);
145                                         } else {
146                                                 ws_opmsg = new OperationOutput ();
147                                                 ws_msg.Name = String.Concat (ws_port.Name, "_", ws_op.Name, "_", "OutputMessage");
148                                                 ws_msgpart = ExportMessageBodyDescription (sm_md.Body, ws_op.Name + "Response", sd.TargetNamespace);
149                                         }
150                                         ws_msg.Parts.Add (ws_msgpart);  
151
152                                         /* FIXME: Faults */
153
154                                         //Action
155                                         XmlAttribute attr = xdoc.CreateAttribute ("wsaw", "Action", "http://www.w3.org/2006/05/addressing/wsdl");
156                                         attr.Value = sm_md.Action;
157                                         ws_opmsg.ExtensibleAttributes = new XmlAttribute [] { attr };
158                                         
159                                         //FIXME: Set .Input & .Output
160
161                                         ws_opmsg.Message = new QName (ws_msg.Name, sd.TargetNamespace);
162                                         ws_op.Messages.Add (ws_opmsg);
163                                         sd.Messages.Add (ws_msg);
164                                 }
165
166                                 ws_port.Operations.Add (ws_op);
167
168                                 foreach (IWsdlExportExtension extn in sm_op.Behaviors.FindAll<IWsdlExportExtension> ())
169                                         extensions.Add (extn);
170                         }
171
172                         //Add Imports for <types
173                         XmlSchema xs_import = new XmlSchema ();
174                         xs_import.TargetNamespace = String.Concat (
175                                         contract.Namespace, 
176                                         contract.Namespace.EndsWith ("/") ? "" : "/",
177                                         "Imports");
178                         foreach (XmlSchema schema in GeneratedXmlSchemas.Schemas ()) {
179                                 XmlSchemaImport imp = new XmlSchemaImport ();
180                                 imp.Namespace = schema.TargetNamespace;
181                                 xs_import.Includes.Add (imp);
182                         }
183                         sd.Types.Schemas.Add (xs_import);
184
185                         sd.PortTypes.Add (ws_port);
186                         var map = new ContractExportMap (qname, contract, extensions);
187                         ExportedContracts.Add (contract, map);
188
189                         WsdlContractConversionContext context = new WsdlContractConversionContext (contract, ws_port);
190                         foreach (IWsdlExportExtension extn in extensions)
191                                 extn.ExportContract (this, context);
192
193                         return map;
194                 }
195
196                 public override void ExportEndpoint (ServiceEndpoint endpoint)
197                 {
198                         ExportEndpoint_Internal (endpoint);
199                 }
200
201                 EndpointExportMap ExportEndpoint_Internal (ServiceEndpoint endpoint)
202                 {
203                         var map = ExportedEndpoints.FirstOrDefault (m => m.Endpoint == endpoint);
204                         if (map != null)
205                                 return map;
206
207                         int index = 0;
208                         var baseName = String.Concat (endpoint.Binding.Name, "_", endpoint.Contract.Name);
209                         var name = baseName;
210                         while (ExportedEndpoints.Exists (m => m.Name == name))
211                                 name = String.Concat (baseName, (++index).ToString ());
212
213                         map = new EndpointExportMap (name, endpoint);
214                         ExportedEndpoints.Add (map);
215
216                         var contract = ExportContractInternal (endpoint.Contract);
217
218                         //FIXME: Namespace
219                         WSServiceDescription sd = GetServiceDescription ("http://tempuri.org/");
220                         if (sd.TargetNamespace != endpoint.Contract.Namespace) {
221                                 sd.Namespaces.Add ("i0", endpoint.Contract.Namespace);
222
223                                 //Import
224                                 Import import = new Import ();
225                                 import.Namespace = endpoint.Contract.Namespace;
226
227                                 sd.Imports.Add (import);
228                         }
229                         
230                         if (endpoint.Binding == null)
231                                 throw new ArgumentException (String.Format (
232                                         "Binding for ServiceEndpoint named '{0}' is null",
233                                         endpoint.Name));
234
235                         var extensions = new List<IWsdlExportExtension> ();
236                         var extensionTypes = new Dictionary<Type, IWsdlExportExtension> ();
237                         if (contract.Results != null) {
238                                 foreach (var extension in contract.Results) {
239                                         var type = extension.GetType ();
240                                         if (extensionTypes.ContainsKey (type))
241                                                 continue;
242                                         extensionTypes.Add (type, extension);
243                                         extensions.Add (extension);
244                                 }
245                         }
246
247                         var bindingElements = endpoint.Binding.CreateBindingElements ();
248                         foreach (var element in bindingElements) {
249                                 var extension = element as IWsdlExportExtension;
250                                 if (extension == null)
251                                         continue;
252                                 var type = extension.GetType ();
253                                 if (extensionTypes.ContainsKey (type))
254                                         continue;
255                                 extensionTypes.Add (type, extension);
256                                 extensions.Add (extension);
257                         }
258
259                         //ExportBinding
260                         WSBinding ws_binding = new WSBinding ();
261                         
262                         //<binding name = .. 
263                         ws_binding.Name = name;
264
265                         //<binding type = ..
266                         ws_binding.Type = new QName (endpoint.Contract.Name, endpoint.Contract.Namespace);
267                         sd.Bindings.Add (ws_binding);
268
269                         //      <operation
270                         foreach (OperationDescription sm_op in endpoint.Contract.Operations) {
271                                 var op_binding = CreateOperationBinding (endpoint, sm_op);
272                                 ws_binding.Operations.Add (op_binding);
273                         }
274
275                         //Add <service
276                         Port ws_port = ExportService (sd, ws_binding, endpoint.Address);
277
278                         //Call IWsdlExportExtension.ExportEndpoint
279                         WsdlContractConversionContext contract_context = new WsdlContractConversionContext (
280                                 endpoint.Contract, sd.PortTypes [endpoint.Contract.Name]);
281                         WsdlEndpointConversionContext endpoint_context = new WsdlEndpointConversionContext (
282                                 contract_context, endpoint, ws_port, ws_binding);
283
284                         foreach (var extension in extensions) {
285                                 try {
286                                         extension.ExportEndpoint (this, endpoint_context);
287                                 } catch (Exception ex) {
288                                         var error = AddError (
289                                                 "Failed to export endpoint '{0}': wsdl exporter '{1}' " +
290                                                 "threw an exception: {2}", endpoint.Name, extension.GetType (), ex);
291                                         throw new MetadataExportException (error, ex);
292                                 }
293                         }
294
295                         try {
296                                 ExportPolicy (endpoint, ws_binding);
297                         } catch (MetadataExportException) {
298                                 throw;
299                         } catch (Exception ex) {
300                                 var error = AddError (
301                                         "Failed to export endpoint '{0}': unhandled exception " +
302                                         "while exporting policy: {1}", endpoint.Name, ex);
303                                 throw new MetadataExportException (error, ex);
304                         }
305
306                         return map;
307                 }
308
309                 OperationBinding CreateOperationBinding (ServiceEndpoint endpoint, OperationDescription sm_op)
310                 {
311                         OperationBinding op_binding = new OperationBinding ();
312                         op_binding.Name = sm_op.Name;
313                         
314                         foreach (MessageDescription sm_md in sm_op.Messages) {
315                                 if (sm_md.Direction == MessageDirection.Input) {
316                                         //<input
317                                         CreateInputBinding (endpoint, op_binding, sm_md);
318                                 } else {
319                                         //<output
320                                         CreateOutputBinding (endpoint, op_binding, sm_md);
321                                 }
322                         }
323
324                         return op_binding;
325                 }
326
327                 void CreateInputBinding (ServiceEndpoint endpoint, OperationBinding op_binding,
328                                          MessageDescription sm_md)
329                 {
330                         var in_binding = new InputBinding ();
331                         op_binding.Input = in_binding;
332
333                         var message_version = endpoint.Binding.MessageVersion ?? MessageVersion.None;
334                         if (message_version == MessageVersion.None)
335                                 return;
336
337                         SoapBodyBinding soap_body_binding;
338                         SoapOperationBinding soap_operation_binding;
339                         if (message_version.Envelope == EnvelopeVersion.Soap11) {
340                                 soap_body_binding = new SoapBodyBinding ();
341                                 soap_operation_binding = new SoapOperationBinding ();
342                         } else if (message_version.Envelope == EnvelopeVersion.Soap12) {
343                                 soap_body_binding = new Soap12BodyBinding ();
344                                 soap_operation_binding = new Soap12OperationBinding ();
345                         } else {
346                                 throw new InvalidOperationException ();
347                         }
348
349                         soap_body_binding.Use = SoapBindingUse.Literal;
350                         in_binding.Extensions.Add (soap_body_binding);
351                                 
352                         //Set Action
353                         //<operation > <soap:operation soapAction .. >
354                         soap_operation_binding.SoapAction = sm_md.Action;
355                         soap_operation_binding.Style = SoapBindingStyle.Document;
356                         op_binding.Extensions.Add (soap_operation_binding);
357                 }
358
359                 void CreateOutputBinding (ServiceEndpoint endpoint, OperationBinding op_binding,
360                                           MessageDescription sm_md)
361                 {
362                         var out_binding = new OutputBinding ();
363                         op_binding.Output = out_binding;
364
365                         var message_version = endpoint.Binding.MessageVersion ?? MessageVersion.None;
366                         if (message_version == MessageVersion.None)
367                                 return;
368
369                         SoapBodyBinding soap_body_binding;
370                         if (message_version.Envelope == EnvelopeVersion.Soap11) {
371                                 soap_body_binding = new SoapBodyBinding ();
372                         } else if (message_version.Envelope == EnvelopeVersion.Soap12) {
373                                 soap_body_binding = new Soap12BodyBinding ();
374                         } else {
375                                 throw new InvalidOperationException ();
376                         }
377
378                         soap_body_binding.Use = SoapBindingUse.Literal;
379                         out_binding.Extensions.Add (soap_body_binding);
380                 }
381                 
382                 Port ExportService (WSServiceDescription sd, WSBinding ws_binding, EndpointAddress address)
383                 {
384                         if (address == null)
385                                 return null;
386
387                         Service ws_svc = GetService (sd, "service");
388                         sd.Name = "service";
389
390                         Port ws_port = new Port ();
391                         ws_port.Name = ws_binding.Name;
392                         ws_port.Binding = new QName (ws_binding.Name, sd.TargetNamespace);
393
394                         ws_svc.Ports.Add (ws_port);
395
396                         return ws_port;
397                 }
398
399                 Service GetService (WSServiceDescription sd, string name)
400                 {
401                         Service svc = sd.Services [name];
402                         if (svc != null)
403                                 return svc;
404
405                         svc = new Service ();
406                         svc.Name = name;
407                         sd.Services.Add (svc);
408
409                         return svc;
410                 }
411
412                 WSServiceDescription GetServiceDescription (string ns)
413                 {
414                         foreach (WSServiceDescription sd in GeneratedWsdlDocuments) {
415                                 if (sd.TargetNamespace == ns)
416                                         return sd;
417                         }
418
419                         WSServiceDescription ret = new WSServiceDescription ();
420                         ret.TargetNamespace = ns;
421                         ret.Namespaces = GetNamespaces (ns);
422                         GeneratedWsdlDocuments.Add (ret);
423
424                         metadata = null;
425
426                         return ret;
427                 }
428
429                 public ServiceDescriptionCollection GeneratedWsdlDocuments {
430                         get {
431                                 if (wsdl_colln == null)
432                                         wsdl_colln = new ServiceDescriptionCollection ();
433                                 return wsdl_colln;
434                         }
435                 }
436
437                 public XmlSchemaSet GeneratedXmlSchemas {
438                         get { return XsdExporter.Schemas; }
439                 }
440
441                 public void ExportEndpoints (
442                         IEnumerable<ServiceEndpoint> endpoints,
443                         XmlQualifiedName name)
444                 {
445                         if (endpoints == null)
446                                 throw new ArgumentNullException ("endpoints");
447                         if (name == null)
448                                 throw new ArgumentNullException ("name");
449
450                         foreach (ServiceEndpoint ep in endpoints) {
451                                 if (ep.Contract.Name == ServiceMetadataBehavior.MexContractName)
452                                         continue;
453
454                                 ExportEndpoint (ep);
455                         }
456                 }
457
458                 XsdDataContractExporter XsdExporter {
459                         get {
460                                 if (xsd_exporter == null)
461                                         xsd_exporter = new XsdDataContractExporter ();
462
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                         XmlSchemaImport imp = new XmlSchemaImport ();
655                         imp.Namespace = ns;
656                         schema.Includes.Add (imp);
657                 }
658
659                 XmlSchema GetSchema (string ns)
660                 {
661                         ICollection colln = GeneratedXmlSchemas.Schemas (ns);
662                         if (colln.Count > 0) { 
663                                 if (colln.Count > 1)
664                                         throw new Exception ("More than 1 schema found for ns = " + ns);
665                                 //FIXME: HORRIBLE!
666                                 foreach (object o in colln)
667                                         return (o as XmlSchema);
668                         }
669
670                         XmlSchema schema = new XmlSchema ();
671                         schema.TargetNamespace = ns;
672                         schema.ElementFormDefault = XmlSchemaForm.Qualified;
673                         GeneratedXmlSchemas.Add (schema);
674
675                         return schema;
676                 }
677
678                 PolicyConversionContext ExportPolicy (ServiceEndpoint endpoint, WSBinding binding)
679                 {
680                         var context = new CustomPolicyConversionContext (endpoint);
681
682                         var elements = endpoint.Binding.CreateBindingElements ();
683                         foreach (var element in elements) {
684                                 var exporter = element as IPolicyExportExtension;
685                                 if (exporter == null)
686                                         continue;
687
688                                 try {
689                                         exporter.ExportPolicy (this, context);
690                                 } catch (Exception ex) {
691                                         var error = AddError (
692                                                 "Failed to export endpoint '{0}': policy exporter " +
693                                                 "'{1}' threw an exception: {2}", endpoint.Name,
694                                                 element.GetType (), ex);
695                                         throw new MetadataExportException (error, ex);
696                                 }
697                         }
698
699                         var assertions = context.GetBindingAssertions ();
700                         if (assertions.Count == 0)
701                                 return context;
702
703                         var doc = new XmlDocument ();
704                         var policy = doc.CreateElement ("wsp", "Policy", PolicyImportHelper.PolicyNS);
705                         doc.AppendChild (policy);
706
707                         var exactlyOne = doc.CreateElement ("wsp", "ExactlyOne", PolicyImportHelper.PolicyNS);
708                         var all = doc.CreateElement ("wsp", "All", PolicyImportHelper.PolicyNS);
709
710                         policy.AppendChild (exactlyOne);
711                         exactlyOne.AppendChild (all);
712
713                         foreach (var assertion in assertions) {
714                                 var imported = doc.ImportNode (assertion, true);
715                                 all.AppendChild (imported);
716                         }
717
718                         binding.Extensions.Add (policy);
719                         return context;
720                 }
721
722         }
723 }