Wrap always_inline and noinline attributes in compiler checks and use MSVC equivalent.
[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                 public void ImportContract (WsdlImporter importer,
92                         WsdlContractConversionContext context)
93                 {
94                         if (importer == null)
95                                 throw new ArgumentNullException ("importer");
96                         if (context == null)
97                                 throw new ArgumentNullException ("context");
98                         if (this.importer != null || this.context != null)
99                                 throw new SystemException ("INTERNAL ERROR: unexpected recursion of ImportContract method call");
100
101                         schema_set_in_use = new XmlSchemaSet ();
102                         schema_set_in_use.Add (importer.XmlSchemas);
103                         foreach (WSDL wsdl in importer.WsdlDocuments)
104                                 foreach (XmlSchema xs in wsdl.Types.Schemas)
105                                         schema_set_in_use.Add (xs);
106
107                         schema_set_in_use.Compile ();
108
109                         this.importer = importer;
110                         this.context = context;
111                         try {
112                                 DoImportContract ();
113                         } finally {
114                                 this.importer = null;
115                                 this.context = null;
116                         }
117                 }
118
119                 internal WsdlImporter importer;
120                 WsdlContractConversionContext context;
121
122                 internal XmlSchemaSet schema_set_in_use;
123
124                 public void BeforeImport (
125                         ServiceDescriptionCollection wsdlDocuments,
126                         XmlSchemaSet xmlSchemas,
127                         ICollection<XmlElement> policy)
128                 {
129                 }
130
131                 void DoImportContract ()
132                 {
133                         PortType port_type = context.WsdlPortType;
134                         ContractDescription contract = context.Contract;
135                         int i, j;
136                         List<MessagePartDescription> parts = new List<MessagePartDescription> ();
137
138                         i = 0;
139                         foreach (Operation op in port_type.Operations) {
140                                 OperationDescription opdescr = contract.Operations [i];
141                                 if (IsOperationImported (port_type, op))
142                                         continue;
143                                 if (!CanImportOperation (port_type, op))
144                                         continue;
145
146                                 j = 0;
147                                 foreach (OperationMessage opmsg in op.Messages) {
148                                         //SM.MessageDescription
149                                         MessageDescription msgdescr = opdescr.Messages [j];
150
151                                         //OpMsg's corresponding WSMessage
152                                         Message msg = port_type.ServiceDescription.Messages [opmsg.Message.Name];
153
154                                         msgdescr.Body.WrapperNamespace = port_type.ServiceDescription.TargetNamespace;
155
156                                         if (opmsg is OperationOutput) {
157                                                 //ReturnValue
158                                                 msg = port_type.ServiceDescription.Messages [opmsg.Message.Name];
159                                                 
160                                                 resolveMessage (msg, msgdescr.Body, parts);
161                                                 if (parts.Count > 0) {
162                                                         msgdescr.Body.ReturnValue = parts [0];
163                                                         parts.Clear ();
164                                                 }
165                                                 continue;
166                                         }
167
168                                         /* OperationInput */
169                                         
170                                         /* Parts, MessagePartDescription */
171                                         resolveMessage (msg, msgdescr.Body, parts);
172                                         foreach (MessagePartDescription p in parts)
173                                                 msgdescr.Body.Parts.Add (p);
174                                         parts.Clear ();
175
176                                         j ++;
177                                 }
178                                 
179                                 OnOperationImported (opdescr);
180
181
182                                 i ++;
183                         }
184
185                 }
186
187                 bool IsOperationImported (PortType pt, Operation op)
188                 {
189                         foreach (OperationMessage opmsg in op.Messages) {
190                                 var parts = context.GetMessageDescription (opmsg).Body.Parts;
191                                 foreach (var part in parts)
192                                         if (part.DataContractImporter != null || part.XmlSerializationImporter != null)
193                                                 return true;
194                         }
195                         return false;
196                 }
197
198                 void resolveMessage (Message msg, MessageBodyDescription body, List<MessagePartDescription> parts)
199                 {
200                         foreach (MessagePart part in msg.Parts) {
201                                 if (part.Name == "parameters") {
202                                         if (!part.Element.IsEmpty) {
203                                                 body.WrapperName = part.Element.Name;
204                                                 ImportPartsBySchemaElement (part.Element, parts, msg, part);
205                                         } else {
206                                                 body.WrapperName = part.Type.Name;
207                                                 ResolveType (part.Type, parts, body.WrapperNamespace);
208                                         }
209                                 }
210                                 else
211                                         throw new InvalidOperationException ("Only 'parameters' element in message part is supported"); // this should have been rejected by CanImportOperation().
212                         }
213                 }
214
215                 public void ImportEndpoint (WsdlImporter importer,
216                         WsdlEndpointConversionContext context)
217                 {
218                 }
219
220                 protected abstract void ImportPartsBySchemaElement (QName qname, List<MessagePartDescription> parts, Message msg, MessagePart part);
221
222                 protected abstract void ResolveType (QName qname, List<MessagePartDescription> parts, string ns);
223
224                 protected abstract bool CanImportOperation (PortType portType, Operation op);
225                 
226                 protected abstract void OnOperationImported (OperationDescription od);
227         }
228
229         class DataContractMessageContractImporterInternal : MessageContractImporterInternal
230         {
231                 XsdDataContractImporter dc_importer = new XsdDataContractImporter ();
232                 
233                 protected override void ImportPartsBySchemaElement (QName qname, List<MessagePartDescription> parts, Message msg, MessagePart part)
234                 {
235                         XmlSchemaElement element = (XmlSchemaElement) schema_set_in_use.GlobalElements [qname];
236                         if (element == null)
237                                 throw new InvalidOperationException ("Could not resolve : " + qname.ToString ()); // this should have been rejected by CanImportOperation().
238
239                         var ct = element.ElementSchemaType as XmlSchemaComplexType;
240                         if (ct == null) // simple type
241                                 parts.Add (CreateMessagePart (element, msg, part));
242                         else // complex type
243                                 foreach (var elem in GetElementsInParticle (ct.ContentTypeParticle))
244                                         parts.Add (CreateMessagePart (elem, msg, part));
245                 }
246
247                 IEnumerable<XmlSchemaElement> GetElementsInParticle (XmlSchemaParticle p)
248                 {
249                         if (p is XmlSchemaElement) {
250                                 yield return (XmlSchemaElement) p;
251                         } else {
252                                 var gb = p as XmlSchemaGroupBase;
253                                 if (gb != null)
254                                         foreach (XmlSchemaParticle pp in gb.Items)
255                                                 foreach (var e in GetElementsInParticle (pp))
256                                                         yield return e;
257                         }
258                 }
259
260                 MessagePartDescription CreateMessagePart (XmlSchemaElement elem, Message msg, MessagePart msgPart)
261                 {
262                         var part = new MessagePartDescription (elem.QualifiedName.Name, elem.QualifiedName.Namespace);
263                         part.DataContractImporter = dc_importer;
264                         if (dc_importer.CanImport (schema_set_in_use, elem)) {
265                                 var typeQName = dc_importer.Import (schema_set_in_use, elem);
266                                 part.CodeTypeReference = dc_importer.GetCodeTypeReference (elem.ElementSchemaType.QualifiedName, elem);
267                         }
268                         return part;
269                 }
270
271                 protected override void ResolveType (QName qname, List<MessagePartDescription> parts, string ns)
272                 {
273                         /*foreach (XmlSchema xs in importer.Schemas)
274                                 if (xs.Types [qname] != null)
275                                         return resolveParameters ((XmlSchemaElement) xs.Types [qname]., msgdescr, importer);
276
277                         //FIXME: What to do here?
278                         throw new Exception ("Could not resolve : " + qname.ToString ());*/
279                         throw new NotImplementedException ();
280                 }
281
282                 Message FindMessage (OperationMessage om)
283                 {
284                         foreach (WSDL sd in importer.WsdlDocuments)
285                                 if (sd.TargetNamespace == om.Message.Namespace)
286                                         foreach (Message msg in sd.Messages)
287                                                 if (msg.Name == om.Message.Name)
288                                                         return msg;
289                         return null;
290                 }
291
292                 protected override bool CanImportOperation (PortType portType, Operation op)
293                 {
294                         foreach (OperationMessage om in op.Messages) {
295                                 var msg = FindMessage (om);
296                                 if (msg == null)
297                                         return false;
298                                 foreach (MessagePart part in msg.Parts) {
299                                         if (part.Name == "parameters" && !part.Element.IsEmpty) {
300                                                 var xe = schema_set_in_use.GlobalElements [part.Element] as XmlSchemaElement;
301                                                 if (xe == null || !dc_importer.CanImport (schema_set_in_use, xe))
302                                                         return false;
303                                         }
304                                         else
305                                                 return false;
306                                 }
307                         }
308                         return true;
309                 }
310                 
311                 protected override void OnOperationImported (OperationDescription od)
312                 {
313                         // do nothing
314                 }
315         }
316
317         class XmlSerializerMessageContractImporterInternal : MessageContractImporterInternal
318         {
319                 CodeCompileUnit ccu = new CodeCompileUnit ();
320                 XmlSchemaSet schema_set_cache;
321                 XmlSchemaImporter schema_importer;
322                 XmlCodeExporter code_exporter;
323                 
324                 public CodeCompileUnit CodeCompileUnit {
325                         get { return ccu; }
326                 }
327                 
328                 protected override void ImportPartsBySchemaElement (QName qname, List<MessagePartDescription> parts, Message msg, MessagePart msgPart)
329                 {
330                         if (schema_set_cache != schema_set_in_use) {
331                                 schema_set_cache = schema_set_in_use;
332                                 var xss = new XmlSchemas ();
333                                 foreach (XmlSchema xs in schema_set_cache.Schemas ())
334                                         xss.Add (xs);
335                                 schema_importer = new XmlSchemaImporter (xss);
336                                 if (ccu.Namespaces.Count == 0)
337                                         ccu.Namespaces.Add (new CodeNamespace ());
338                                 var cns = ccu.Namespaces [0];
339                                 code_exporter = new XmlCodeExporter (cns, ccu);
340                         }
341
342                         var part = new MessagePartDescription (qname.Name, qname.Namespace);
343                         part.XmlSerializationImporter = this;
344                         var mbrNS = msg.ServiceDescription.TargetNamespace;
345                         var xmm = schema_importer.ImportMembersMapping (qname);
346                         code_exporter.ExportMembersMapping (xmm);
347                         // FIXME: use of ElementName is a hack!
348                         part.CodeTypeReference = new CodeTypeReference (xmm.ElementName);
349                         parts.Add (part);
350                 }
351
352                 protected override void ResolveType (QName qname, List<MessagePartDescription> parts, string ns)
353                 {
354                         throw new NotImplementedException ();
355                 }
356
357                 protected override bool CanImportOperation (PortType portType, Operation op)
358                 {
359                         // FIXME: implement
360                         return true;
361                 }
362                 
363                 protected override void OnOperationImported (OperationDescription od)
364                 {
365                         od.Behaviors.Add (new XmlSerializerMappingBehavior ());
366                 }
367         }
368         
369         // just a marker behavior
370         class XmlSerializerMappingBehavior : IOperationBehavior
371         {
372                 public void AddBindingParameters (OperationDescription operationDescription, BindingParameterCollection bindingParameters)
373                 {
374                 }
375                 
376                 public void ApplyClientBehavior (OperationDescription operationDescription, ClientOperation clientOperation)
377                 {
378                 }
379                 
380                 public void ApplyDispatchBehavior (OperationDescription operationDescription, DispatchOperation dispatchOperation)
381                 {
382                 }
383                 
384                 public void Validate (OperationDescription operationDescription)
385                 {
386                 }
387         }
388 }