Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Description / DataContractSerializerMessageContractImporter.cs
1 //
2 // DataContractSerializerMessageContractImporter.cs
3 //
4 // Author: Atsushi Enomoto (atsushi@ximian.com)
5 //         Ankit Jain (jankit@novell.com)
6 //
7 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.CodeDom;
30 using System.Collections.Generic;
31 using System.Collections.ObjectModel;
32 using System.Runtime.Serialization;
33 using System.ServiceModel;
34 using System.ServiceModel.Channels;
35 using System.ServiceModel.Dispatcher;
36 using System.Text;
37 using System.Web.Services.Description;
38 using System.Xml;
39 using System.Xml.Schema;
40 using System.Xml.Serialization;
41
42 using QName = System.Xml.XmlQualifiedName;
43 using WSDL = System.Web.Services.Description.ServiceDescription;
44 using Message = System.Web.Services.Description.Message;
45
46 namespace System.ServiceModel.Description
47 {
48         public class DataContractSerializerMessageContractImporter
49                 : IWsdlImportExtension
50         {
51                 MessageContractImporterInternal impl = new DataContractMessageContractImporterInternal ();
52                 bool enabled = true;
53
54                 public bool Enabled {
55                         get { return enabled; }
56                         set { enabled = value; }
57                 }
58
59                 void IWsdlImportExtension.BeforeImport (
60                         ServiceDescriptionCollection wsdlDocuments,
61                         XmlSchemaSet xmlSchemas,
62                         ICollection<XmlElement> policy)
63                 {
64                         if (!Enabled)
65                                 return;
66
67                         impl.BeforeImport (wsdlDocuments, xmlSchemas, policy);
68                 }
69
70                 void IWsdlImportExtension.ImportContract (WsdlImporter importer,
71                         WsdlContractConversionContext context)
72                 {
73                         if (!Enabled)
74                                 return;
75
76                         impl.ImportContract (importer, context);
77                 }
78
79                 void IWsdlImportExtension.ImportEndpoint (WsdlImporter importer,
80                         WsdlEndpointConversionContext context)
81                 {
82                         if (!Enabled)
83                                 return;
84
85                         impl.ImportEndpoint (importer, context);
86                 }
87         }
88
89         abstract class MessageContractImporterInternal : IWsdlImportExtension
90         {
91                 protected abstract void Init (WsdlImporter importer);
92
93                 public void ImportContract (WsdlImporter importer,
94                         WsdlContractConversionContext context)
95                 {
96                         if (importer == null)
97                                 throw new ArgumentNullException ("importer");
98                         if (context == null)
99                                 throw new ArgumentNullException ("context");
100                         if (this.importer != null || this.context != null)
101                                 throw new SystemException ("INTERNAL ERROR: unexpected recursion of ImportContract method call");
102
103                         Init (importer);
104
105                         schema_set_in_use = new XmlSchemaSet ();
106                         schema_set_in_use.Add (importer.XmlSchemas);
107                         foreach (WSDL wsdl in importer.WsdlDocuments)
108                                 foreach (XmlSchema xs in wsdl.Types.Schemas)
109                                         schema_set_in_use.Add (xs);
110
111                         schema_set_in_use.Compile ();
112
113                         this.importer = importer;
114                         this.context = context;
115                         try {
116                                 DoImportContract ();
117                         } finally {
118                                 this.importer = null;
119                                 this.context = null;
120                         }
121                 }
122
123                 internal WsdlImporter importer;
124                 WsdlContractConversionContext context;
125
126                 internal XmlSchemaSet schema_set_in_use;
127
128                 public void BeforeImport (
129                         ServiceDescriptionCollection wsdlDocuments,
130                         XmlSchemaSet xmlSchemas,
131                         ICollection<XmlElement> policy)
132                 {
133                 }
134
135                 void DoImportContract ()
136                 {
137                         PortType port_type = context.WsdlPortType;
138                         ContractDescription contract = context.Contract;
139                         int i, j;
140                         List<MessagePartDescription> parts = new List<MessagePartDescription> ();
141
142                         i = 0;
143                         foreach (Operation op in port_type.Operations) {
144                                 OperationDescription opdescr = contract.Operations [i];
145                                 if (IsOperationImported (port_type, op))
146                                         continue;
147                                 if (!CanImportOperation (port_type, op))
148                                         continue;
149
150                                 j = 0;
151                                 foreach (OperationMessage opmsg in op.Messages) {
152                                         //SM.MessageDescription
153                                         MessageDescription msgdescr = opdescr.Messages [j];
154
155                                         //OpMsg's corresponding WSMessage
156                                         Message msg = port_type.ServiceDescription.Messages [opmsg.Message.Name];
157
158                                         msgdescr.Body.WrapperNamespace = port_type.ServiceDescription.TargetNamespace;
159
160                                         if (opmsg is OperationOutput) {
161                                                 //ReturnValue
162                                                 msg = port_type.ServiceDescription.Messages [opmsg.Message.Name];
163                                                 
164                                                 resolveMessage (msg, msgdescr.Body, parts);
165                                                 if (parts.Count > 0) {
166                                                         msgdescr.Body.ReturnValue = parts [0];
167                                                         parts.Clear ();
168                                                 }
169                                                 continue;
170                                         }
171
172                                         /* OperationInput */
173                                         
174                                         /* Parts, MessagePartDescription */
175                                         resolveMessage (msg, msgdescr.Body, parts);
176                                         foreach (MessagePartDescription p in parts)
177                                                 msgdescr.Body.Parts.Add (p);
178                                         parts.Clear ();
179
180                                         j ++;
181                                 }
182                                 
183                                 OnOperationImported (opdescr);
184
185
186                                 i ++;
187                         }
188
189                 }
190
191                 bool IsOperationImported (PortType pt, Operation op)
192                 {
193                         foreach (OperationMessage opmsg in op.Messages) {
194
195                                 var opdsc = context.GetMessageDescription (opmsg);
196
197                                 var parts = opdsc.Body.Parts;
198                                 var ret = opdsc.Body.ReturnValue;
199
200                                 if ((ret != null) &&
201                                     (ret.DataContractImporter != null || ret.XmlSerializationImporter != null))
202                                         return true;
203
204                                 foreach (var part in opdsc.Body.Parts)
205                                         if (part.DataContractImporter != null || part.XmlSerializationImporter != null)
206                                                 return true;
207                         }
208                         return false;
209                 }
210
211                 void resolveMessage (Message msg, MessageBodyDescription body, List<MessagePartDescription> parts)
212                 {
213                         foreach (MessagePart part in msg.Parts) {
214                                 if (part.Name == "parameters") {
215                                         if (!part.Element.IsEmpty) {
216                                                 body.WrapperName = part.Element.Name;
217                                                 ImportPartsBySchemaElement (part.Element, parts, msg, part);
218                                         } else {
219                                                 body.WrapperName = part.Type.Name;
220                                                 ResolveType (part.Type, parts, body.WrapperNamespace);
221                                         }
222                                 }
223                                 else
224                                         throw new InvalidOperationException ("Only 'parameters' element in message part is supported"); // this should have been rejected by CanImportOperation().
225                         }
226                 }
227
228                 public void ImportEndpoint (WsdlImporter importer,
229                         WsdlEndpointConversionContext context)
230                 {
231                 }
232
233                 protected abstract void ImportPartsBySchemaElement (QName qname, List<MessagePartDescription> parts, Message msg, MessagePart part);
234
235                 protected abstract void ResolveType (QName qname, List<MessagePartDescription> parts, string ns);
236
237                 protected abstract bool CanImportOperation (PortType portType, Operation op);
238                 
239                 protected abstract void OnOperationImported (OperationDescription od);
240         }
241
242         class DataContractMessageContractImporterInternal : MessageContractImporterInternal
243         {
244                 XsdDataContractImporter dc_importer;
245                 
246                 protected override void Init (WsdlImporter importer)
247                 {
248                         if (dc_importer == null)
249                                 dc_importer = importer.GetState<XsdDataContractImporter> ();
250                 }
251
252                 protected override void ImportPartsBySchemaElement (QName qname, List<MessagePartDescription> parts, Message msg, MessagePart part)
253                 {
254                         XmlSchemaElement element = (XmlSchemaElement) schema_set_in_use.GlobalElements [qname];
255                         if (element == null)
256                                 throw new InvalidOperationException ("Could not resolve : " + qname.ToString ()); // this should have been rejected by CanImportOperation().
257
258                         var ct = element.ElementSchemaType as XmlSchemaComplexType;
259                         if (ct == null) // simple type
260                                 parts.Add (CreateMessagePart (element, msg, part));
261                         else // complex type
262                                 foreach (var elem in GetElementsInParticle (ct.ContentTypeParticle))
263                                         parts.Add (CreateMessagePart (elem, msg, part));
264                 }
265
266                 IEnumerable<XmlSchemaElement> GetElementsInParticle (XmlSchemaParticle p)
267                 {
268                         if (p is XmlSchemaElement) {
269                                 yield return (XmlSchemaElement) p;
270                         } else {
271                                 var gb = p as XmlSchemaGroupBase;
272                                 if (gb != null)
273                                         foreach (XmlSchemaParticle pp in gb.Items)
274                                                 foreach (var e in GetElementsInParticle (pp))
275                                                         yield return e;
276                         }
277                 }
278
279                 MessagePartDescription CreateMessagePart (XmlSchemaElement elem, Message msg, MessagePart msgPart)
280                 {
281                         var part = new MessagePartDescription (elem.QualifiedName.Name, elem.QualifiedName.Namespace);
282                         part.DataContractImporter = dc_importer;
283                         if (dc_importer.CanImport (schema_set_in_use, elem)) {
284                                 var typeQName = dc_importer.Import (schema_set_in_use, elem);
285                                 part.CodeTypeReference = dc_importer.GetCodeTypeReference (elem.ElementSchemaType.QualifiedName, elem);
286                         }
287                         return part;
288                 }
289
290                 protected override void ResolveType (QName qname, List<MessagePartDescription> parts, string ns)
291                 {
292                         /*foreach (XmlSchema xs in importer.Schemas)
293                                 if (xs.Types [qname] != null)
294                                         return resolveParameters ((XmlSchemaElement) xs.Types [qname]., msgdescr, importer);
295
296                         //FIXME: What to do here?
297                         throw new Exception ("Could not resolve : " + qname.ToString ());*/
298                         throw new NotImplementedException ();
299                 }
300
301                 Message FindMessage (OperationMessage om)
302                 {
303                         foreach (WSDL sd in importer.WsdlDocuments)
304                                 if (sd.TargetNamespace == om.Message.Namespace)
305                                         foreach (Message msg in sd.Messages)
306                                                 if (msg.Name == om.Message.Name)
307                                                         return msg;
308                         return null;
309                 }
310
311                 protected override bool CanImportOperation (PortType portType, Operation op)
312                 {
313                         foreach (OperationMessage om in op.Messages) {
314                                 var msg = FindMessage (om);
315                                 if (msg == null)
316                                         return false;
317                                 foreach (MessagePart part in msg.Parts) {
318                                         if (part.Name == "parameters" && !part.Element.IsEmpty) {
319                                                 var xe = schema_set_in_use.GlobalElements [part.Element] as XmlSchemaElement;
320                                                 if (xe == null || !dc_importer.CanImport (schema_set_in_use, xe))
321                                                         return false;
322                                         }
323                                         else
324                                                 return false;
325                                 }
326                         }
327                         return true;
328                 }
329                 
330                 protected override void OnOperationImported (OperationDescription od)
331                 {
332                         // do nothing
333                 }
334         }
335
336         class XmlSerializerMessageContractImporterInternal : MessageContractImporterInternal
337         {
338                 CodeCompileUnit ccu;
339                 XmlSchemaSet schema_set_cache;
340                 XmlSchemaImporter schema_importer;
341                 XmlCodeExporter code_exporter;
342                 
343                 public CodeCompileUnit CodeCompileUnit {
344                         get { return ccu; }
345                 }
346
347                 protected override void Init (WsdlImporter importer)
348                 {
349                         if (ccu == null)
350                                 ccu = importer.GetState<CodeCompileUnit> ();
351                 }
352                 
353                 protected override void ImportPartsBySchemaElement (QName qname, List<MessagePartDescription> parts, Message msg, MessagePart msgPart)
354                 {
355                         if (schema_set_cache != schema_set_in_use) {
356                                 schema_set_cache = schema_set_in_use;
357                                 var xss = new XmlSchemas ();
358                                 foreach (XmlSchema xs in schema_set_cache.Schemas ())
359                                         xss.Add (xs);
360                                 schema_importer = new XmlSchemaImporter (xss);
361                                 if (ccu.Namespaces.Count == 0)
362                                         ccu.Namespaces.Add (new CodeNamespace ());
363                                 var cns = ccu.Namespaces [0];
364                                 code_exporter = new XmlCodeExporter (cns, ccu);
365                         }
366
367                         var part = new MessagePartDescription (qname.Name, qname.Namespace);
368                         part.XmlSerializationImporter = this;
369                         var mbrNS = msg.ServiceDescription.TargetNamespace;
370                         var xmm = schema_importer.ImportMembersMapping (qname);
371                         code_exporter.ExportMembersMapping (xmm);
372                         // FIXME: use of ElementName is a hack!
373                         part.CodeTypeReference = new CodeTypeReference (xmm.ElementName);
374                         parts.Add (part);
375                 }
376
377                 protected override void ResolveType (QName qname, List<MessagePartDescription> parts, string ns)
378                 {
379                         throw new NotImplementedException ();
380                 }
381
382                 protected override bool CanImportOperation (PortType portType, Operation op)
383                 {
384                         // FIXME: implement
385                         return true;
386                 }
387                 
388                 protected override void OnOperationImported (OperationDescription od)
389                 {
390                         od.Behaviors.Add (new XmlSerializerMappingBehavior ());
391                 }
392         }
393         
394         // just a marker behavior
395         class XmlSerializerMappingBehavior : IOperationBehavior
396         {
397                 public void AddBindingParameters (OperationDescription operationDescription, BindingParameterCollection bindingParameters)
398                 {
399                 }
400                 
401                 public void ApplyClientBehavior (OperationDescription operationDescription, ClientOperation clientOperation)
402                 {
403                 }
404                 
405                 public void ApplyDispatchBehavior (OperationDescription operationDescription, DispatchOperation dispatchOperation)
406                 {
407                 }
408                 
409                 public void Validate (OperationDescription operationDescription)
410                 {
411                 }
412         }
413 }