Merge branch 'master' of github.com:mono/mono
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Description / ServiceContractGenerator.cs
1 //
2 // ServiceContractGenerator.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.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.ComponentModel;
33 using System.Configuration;
34 using System.Linq;
35 using System.Reflection;
36 using System.Runtime.Serialization;
37 using System.ServiceModel;
38 using System.ServiceModel.Channels;
39 using System.ServiceModel.Configuration;
40 using System.Threading;
41 using System.Xml.Schema;
42 using System.Xml.Serialization;
43
44 using ConfigurationType = System.Configuration.Configuration;
45 using QName = System.Xml.XmlQualifiedName;
46 using OPair = System.Collections.Generic.KeyValuePair<System.ServiceModel.Description.IOperationContractGenerationExtension,System.ServiceModel.Description.OperationContractGenerationContext>;
47
48 namespace System.ServiceModel.Description
49 {
50         public class ServiceContractGenerator
51         {
52                 CodeCompileUnit ccu;
53                 ConfigurationType config;
54                 Collection<MetadataConversionError> errors
55                         = new Collection<MetadataConversionError> ();
56                 Dictionary<string,string> nsmappings
57                         = new Dictionary<string,string> ();
58                 Dictionary<ContractDescription,Type> referenced_types
59                         = new Dictionary<ContractDescription,Type> ();
60                 ServiceContractGenerationOptions options;
61                 Dictionary<QName, QName> imported_names = null;
62                 ServiceContractGenerationContext contract_context;
63                 List<OPair> operation_contexts = new List<OPair> ();
64
65 #if USE_DATA_CONTRACT_IMPORTER
66                 XsdDataContractImporter xsd_data_importer;
67 #endif
68
69                 public ServiceContractGenerator ()
70                         : this (null, null)
71                 {
72                 }
73
74                 public ServiceContractGenerator (CodeCompileUnit ccu)
75                         : this (ccu, null)
76                 {
77                 }
78
79                 public ServiceContractGenerator (ConfigurationType config)
80                         : this (null, config)
81                 {
82                 }
83
84                 public ServiceContractGenerator (CodeCompileUnit ccu, ConfigurationType config)
85                 {
86                         if (ccu == null)
87                                 this.ccu = new CodeCompileUnit ();
88                         else
89                                 this.ccu = ccu;
90                         this.config = config;
91                         Options |= ServiceContractGenerationOptions.ChannelInterface | 
92                                 ServiceContractGenerationOptions.ClientClass;
93                 }
94
95                 public ConfigurationType Configuration {
96                         get { return config; }
97                 }
98
99                 public Collection<MetadataConversionError> Errors {
100                         get { return errors; }
101                 }
102
103                 public Dictionary<string,string> NamespaceMappings {
104                         get { return nsmappings; }
105                 }
106
107                 public ServiceContractGenerationOptions Options {
108                         get { return options; }
109                         set { options = value; }
110                 }
111
112                 bool GenerateAsync {
113                         get { return GenerateEventBasedAsync || (options & ServiceContractGenerationOptions.AsynchronousMethods) != 0; }
114                 }
115
116                 bool GenerateEventBasedAsync {
117                         get { return (options & ServiceContractGenerationOptions.EventBasedAsynchronousMethods) != 0; }
118                 }
119
120                 public Dictionary<ContractDescription,Type> ReferencedTypes {
121                         get { return referenced_types; }
122                 }
123
124                 public CodeCompileUnit TargetCompileUnit {
125                         get { return ccu; }
126                 }
127
128                 [MonoTODO]
129                 public void GenerateBinding (Binding binding,
130                         out string bindingSectionName,
131                         out string configurationName)
132                 {
133                         throw new NotImplementedException ();
134                 }
135
136                 #region Service Contract Type
137
138                 // Those implementation classes are very likely to be split
139                 // into different classes.
140
141                 [MonoTODO]
142                 public CodeTypeReference GenerateServiceContractType (
143                         ContractDescription contractDescription)
144                 {
145                         CodeNamespace cns = GetNamespace (contractDescription.Namespace);
146                         imported_names = new Dictionary<QName, QName> ();
147                         var ret = ExportInterface (contractDescription, cns);
148
149                         // FIXME: handle duplex callback
150
151                         if ((Options & ServiceContractGenerationOptions.ChannelInterface) != 0)
152                                 GenerateChannelInterface (contractDescription, cns);
153
154                         if ((Options & ServiceContractGenerationOptions.ClientClass) != 0)
155                                 GenerateProxyClass (contractDescription, cns);
156
157 #if USE_DATA_CONTRACT_IMPORTER
158                         if (xsd_data_importer != null)
159                                 MergeCompileUnit (xsd_data_importer.CodeCompileUnit, ccu);
160 #endif
161
162                         // Process extensions. Class first, then methods.
163                         // (built-in ones must present before processing class extensions).
164                         foreach (var cb in contractDescription.Behaviors) {
165                                 var gex = cb as IServiceContractGenerationExtension;
166                                 if (gex != null)
167                                         gex.GenerateContract (contract_context);
168                         }
169                         foreach (var opair in operation_contexts)
170                                 opair.Key.GenerateOperation (opair.Value);
171
172                         return ret;
173                 }
174
175                 CodeNamespace GetNamespace (string ns)
176                 {
177                         if (ns == null)
178                                 ns = String.Empty;
179                         foreach (CodeNamespace cns in ccu.Namespaces)
180                                 if (cns.Name == ns)
181                                         return cns;
182                         CodeNamespace ncns = new CodeNamespace ();
183                         //ncns.Name = ns;
184                         ccu.Namespaces.Add (ncns);
185                         return ncns;
186                 }
187
188                 CodeTypeDeclaration GetTypeDeclaration (CodeNamespace cns, string name)
189                 {
190                         foreach (CodeTypeDeclaration type in cns.Types)
191                                 if (type.Name == name)
192                                         return type;
193                         return null;
194                 }
195
196                 void GenerateProxyClass (ContractDescription cd, CodeNamespace cns)
197                 {
198                         string name = cd.Name + "Client";
199                         if (name [0] == 'I')
200                                 name = name.Substring (1);
201                         CodeTypeDeclaration type = GetTypeDeclaration (cns, name);
202                         if (type != null)
203                                 return; // already imported
204                         CodeTypeReference clientBase = new CodeTypeReference (typeof (ClientBase<>));
205                         clientBase.TypeArguments.Add (new CodeTypeReference (cd.Name));
206                         type = new CodeTypeDeclaration (name);
207                         cns.Types.Add (type);
208                         type.TypeAttributes = TypeAttributes.Public;
209                         type.BaseTypes.Add (clientBase);
210                         type.BaseTypes.Add (new CodeTypeReference (cd.Name));
211
212                         // .ctor()
213                         CodeConstructor ctor = new CodeConstructor ();
214                         ctor.Attributes = MemberAttributes.Public;
215                         type.Members.Add (ctor);
216
217                         // .ctor(string endpointConfigurationName)
218                         ctor = new CodeConstructor ();
219                         ctor.Attributes = MemberAttributes.Public;
220                         ctor.Parameters.Add (
221                                 new CodeParameterDeclarationExpression (
222                                         new CodeTypeReference (typeof (string)), "endpointConfigurationName"));
223                         ctor.BaseConstructorArgs.Add (
224                                 new CodeArgumentReferenceExpression ("endpointConfigurationName"));
225                         type.Members.Add (ctor);
226
227                         // .ctor(string endpointConfigurationName, string remoteAddress)
228                         ctor = new CodeConstructor ();
229                         ctor.Attributes = MemberAttributes.Public;
230                         ctor.Parameters.Add (
231                                 new CodeParameterDeclarationExpression (
232                                         new CodeTypeReference (typeof (string)), "endpointConfigurationName"));
233                         ctor.Parameters.Add (
234                                 new CodeParameterDeclarationExpression (
235                                         new CodeTypeReference (typeof (string)), "remoteAddress"));
236                         ctor.BaseConstructorArgs.Add (
237                                 new CodeArgumentReferenceExpression ("endpointConfigurationName"));
238                         ctor.BaseConstructorArgs.Add (
239                                 new CodeArgumentReferenceExpression ("remoteAddress"));
240                         type.Members.Add (ctor);
241
242                         // .ctor(string endpointConfigurationName, EndpointAddress remoteAddress)
243                         ctor = new CodeConstructor ();
244                         ctor.Attributes = MemberAttributes.Public;
245                         ctor.Parameters.Add (
246                                 new CodeParameterDeclarationExpression (
247                                         new CodeTypeReference (typeof (string)), "endpointConfigurationName"));
248                         ctor.Parameters.Add (
249                                 new CodeParameterDeclarationExpression (
250                                         new CodeTypeReference (typeof (EndpointAddress)), "remoteAddress"));
251                         ctor.BaseConstructorArgs.Add (
252                                 new CodeArgumentReferenceExpression ("endpointConfigurationName"));
253                         ctor.BaseConstructorArgs.Add (
254                                 new CodeArgumentReferenceExpression ("remoteAddress"));
255                         type.Members.Add (ctor);
256
257                         // .ctor(Binding,EndpointAddress)
258                         ctor = new CodeConstructor ();
259                         ctor.Attributes = MemberAttributes.Public;
260                         ctor.Parameters.Add (
261                                 new CodeParameterDeclarationExpression (
262                                         new CodeTypeReference (typeof (Binding)), "binding"));
263                         ctor.Parameters.Add (
264                                 new CodeParameterDeclarationExpression (
265                                         new CodeTypeReference (typeof (EndpointAddress)), "endpoint"));
266                         ctor.BaseConstructorArgs.Add (
267                                 new CodeArgumentReferenceExpression ("binding"));
268                         ctor.BaseConstructorArgs.Add (
269                                 new CodeArgumentReferenceExpression ("endpoint"));
270                         type.Members.Add (ctor);
271
272                         // service contract methods
273                         AddImplementationClientMethods (type, cd);
274
275                         if (GenerateEventBasedAsync)
276                                 foreach (var od in cd.Operations)
277                                         GenerateEventBasedAsyncSupport (type, od, cns);
278                 }
279
280                 void GenerateChannelInterface (ContractDescription cd, CodeNamespace cns)
281                 {
282                         string name = cd.Name + "Channel";
283                         CodeTypeDeclaration type = GetTypeDeclaration (cns, name);
284                         if (type != null)
285                                 return;
286
287                         type = new CodeTypeDeclaration ();
288                         type.Name = name;
289                         type.TypeAttributes = TypeAttributes.Interface | TypeAttributes.Public;
290                         cns.Types.Add (type);
291                         
292                         type.BaseTypes.Add (ExportInterface (cd, cns));
293                         type.BaseTypes.Add (new CodeTypeReference (typeof (System.ServiceModel.IClientChannel)));
294                 }
295
296                 CodeTypeReference ExportInterface (ContractDescription cd, CodeNamespace cns)
297                 {
298                         CodeTypeDeclaration type = GetTypeDeclaration (cns, cd.Name);
299                         if (type != null)
300                                 return new CodeTypeReference (type.Name);
301                         type = new CodeTypeDeclaration ();
302                         type.TypeAttributes = TypeAttributes.Interface;
303                         type.TypeAttributes |= TypeAttributes.Public;
304                         cns.Types.Add (type);
305                         type.Name = cd.Name;
306                         CodeAttributeDeclaration ad = 
307                                 new CodeAttributeDeclaration (
308                                         new CodeTypeReference (
309                                                 typeof (ServiceContractAttribute)));
310                         ad.Arguments.Add (new CodeAttributeArgument ("Namespace", new CodePrimitiveExpression (cd.Namespace)));
311                         type.CustomAttributes.Add (ad);
312                         contract_context = new ServiceContractGenerationContext (this, cd, type);
313
314                         AddOperationMethods (type, cd);
315
316                         return new CodeTypeReference (type.Name);
317                 }
318
319                 void AddBeginAsyncArgs (CodeMemberMethod cm)
320                 {
321                         var acb = new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (AsyncCallback)), "asyncCallback");
322                         cm.Parameters.Add (acb);
323                         var us = new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (object)), "userState");
324                         cm.Parameters.Add (us);
325                 }
326
327                 void AddOperationMethods (CodeTypeDeclaration type, ContractDescription cd)
328                 {
329                         foreach (OperationDescription od in cd.Operations) {
330                                 CodeMemberMethod syncMethod = null, beginMethod = null, endMethod = null;
331
332                                 CodeTypeReference returnTypeFromMessageContract = null;
333                                 syncMethod = GenerateOperationMethod (type, cd, od, false, out returnTypeFromMessageContract);
334                                 type.Members.Add (syncMethod);
335
336                                 if (GenerateAsync) {
337                                         beginMethod = GenerateOperationMethod (type, cd, od, true, out returnTypeFromMessageContract);
338                                         type.Members.Add (beginMethod);
339
340                                         var cm = new CodeMemberMethod ();
341                                         type.Members.Add (cm);
342                                         cm.Name = "End" + od.Name;
343                                         endMethod = cm;
344
345                                         var res = new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (IAsyncResult)), "result");
346                                         cm.Parameters.Add (res);
347
348                                         if (od.SyncMethod != null) // FIXME: it depends on sync method!
349                                                 cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
350                                         else
351                                                 cm.ReturnType = returnTypeFromMessageContract;
352                                 }
353
354                                 foreach (var ob in od.Behaviors) {
355                                         var gex = ob as IOperationContractGenerationExtension;
356                                         if (gex != null)
357                                                 operation_contexts.Add (new OPair (gex, new OperationContractGenerationContext (this, contract_context, od, type, syncMethod, beginMethod, endMethod)));
358                                 }
359                         }
360                 }
361
362                 CodeMemberMethod GenerateOperationMethod (CodeTypeDeclaration type, ContractDescription cd, OperationDescription od, bool async, out CodeTypeReference returnType)
363                 {
364                         CodeMemberMethod cm = new CodeMemberMethod ();
365
366                         if (async)
367                                 cm.Name = "Begin" + od.Name;
368                         else
369                                 cm.Name = od.Name;
370
371                         if (od.SyncMethod != null) {
372                                 ExportParameters (cm, od.SyncMethod.GetParameters ());
373                                 if (async) {
374                                         AddBeginAsyncArgs (cm);
375                                         cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult));
376                                 }
377                                 else
378                                         cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
379                                 returnType = new CodeTypeReference (od.SyncMethod.ReturnType);
380                         } else {
381                                 ExportMessages (od.Messages, cm, false);
382                                 returnType = cm.ReturnType;
383                                 if (async) {
384                                         AddBeginAsyncArgs (cm);
385                                         cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult));
386                                 }
387                         }
388
389                         // [OperationContract (Action = "...", ReplyAction = "..")]
390                         var ad = new CodeAttributeDeclaration (new CodeTypeReference (typeof (OperationContractAttribute)));
391                         foreach (MessageDescription md in od.Messages) {
392                                 if (md.IsRequest)
393                                         ad.Arguments.Add (new CodeAttributeArgument ("Action", new CodePrimitiveExpression (md.Action)));
394                                 else
395                                         ad.Arguments.Add (new CodeAttributeArgument ("ReplyAction", new CodePrimitiveExpression (md.Action)));
396                         }
397                         if (async)
398                                 ad.Arguments.Add (new CodeAttributeArgument ("AsyncPattern", new CodePrimitiveExpression (true)));
399                         cm.CustomAttributes.Add (ad);
400
401                         return cm;
402                 }
403
404                 void ExportParameters (CodeMemberMethod method, ParameterInfo [] parameters)
405                 {
406                         foreach (ParameterInfo pi in parameters)
407                                 method.Parameters.Add (
408                                         new CodeParameterDeclarationExpression (
409                                                 new CodeTypeReference (pi.ParameterType),
410                                                 pi.Name));
411                 }
412
413                 void AddImplementationClientMethods (CodeTypeDeclaration type, ContractDescription cd)
414                 {
415                         foreach (OperationDescription od in cd.Operations) {
416                                 CodeMemberMethod cm;
417                                 CodeTypeReference returnTypeFromMessageContract = null;
418                                 cm = GenerateImplementationClientMethod (type, cd, od, false, out returnTypeFromMessageContract);
419                                 type.Members.Add (cm);
420
421                                 if (!GenerateAsync)
422                                         continue;
423
424                                 cm = GenerateImplementationClientMethod (type, cd, od, true, out returnTypeFromMessageContract);
425                                 type.Members.Add (cm);
426
427                                 // EndXxx() implementation
428
429                                 cm = new CodeMemberMethod ();
430                                 cm.Attributes = MemberAttributes.Public 
431                                                 | MemberAttributes.Final;
432                                 type.Members.Add (cm);
433                                 cm.Name = "End" + od.Name;
434
435                                 var res = new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (IAsyncResult)), "result");
436                                 cm.Parameters.Add (res);
437
438                                 if (od.SyncMethod != null) // FIXME: it depends on sync method!
439                                         cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
440                                 else
441                                         cm.ReturnType = returnTypeFromMessageContract;
442
443                                 string resultArgName = "result";
444                                 if (od.EndMethod != null)
445                                         resultArgName = od.EndMethod.GetParameters () [0].Name;
446
447                                 var call = new CodeMethodInvokeExpression (
448                                         new CodePropertyReferenceExpression (
449                                                 new CodeBaseReferenceExpression (),
450                                                 "Channel"),
451                                         cm.Name,
452                                         new CodeArgumentReferenceExpression (resultArgName));
453
454                                 if (cm.ReturnType.BaseType == "System.Void")
455                                         cm.Statements.Add (new CodeExpressionStatement (call));
456                                 else
457                                         cm.Statements.Add (new CodeMethodReturnStatement (call));
458                         }
459                 }
460
461                 CodeMemberMethod GenerateImplementationClientMethod (CodeTypeDeclaration type, ContractDescription cd, OperationDescription od, bool async, out CodeTypeReference returnTypeFromMessageContract)
462                 {
463                         CodeMemberMethod cm = new CodeMemberMethod ();
464                         if (async)
465                                 cm.Name = "Begin" + od.Name;
466                         else
467                                 cm.Name = od.Name;
468                         cm.Attributes = MemberAttributes.Public | MemberAttributes.Final;
469                         returnTypeFromMessageContract = null;
470
471                         List<CodeExpression> args = new List<CodeExpression> ();
472                         if (od.SyncMethod != null) {
473                                 ParameterInfo [] pars = od.SyncMethod.GetParameters ();
474                                 ExportParameters (cm, pars);
475                                 cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
476                                 int i = 0;
477                                 foreach (ParameterInfo pi in pars)
478                                         args.Add (new CodeArgumentReferenceExpression (pi.Name));
479                         } else {
480                                 args.AddRange (ExportMessages (od.Messages, cm, true));
481                                 returnTypeFromMessageContract = cm.ReturnType;
482                                 if (async) {
483                                         AddBeginAsyncArgs (cm);
484                                         cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult));
485                                 }
486                         }
487                         if (async) {
488                                 args.Add (new CodeArgumentReferenceExpression ("asyncCallback"));
489                                 args.Add (new CodeArgumentReferenceExpression ("userState"));
490                         }
491
492                         CodeExpression call = new CodeMethodInvokeExpression (
493                                 new CodePropertyReferenceExpression (
494                                         new CodeBaseReferenceExpression (),
495                                         "Channel"),
496                                 cm.Name,
497                                 args.ToArray ());
498
499                         if (cm.ReturnType.BaseType == "System.Void")
500                                 cm.Statements.Add (new CodeExpressionStatement (call));
501                         else
502                                 cm.Statements.Add (new CodeMethodReturnStatement (call));
503                         return cm;
504                 }
505
506                 CodeMemberMethod FindByName (CodeTypeDeclaration type, string name)
507                 {
508                         foreach (var m in type.Members) {
509                                 var method = m as CodeMemberMethod;
510                                 if (method != null && method.Name == name)
511                                         return method;
512                         }
513                         return null;
514                 }
515
516                 void GenerateEventBasedAsyncSupport (CodeTypeDeclaration type, OperationDescription od, CodeNamespace cns)
517                 {
518                         var method = FindByName (type, od.Name) ?? FindByName (type, "Begin" + od.Name);
519                         var endMethod = method.Name == od.Name ? null : FindByName (type, "End" + od.Name);
520                         bool methodAsync = method.Name.StartsWith ("Begin", StringComparison.Ordinal);
521
522                         var thisExpr = new CodeThisReferenceExpression ();
523                         var baseExpr = new CodeBaseReferenceExpression ();
524                         var nullExpr = new CodePrimitiveExpression (null);
525                         var asyncResultType = new CodeTypeReference (typeof (IAsyncResult));
526
527                         // OnBeginXxx() implementation
528                         var cm = new CodeMemberMethod () {
529                                 Name = "OnBegin" + od.Name,
530                                 Attributes = MemberAttributes.Private | MemberAttributes.Final,
531                                 ReturnType = asyncResultType
532                                 };
533                         type.Members.Add (cm);
534
535                         AddMethodParam (cm, typeof (object []), "args");
536                         AddMethodParam (cm, typeof (AsyncCallback), "asyncCallback");
537                         AddMethodParam (cm, typeof (object), "userState");
538
539                         var call = new CodeMethodInvokeExpression (
540                                 thisExpr,
541                                 "Begin" + od.Name);
542                         for (int idx = 0; idx < method.Parameters.Count - (methodAsync ? 2 : 0); idx++) {
543                                 var p = method.Parameters [idx];
544                                 cm.Statements.Add (new CodeVariableDeclarationStatement (p.Type, p.Name, new CodeCastExpression (p.Type, new CodeArrayIndexerExpression (new CodeArgumentReferenceExpression ("args"), new CodePrimitiveExpression (idx)))));
545                                 call.Parameters.Add (new CodeVariableReferenceExpression (p.Name));
546                         }
547                         call.Parameters.Add (new CodeArgumentReferenceExpression ("asyncCallback"));
548                         call.Parameters.Add (new CodeArgumentReferenceExpression ("userState"));
549                         cm.Statements.Add (new CodeMethodReturnStatement (call));
550
551                         // OnEndXxx() implementation
552                         cm = new CodeMemberMethod () {
553                                 Name = "OnEnd" + od.Name,
554                                 Attributes = MemberAttributes.Private | MemberAttributes.Final,
555                                 ReturnType = new CodeTypeReference (typeof (object [])) };
556                         type.Members.Add (cm);
557
558                         AddMethodParam (cm, typeof (IAsyncResult), "result");
559
560                         var outArgRefs = new List<CodeVariableReferenceExpression> ();
561
562                         for (int idx = 0; idx < method.Parameters.Count; idx++) {
563                                 var p = method.Parameters [idx];
564                                 if (p.Direction != FieldDirection.In) {
565                                         cm.Statements.Add (new CodeVariableDeclarationStatement (p.Type, p.Name));
566                                         outArgRefs.Add (new CodeVariableReferenceExpression (p.Name)); // FIXME: should this work? They need "out" or "ref" modifiers.
567                                 }
568                         }
569
570                         call = new CodeMethodInvokeExpression (
571                                 thisExpr,
572                                 "End" + od.Name,
573                                 new CodeArgumentReferenceExpression ("result"));
574                         call.Parameters.AddRange (outArgRefs.Cast<CodeExpression> ().ToArray ()); // questionable
575
576                         cm.Statements.Add (new CodeVariableDeclarationStatement (typeof (object), "__ret", call));
577                         var retCreate = new CodeArrayCreateExpression (typeof (object));
578                         retCreate.Initializers.Add (new CodeVariableReferenceExpression ("__ret"));
579                         foreach (var outArgRef in outArgRefs)
580                                 retCreate.Initializers.Add (new CodeVariableReferenceExpression (outArgRef.VariableName));
581
582                         cm.Statements.Add (new CodeMethodReturnStatement (retCreate));
583
584                         // OnXxxCompleted() implementation
585                         cm = new CodeMemberMethod () {
586                                 Name = "On" + od.Name + "Completed",
587                                 Attributes = MemberAttributes.Private | MemberAttributes.Final };
588                         type.Members.Add (cm);
589
590                         AddMethodParam (cm, typeof (object), "state");
591
592                         var iaargs = new CodeTypeReference ("InvokeAsyncCompletedEventArgs"); // avoid messy System.Type instance for generic nested type :|
593                         var iaref = new CodeVariableReferenceExpression ("args");
594                         var methodEventArgs = new CodeObjectCreateExpression (new CodeTypeReference (od.Name + "CompletedEventArgs"),
595                                 new CodePropertyReferenceExpression (iaref, "Results"),
596                                 new CodePropertyReferenceExpression (iaref, "Error"),
597                                 new CodePropertyReferenceExpression (iaref, "Cancelled"),
598                                 new CodePropertyReferenceExpression (iaref, "UserState"));
599                         cm.Statements.Add (new CodeConditionStatement (
600                                 new CodeBinaryOperatorExpression (
601                                         new CodeEventReferenceExpression (thisExpr, od.Name + "Completed"), CodeBinaryOperatorType.IdentityInequality, nullExpr),
602                                 new CodeVariableDeclarationStatement (iaargs, "args", new CodeCastExpression (iaargs, new CodeArgumentReferenceExpression ("state"))),
603                                 new CodeExpressionStatement (new CodeMethodInvokeExpression (thisExpr, od.Name + "Completed", thisExpr, methodEventArgs))));
604
605                         // delegate fields
606                         type.Members.Add (new CodeMemberField (new CodeTypeReference ("BeginOperationDelegate"), "onBegin" + od.Name + "Delegate"));
607                         type.Members.Add (new CodeMemberField (new CodeTypeReference ("EndOperationDelegate"), "onEnd" + od.Name + "Delegate"));
608                         type.Members.Add (new CodeMemberField (new CodeTypeReference (typeof (SendOrPostCallback)), "on" + od.Name + "CompletedDelegate"));
609
610                         // XxxCompletedEventArgs class
611                         var argsType = new CodeTypeDeclaration (od.Name + "CompletedEventArgs");
612                         argsType.BaseTypes.Add (new CodeTypeReference (typeof (AsyncCompletedEventArgs)));
613                         cns.Types.Add (argsType);
614
615                         var argsCtor = new CodeConstructor () {
616                                 Attributes = MemberAttributes.Public | MemberAttributes.Final };
617                         argsCtor.Parameters.Add (new CodeParameterDeclarationExpression (typeof (object []), "results"));
618                         argsCtor.Parameters.Add (new CodeParameterDeclarationExpression (typeof (Exception), "error"));
619                         argsCtor.Parameters.Add (new CodeParameterDeclarationExpression (typeof (bool), "cancelled"));
620                         argsCtor.Parameters.Add (new CodeParameterDeclarationExpression (typeof (object), "userState"));
621                         argsCtor.BaseConstructorArgs.Add (new CodeArgumentReferenceExpression ("error"));
622                         argsCtor.BaseConstructorArgs.Add (new CodeArgumentReferenceExpression ("cancelled"));
623                         argsCtor.BaseConstructorArgs.Add (new CodeArgumentReferenceExpression ("userState"));
624                         var resultsField = new CodeFieldReferenceExpression (thisExpr, "results");
625                         argsCtor.Statements.Add (new CodeAssignStatement (resultsField, new CodeArgumentReferenceExpression ("results")));
626                         argsType.Members.Add (argsCtor);
627
628                         argsType.Members.Add (new CodeMemberField (typeof (object []), "results"));
629
630                         var resultProp = new CodeMemberProperty {
631                                 Name = "Result",
632                                 Type = endMethod != null ? endMethod.ReturnType : method.ReturnType,
633                                 Attributes = MemberAttributes.Public | MemberAttributes.Final };
634                         resultProp.GetStatements.Add (new CodeMethodReturnStatement (new CodeCastExpression (resultProp.Type, new CodeArrayIndexerExpression (resultsField, new CodePrimitiveExpression (0)))));
635                         argsType.Members.Add (resultProp);
636
637                         // event field
638                         var handlerType = new CodeTypeReference (typeof (EventHandler<>));
639                         handlerType.TypeArguments.Add (new CodeTypeReference (argsType.Name));
640                         type.Members.Add (new CodeMemberEvent () {
641                                 Name = od.Name + "Completed",
642                                 Type = handlerType,
643                                 Attributes = MemberAttributes.Public | MemberAttributes.Final });
644
645                         // XxxAsync() implementations
646                         bool hasAsync = false;
647                         foreach (int __x in Enumerable.Range (0, 2)) {
648                                 cm = new CodeMemberMethod ();
649                                 type.Members.Add (cm);
650                                 cm.Name = od.Name + "Async";
651                                 cm.Attributes = MemberAttributes.Public 
652                                                 | MemberAttributes.Final;
653
654                                 var inArgs = new List<CodeParameterDeclarationExpression > ();
655
656                                 for (int idx = 0; idx < method.Parameters.Count - (methodAsync ? 2 : 0); idx++) {
657                                         var pd = method.Parameters [idx];
658                                         inArgs.Add (pd);
659                                         cm.Parameters.Add (pd);
660                                 }
661
662                                 // First one is overload without asyncState arg.
663                                 if (!hasAsync) {
664                                         call = new CodeMethodInvokeExpression (thisExpr, cm.Name, inArgs.ConvertAll<CodeExpression> (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ());
665                                         call.Parameters.Add (nullExpr);
666                                         cm.Statements.Add (new CodeExpressionStatement (call));
667                                         hasAsync = true;
668                                         continue;
669                                 }
670
671                                 // Second one is the primary one.
672
673                                 cm.Parameters.Add (new CodeParameterDeclarationExpression (typeof (object), "userState"));
674
675                                 // if (onBeginBarOperDelegate == null) onBeginBarOperDelegate = new BeginOperationDelegate (OnBeginBarOper);
676                                 // if (onEndBarOperDelegate == null) onEndBarOperDelegate = new EndOperationDelegate (OnEndBarOper);
677                                 // if (onBarOperCompletedDelegate == null) onBarOperCompletedDelegate = new BeginOperationDelegate (OnBarOperCompleted);
678                                 var beginOperDelegateRef = new CodeFieldReferenceExpression (thisExpr, "onBegin" + od.Name + "Delegate");
679                                 var endOperDelegateRef = new CodeFieldReferenceExpression (thisExpr, "onEnd" + od.Name + "Delegate");
680                                 var operCompletedDelegateRef = new CodeFieldReferenceExpression (thisExpr, "on" + od.Name + "CompletedDelegate");
681
682                                 var ifstmt = new CodeConditionStatement (
683                                         new CodeBinaryOperatorExpression (beginOperDelegateRef, CodeBinaryOperatorType.IdentityEquality, nullExpr),
684                                         new CodeAssignStatement (beginOperDelegateRef, new CodeDelegateCreateExpression (new CodeTypeReference ("BeginOperationDelegate"), thisExpr, "OnBegin" + od.Name)));
685                                 cm.Statements.Add (ifstmt);
686                                 ifstmt = new CodeConditionStatement (
687                                         new CodeBinaryOperatorExpression (endOperDelegateRef, CodeBinaryOperatorType.IdentityEquality, nullExpr),
688                                         new CodeAssignStatement (endOperDelegateRef, new CodeDelegateCreateExpression (new CodeTypeReference ("EndOperationDelegate"), thisExpr, "OnEnd" + od.Name)));
689                                 cm.Statements.Add (ifstmt);
690                                 ifstmt = new CodeConditionStatement (
691                                         new CodeBinaryOperatorExpression (operCompletedDelegateRef, CodeBinaryOperatorType.IdentityEquality, nullExpr),
692                                         new CodeAssignStatement (operCompletedDelegateRef, new CodeDelegateCreateExpression (new CodeTypeReference (typeof (SendOrPostCallback)), thisExpr, "On" + od.Name + "Completed")));
693                                 cm.Statements.Add (ifstmt);
694
695                                 // InvokeAsync (onBeginBarOperDelegate, inValues, onEndBarOperDelegate, onBarOperCompletedDelegate, userState);
696
697                                 inArgs.Add (new CodeParameterDeclarationExpression (typeof (object), "userState"));
698
699                                 var args = new List<CodeExpression> ();
700                                 args.Add (beginOperDelegateRef);
701                                 args.Add (new CodeArrayCreateExpression (typeof (object), inArgs.ConvertAll<CodeExpression> (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ()));
702                                 args.Add (endOperDelegateRef);
703                                 args.Add (new CodeFieldReferenceExpression (thisExpr, "on" + od.Name + "CompletedDelegate"));
704                                 args.Add (new CodeArgumentReferenceExpression ("userState"));
705                                 call = new CodeMethodInvokeExpression (baseExpr, "InvokeAsync", args.ToArray ());
706                                 cm.Statements.Add (new CodeExpressionStatement (call));
707                         }
708                 }
709
710                 void AddMethodParam (CodeMemberMethod cm, Type type, string name)
711                 {
712                         cm.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (type), name));
713                 }
714
715                 const string ms_arrays_ns = "http://schemas.microsoft.com/2003/10/Serialization/Arrays";
716
717                 string GetCodeTypeName (QName mappedTypeName)
718                 {
719                         if (mappedTypeName.Namespace == ms_arrays_ns)
720                                 return DataContractSerializerMessageContractImporter.GetCLRTypeName (mappedTypeName.Name.Substring ("ArrayOf".Length)) + "[]";
721                         return mappedTypeName.Name;
722                 }
723
724                 private CodeExpression[] ExportMessages (MessageDescriptionCollection messages, CodeMemberMethod method, bool return_args)
725                 {
726                         CodeExpression [] args = null;
727                         foreach (MessageDescription md in messages) {
728                                 if (!md.IsRequest) {
729                                         if (md.Body.ReturnValue != null) {
730                                                 ExportDataContract (md.Body.ReturnValue);
731 #if USE_DATA_CONTRACT_IMPORTER
732                                                 method.ReturnType = md.Body.ReturnValue.CodeTypeReference;
733 #else
734                                                 method.ReturnType = new CodeTypeReference (GetCodeTypeName (md.Body.ReturnValue.TypeName));
735 #endif
736                                         }
737                                         continue;
738                                 }
739
740                                 if (return_args)
741                                         args = new CodeExpression [md.Body.Parts.Count];
742
743                                 MessagePartDescriptionCollection parts = md.Body.Parts;
744                                 for (int i = 0; i < parts.Count; i++) {
745                                         ExportDataContract (parts [i]);
746
747                                         method.Parameters.Add (
748                                                 new CodeParameterDeclarationExpression (
749 #if USE_DATA_CONTRACT_IMPORTER
750                                                         parts [i].CodeTypeReference,
751 #else
752                                                         new CodeTypeReference (GetCodeTypeName (parts [i].TypeName)),
753 #endif
754                                                         parts [i].Name));
755
756                                         if (return_args)
757                                                 args [i] = new CodeArgumentReferenceExpression (parts [i].Name);
758                                 }
759                         }
760
761                         return args;
762                 }
763
764                 #endregion
765
766                 [MonoTODO]
767                 public CodeTypeReference GenerateServiceEndpoint (
768                         ServiceEndpoint endpoint,
769                         out ChannelEndpointElement channelElement)
770                 {
771                         throw new NotImplementedException ();
772                 }
773
774 #if USE_DATA_CONTRACT_IMPORTER
775                 void MergeCompileUnit (CodeCompileUnit from, CodeCompileUnit to)
776                 {
777                         if (from == to)
778                                 return;
779                         foreach (CodeNamespace fns in from.Namespaces) {
780                                 bool merged = false;
781                                 foreach (CodeNamespace tns in to.Namespaces)
782                                         if (fns.Name == tns.Name) {
783                                                 // namespaces are merged.
784                                                 MergeNamespace (fns, tns);
785                                                 merged = true;
786                                                 break;
787                                         }
788                                 if (!merged)
789                                         to.Namespaces.Add (fns);
790                         }
791                 }
792
793                 // existing type is skipped.
794                 void MergeNamespace (CodeNamespace from, CodeNamespace to)
795                 {
796                         foreach (CodeTypeDeclaration ftd in from.Types) {
797                                 bool skip = false;
798                                 foreach (CodeTypeDeclaration ttd in to.Types)
799                                         if (ftd.Name == ttd.Name) {
800                                                 skip = true;
801                                                 break;
802                                         }
803                                 if (!skip)
804                                         to.Types.Add (ftd);
805                         }
806                 }
807 #endif
808
809                 private void ExportDataContract (MessagePartDescription md)
810                 {
811 #if USE_DATA_CONTRACT_IMPORTER
812                         if (xsd_data_importer == null)
813                                 xsd_data_importer = md.Importer;
814 #else
815                         var mapping = md.XmlTypeMapping;
816
817                         if (mapping == null)
818                                 return;
819
820                         QName qname = new QName (mapping.TypeName, mapping.Namespace);
821                         if (imported_names.ContainsKey (qname))
822                                 return;
823
824                         CodeNamespace cns = new CodeNamespace ();
825
826                         XmlCodeExporter xce = new XmlCodeExporter (cns);
827                         xce.ExportTypeMapping (mapping);
828
829                         List <CodeTypeDeclaration> to_remove = new List <CodeTypeDeclaration> ();
830                         
831                         //Process the types just generated
832                         //FIXME: Iterate and assign the types to correct namespaces
833                         //At the end, add all those namespaces to the ccu
834                         foreach (CodeTypeDeclaration type in cns.Types) {
835                                 string ns = GetXmlNamespace (type);
836                                 if (ns == null)
837                                         //FIXME: do what here?
838                                         continue;
839
840                                 QName type_name = new QName (type.Name, ns);
841                                 if (imported_names.ContainsKey (type_name)) {
842                                         //Type got reemitted, so remove it!
843                                         to_remove.Add (type);
844                                         continue;
845                                 }
846                                 if (ns == ms_arrays_ns) {
847                                         //Do not emit arrays as an independent type.
848                                         to_remove.Add (type);
849                                         continue;
850                                 }
851
852                                 imported_names [type_name] = type_name;
853
854                                 type.Comments.Clear ();
855                                 //Custom Attributes
856                                 type.CustomAttributes.Clear ();
857
858                                 if (type.IsEnum)
859                                         continue;
860         
861                                 type.CustomAttributes.Add (
862                                         new CodeAttributeDeclaration (
863                                                 new CodeTypeReference ("System.CodeDom.Compiler.GeneratedCodeAttribute"),
864                                                 new CodeAttributeArgument (new CodePrimitiveExpression ("System.Runtime.Serialization")),
865                                                 new CodeAttributeArgument (new CodePrimitiveExpression ("3.0.0.0"))));
866                         
867                                 type.CustomAttributes.Add (
868                                         new CodeAttributeDeclaration (
869                                                 new CodeTypeReference ("System.Runtime.Serialization.DataContractAttribute")));
870
871                                 //BaseType and interface
872                                 type.BaseTypes.Add (new CodeTypeReference (typeof (object)));
873                                 type.BaseTypes.Add (new CodeTypeReference ("System.Runtime.Serialization.IExtensibleDataObject"));
874
875                                 foreach (CodeTypeMember mbr in type.Members) {
876                                         CodeMemberProperty p = mbr as CodeMemberProperty;
877                                         if (p == null)
878                                                 continue;
879
880                                         if ((p.Attributes & MemberAttributes.Public) == MemberAttributes.Public) {
881                                                 //FIXME: Clear all attributes or only XmlElementAttribute?
882                                                 p.CustomAttributes.Clear ();
883                                                 p.CustomAttributes.Add (new CodeAttributeDeclaration (
884                                                         new CodeTypeReference ("System.Runtime.Serialization.DataMemberAttribute")));
885
886                                                 p.Comments.Clear ();
887                                         }
888                                 }
889
890                                 //Fields
891                                 CodeMemberField field = new CodeMemberField (
892                                         new CodeTypeReference ("System.Runtime.Serialization.ExtensionDataObject"),
893                                         "extensionDataField");
894                                 field.Attributes = MemberAttributes.Private | MemberAttributes.Final;
895                                 type.Members.Add (field);
896
897                                 //Property 
898                                 CodeMemberProperty prop = new CodeMemberProperty ();
899                                 prop.Type = new CodeTypeReference ("System.Runtime.Serialization.ExtensionDataObject");
900                                 prop.Name = "ExtensionData";
901                                 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
902
903                                 //Get
904                                 prop.GetStatements.Add (new CodeMethodReturnStatement (
905                                         new CodeFieldReferenceExpression (
906                                         new CodeThisReferenceExpression (),
907                                         "extensionDataField")));
908
909                                 //Set
910                                 prop.SetStatements.Add (new CodeAssignStatement (
911                                         new CodeFieldReferenceExpression (
912                                         new CodeThisReferenceExpression (),
913                                         "extensionDataField"),
914                                         new CodePropertySetValueReferenceExpression ()));
915
916                                 type.Members.Add (prop);
917                         }
918
919                         foreach (CodeTypeDeclaration type in to_remove)
920                                 cns.Types.Remove (type);
921
922                         ccu.Namespaces.Add (cns);
923 #endif
924                 }
925                 
926                 private string GetXmlNamespace (CodeTypeDeclaration type)
927                 {
928                         foreach (CodeAttributeDeclaration attr in type.CustomAttributes) {
929                                 if (attr.Name == "System.Xml.Serialization.XmlTypeAttribute" ||
930                                         attr.Name == "System.Xml.Serialization.XmlRootAttribute") {
931
932                                         foreach (CodeAttributeArgument arg in attr.Arguments)
933                                                 if (arg.Name == "Namespace")
934                                                         return ((CodePrimitiveExpression)arg.Value).Value as string;
935
936                                         //Could not find Namespace arg!
937                                         return null;    
938                                 }
939                         }
940                         
941                         return null;
942                 }
943
944
945         }
946 }