Make generated type names unique.
[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                 CodeIdentifiers identifiers = new CodeIdentifiers ();
55                 Collection<MetadataConversionError> errors
56                         = new Collection<MetadataConversionError> ();
57                 Dictionary<string,string> nsmappings
58                         = new Dictionary<string,string> ();
59                 Dictionary<ContractDescription,Type> referenced_types
60                         = new Dictionary<ContractDescription,Type> ();
61                 ServiceContractGenerationOptions options;
62                 Dictionary<QName, QName> imported_names = null;
63                 ServiceContractGenerationContext contract_context;
64                 List<OPair> operation_contexts = new List<OPair> ();
65
66                 XsdDataContractImporter xsd_data_importer;
67
68                 public ServiceContractGenerator ()
69                         : this (null, null)
70                 {
71                 }
72
73                 public ServiceContractGenerator (CodeCompileUnit ccu)
74                         : this (ccu, null)
75                 {
76                 }
77
78                 public ServiceContractGenerator (ConfigurationType config)
79                         : this (null, config)
80                 {
81                 }
82
83                 public ServiceContractGenerator (CodeCompileUnit ccu, ConfigurationType config)
84                 {
85                         if (ccu == null)
86                                 this.ccu = new CodeCompileUnit ();
87                         else
88                                 this.ccu = ccu;
89                         this.config = config;
90                         Options |= ServiceContractGenerationOptions.ChannelInterface | 
91                                 ServiceContractGenerationOptions.ClientClass;
92                 }
93
94                 public ConfigurationType Configuration {
95                         get { return config; }
96                 }
97
98                 public Collection<MetadataConversionError> Errors {
99                         get { return errors; }
100                 }
101
102                 public Dictionary<string,string> NamespaceMappings {
103                         get { return nsmappings; }
104                 }
105
106                 public ServiceContractGenerationOptions Options {
107                         get { return options; }
108                         set { options = value; }
109                 }
110
111                 bool GenerateAsync {
112                         get { return GenerateEventBasedAsync || (options & ServiceContractGenerationOptions.AsynchronousMethods) != 0; }
113                 }
114
115                 bool GenerateEventBasedAsync {
116                         get { return (options & ServiceContractGenerationOptions.EventBasedAsynchronousMethods) != 0; }
117                 }
118
119                 public Dictionary<ContractDescription,Type> ReferencedTypes {
120                         get { return referenced_types; }
121                 }
122
123                 public CodeCompileUnit TargetCompileUnit {
124                         get { return ccu; }
125                 }
126
127                 [MonoTODO]
128                 public void GenerateBinding (Binding binding,
129                         out string bindingSectionName,
130                         out string configurationName)
131                 {
132                         throw new NotImplementedException ();
133                 }
134
135                 #region Service Contract Type
136
137                 // Those implementation classes are very likely to be split
138                 // into different classes.
139
140                 [MonoTODO]
141                 public CodeTypeReference GenerateServiceContractType (
142                         ContractDescription contractDescription)
143                 {
144                         CodeNamespace cns = GetNamespace (contractDescription.Namespace);
145                         imported_names = new Dictionary<QName, QName> ();
146                         var ret = ExportInterface (contractDescription, cns);
147
148                         // FIXME: handle duplex callback
149
150                         if ((Options & ServiceContractGenerationOptions.ChannelInterface) != 0)
151                                 GenerateChannelInterface (contractDescription, cns);
152
153                         if ((Options & ServiceContractGenerationOptions.ClientClass) != 0)
154                                 GenerateProxyClass (contractDescription, cns);
155
156                         if (xsd_data_importer != null)
157                                 MergeCompileUnit (xsd_data_importer.CodeCompileUnit, ccu);
158
159                         // Process extensions. Class first, then methods.
160                         // (built-in ones must present before processing class extensions).
161                         foreach (var cb in contractDescription.Behaviors) {
162                                 var gex = cb as IServiceContractGenerationExtension;
163                                 if (gex != null)
164                                         gex.GenerateContract (contract_context);
165                         }
166                         foreach (var opair in operation_contexts)
167                                 opair.Key.GenerateOperation (opair.Value);
168
169                         return ret;
170                 }
171
172                 CodeNamespace GetNamespace (string ns)
173                 {
174                         if (ns == null)
175                                 ns = String.Empty;
176                         foreach (CodeNamespace cns in ccu.Namespaces)
177                                 if (cns.Name == ns)
178                                         return cns;
179                         CodeNamespace ncns = new CodeNamespace ();
180                         //ncns.Name = ns;
181                         ccu.Namespaces.Add (ncns);
182                         return ncns;
183                 }
184
185                 CodeTypeDeclaration GetTypeDeclaration (CodeNamespace cns, string name)
186                 {
187                         foreach (CodeTypeDeclaration type in cns.Types)
188                                 if (type.Name == name)
189                                         return type;
190                         return null;
191                 }
192
193                 void GenerateProxyClass (ContractDescription cd, CodeNamespace cns)
194                 {
195                         string name = cd.Name + "Client";
196                         if (name [0] == 'I')
197                                 name = name.Substring (1);
198                         name = identifiers.AddUnique (name, null);
199                         CodeTypeDeclaration type = GetTypeDeclaration (cns, name);
200                         if (type != null)
201                                 return; // already imported
202                         CodeTypeReference clientBase = new CodeTypeReference (typeof (ClientBase<>));
203                         clientBase.TypeArguments.Add (new CodeTypeReference (cd.Name));
204                         type = new CodeTypeDeclaration (name);
205                         cns.Types.Add (type);
206                         type.TypeAttributes = TypeAttributes.Public;
207                         type.BaseTypes.Add (clientBase);
208                         type.BaseTypes.Add (new CodeTypeReference (cd.Name));
209
210                         // .ctor()
211                         CodeConstructor ctor = new CodeConstructor ();
212                         ctor.Attributes = MemberAttributes.Public;
213                         type.Members.Add (ctor);
214
215                         // .ctor(string endpointConfigurationName)
216                         ctor = new CodeConstructor ();
217                         ctor.Attributes = MemberAttributes.Public;
218                         ctor.Parameters.Add (
219                                 new CodeParameterDeclarationExpression (
220                                         new CodeTypeReference (typeof (string)), "endpointConfigurationName"));
221                         ctor.BaseConstructorArgs.Add (
222                                 new CodeArgumentReferenceExpression ("endpointConfigurationName"));
223                         type.Members.Add (ctor);
224
225                         // .ctor(string endpointConfigurationName, string remoteAddress)
226                         ctor = new CodeConstructor ();
227                         ctor.Attributes = MemberAttributes.Public;
228                         ctor.Parameters.Add (
229                                 new CodeParameterDeclarationExpression (
230                                         new CodeTypeReference (typeof (string)), "endpointConfigurationName"));
231                         ctor.Parameters.Add (
232                                 new CodeParameterDeclarationExpression (
233                                         new CodeTypeReference (typeof (string)), "remoteAddress"));
234                         ctor.BaseConstructorArgs.Add (
235                                 new CodeArgumentReferenceExpression ("endpointConfigurationName"));
236                         ctor.BaseConstructorArgs.Add (
237                                 new CodeArgumentReferenceExpression ("remoteAddress"));
238                         type.Members.Add (ctor);
239
240                         // .ctor(string endpointConfigurationName, EndpointAddress remoteAddress)
241                         ctor = new CodeConstructor ();
242                         ctor.Attributes = MemberAttributes.Public;
243                         ctor.Parameters.Add (
244                                 new CodeParameterDeclarationExpression (
245                                         new CodeTypeReference (typeof (string)), "endpointConfigurationName"));
246                         ctor.Parameters.Add (
247                                 new CodeParameterDeclarationExpression (
248                                         new CodeTypeReference (typeof (EndpointAddress)), "remoteAddress"));
249                         ctor.BaseConstructorArgs.Add (
250                                 new CodeArgumentReferenceExpression ("endpointConfigurationName"));
251                         ctor.BaseConstructorArgs.Add (
252                                 new CodeArgumentReferenceExpression ("remoteAddress"));
253                         type.Members.Add (ctor);
254
255                         // .ctor(Binding,EndpointAddress)
256                         ctor = new CodeConstructor ();
257                         ctor.Attributes = MemberAttributes.Public;
258                         ctor.Parameters.Add (
259                                 new CodeParameterDeclarationExpression (
260                                         new CodeTypeReference (typeof (Binding)), "binding"));
261                         ctor.Parameters.Add (
262                                 new CodeParameterDeclarationExpression (
263                                         new CodeTypeReference (typeof (EndpointAddress)), "endpoint"));
264                         ctor.BaseConstructorArgs.Add (
265                                 new CodeArgumentReferenceExpression ("binding"));
266                         ctor.BaseConstructorArgs.Add (
267                                 new CodeArgumentReferenceExpression ("endpoint"));
268                         type.Members.Add (ctor);
269
270                         // service contract methods
271                         AddImplementationClientMethods (type, cd);
272
273                         if (GenerateEventBasedAsync)
274                                 foreach (var od in cd.Operations)
275                                         GenerateEventBasedAsyncSupport (type, od, cns);
276                 }
277
278                 void GenerateChannelInterface (ContractDescription cd, CodeNamespace cns)
279                 {
280                         string name = cd.Name + "Channel";
281                         name = identifiers.AddUnique (name, null);
282                         CodeTypeDeclaration type = GetTypeDeclaration (cns, name);
283                         if (type != null)
284                                 return;
285
286                         type = new CodeTypeDeclaration ();
287                         type.Name = name;
288                         type.TypeAttributes = TypeAttributes.Interface | TypeAttributes.Public;
289                         cns.Types.Add (type);
290                         
291                         type.BaseTypes.Add (ExportInterface (cd, cns));
292                         type.BaseTypes.Add (new CodeTypeReference (typeof (System.ServiceModel.IClientChannel)));
293                 }
294
295                 CodeTypeReference ExportInterface (ContractDescription cd, CodeNamespace cns)
296                 {
297                         CodeTypeDeclaration type = GetTypeDeclaration (cns, cd.Name);
298                         if (type != null)
299                                 return new CodeTypeReference (type.Name);
300                         type = new CodeTypeDeclaration ();
301                         type.TypeAttributes = TypeAttributes.Interface;
302                         type.TypeAttributes |= TypeAttributes.Public;
303                         cns.Types.Add (type);
304                         type.Name = identifiers.AddUnique (cd.Name, null);
305                         CodeAttributeDeclaration ad = 
306                                 new CodeAttributeDeclaration (
307                                         new CodeTypeReference (
308                                                 typeof (ServiceContractAttribute)));
309                         ad.Arguments.Add (new CodeAttributeArgument ("Namespace", new CodePrimitiveExpression (cd.Namespace)));
310                         type.CustomAttributes.Add (ad);
311                         contract_context = new ServiceContractGenerationContext (this, cd, type);
312
313                         AddOperationMethods (type, cd);
314
315                         return new CodeTypeReference (type.Name);
316                 }
317
318                 void AddBeginAsyncArgs (CodeMemberMethod cm)
319                 {
320                         var acb = new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (AsyncCallback)), "asyncCallback");
321                         cm.Parameters.Add (acb);
322                         var us = new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (object)), "userState");
323                         cm.Parameters.Add (us);
324                 }
325
326                 void AddOperationMethods (CodeTypeDeclaration type, ContractDescription cd)
327                 {
328                         foreach (OperationDescription od in cd.Operations) {
329                                 CodeMemberMethod syncMethod = null, beginMethod = null, endMethod = null;
330
331                                 CodeTypeReference returnTypeFromMessageContract = null;
332                                 syncMethod = GenerateOperationMethod (type, cd, od, false, out returnTypeFromMessageContract);
333                                 type.Members.Add (syncMethod);
334
335                                 if (GenerateAsync) {
336                                         beginMethod = GenerateOperationMethod (type, cd, od, true, out returnTypeFromMessageContract);
337                                         type.Members.Add (beginMethod);
338
339                                         var cm = new CodeMemberMethod ();
340                                         type.Members.Add (cm);
341                                         cm.Name = "End" + od.Name;
342                                         endMethod = cm;
343
344                                         var res = new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (IAsyncResult)), "result");
345                                         cm.Parameters.Add (res);
346
347                                         if (od.SyncMethod != null) // FIXME: it depends on sync method!
348                                                 cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
349                                         else
350                                                 cm.ReturnType = returnTypeFromMessageContract;
351                                 }
352
353                                 foreach (var ob in od.Behaviors) {
354                                         var gex = ob as IOperationContractGenerationExtension;
355                                         if (gex != null)
356                                                 operation_contexts.Add (new OPair (gex, new OperationContractGenerationContext (this, contract_context, od, type, syncMethod, beginMethod, endMethod)));
357                                 }
358                         }
359                 }
360
361                 CodeMemberMethod GenerateOperationMethod (CodeTypeDeclaration type, ContractDescription cd, OperationDescription od, bool async, out CodeTypeReference returnType)
362                 {
363                         CodeMemberMethod cm = new CodeMemberMethod ();
364
365                         if (async)
366                                 cm.Name = "Begin" + od.Name;
367                         else
368                                 cm.Name = od.Name;
369
370                         if (od.SyncMethod != null) {
371                                 ExportParameters (cm, od.SyncMethod.GetParameters ());
372                                 if (async) {
373                                         AddBeginAsyncArgs (cm);
374                                         cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult));
375                                 }
376                                 else
377                                         cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
378                                 returnType = new CodeTypeReference (od.SyncMethod.ReturnType);
379                         } else {
380                                 ExportMessages (od.Messages, cm, false);
381                                 returnType = cm.ReturnType;
382                                 if (async) {
383                                         AddBeginAsyncArgs (cm);
384                                         cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult));
385                                 }
386                         }
387
388                         // [OperationContract (Action = "...", ReplyAction = "..")]
389                         var ad = new CodeAttributeDeclaration (new CodeTypeReference (typeof (OperationContractAttribute)));
390                         foreach (MessageDescription md in od.Messages) {
391                                 if (md.Direction == MessageDirection.Input)
392                                         ad.Arguments.Add (new CodeAttributeArgument ("Action", new CodePrimitiveExpression (md.Action)));
393                                 else
394                                         ad.Arguments.Add (new CodeAttributeArgument ("ReplyAction", new CodePrimitiveExpression (md.Action)));
395                         }
396                         if (async)
397                                 ad.Arguments.Add (new CodeAttributeArgument ("AsyncPattern", new CodePrimitiveExpression (true)));
398                         cm.CustomAttributes.Add (ad);
399
400                         return cm;
401                 }
402
403                 void ExportParameters (CodeMemberMethod method, ParameterInfo [] parameters)
404                 {
405                         foreach (ParameterInfo pi in parameters)
406                                 method.Parameters.Add (
407                                         new CodeParameterDeclarationExpression (
408                                                 new CodeTypeReference (pi.ParameterType),
409                                                 pi.Name));
410                 }
411
412                 void AddImplementationClientMethods (CodeTypeDeclaration type, ContractDescription cd)
413                 {
414                         foreach (OperationDescription od in cd.Operations) {
415                                 CodeMemberMethod cm;
416                                 CodeTypeReference returnTypeFromMessageContract = null;
417                                 cm = GenerateImplementationClientMethod (type, cd, od, false, out returnTypeFromMessageContract);
418                                 type.Members.Add (cm);
419
420                                 if (!GenerateAsync)
421                                         continue;
422
423                                 cm = GenerateImplementationClientMethod (type, cd, od, true, out returnTypeFromMessageContract);
424                                 type.Members.Add (cm);
425
426                                 // EndXxx() implementation
427
428                                 cm = new CodeMemberMethod ();
429                                 cm.Attributes = MemberAttributes.Public 
430                                                 | MemberAttributes.Final;
431                                 type.Members.Add (cm);
432                                 cm.Name = "End" + od.Name;
433
434                                 var res = new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (IAsyncResult)), "result");
435                                 cm.Parameters.Add (res);
436
437                                 if (od.SyncMethod != null) // FIXME: it depends on sync method!
438                                         cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
439                                 else
440                                         cm.ReturnType = returnTypeFromMessageContract;
441
442                                 string resultArgName = "result";
443                                 if (od.EndMethod != null)
444                                         resultArgName = od.EndMethod.GetParameters () [0].Name;
445
446                                 var call = new CodeMethodInvokeExpression (
447                                         new CodePropertyReferenceExpression (
448                                                 new CodeBaseReferenceExpression (),
449                                                 "Channel"),
450                                         cm.Name,
451                                         new CodeArgumentReferenceExpression (resultArgName));
452
453                                 if (cm.ReturnType.BaseType == "System.Void")
454                                         cm.Statements.Add (new CodeExpressionStatement (call));
455                                 else
456                                         cm.Statements.Add (new CodeMethodReturnStatement (call));
457                         }
458                 }
459
460                 CodeMemberMethod GenerateImplementationClientMethod (CodeTypeDeclaration type, ContractDescription cd, OperationDescription od, bool async, out CodeTypeReference returnTypeFromMessageContract)
461                 {
462                         CodeMemberMethod cm = new CodeMemberMethod ();
463                         if (async)
464                                 cm.Name = "Begin" + od.Name;
465                         else
466                                 cm.Name = od.Name;
467                         cm.Attributes = MemberAttributes.Public | MemberAttributes.Final;
468                         returnTypeFromMessageContract = null;
469
470                         List<CodeExpression> args = new List<CodeExpression> ();
471                         if (od.SyncMethod != null) {
472                                 ParameterInfo [] pars = od.SyncMethod.GetParameters ();
473                                 ExportParameters (cm, pars);
474                                 cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
475                                 int i = 0;
476                                 foreach (ParameterInfo pi in pars)
477                                         args.Add (new CodeArgumentReferenceExpression (pi.Name));
478                         } else {
479                                 args.AddRange (ExportMessages (od.Messages, cm, true));
480                                 returnTypeFromMessageContract = cm.ReturnType;
481                                 if (async) {
482                                         AddBeginAsyncArgs (cm);
483                                         cm.ReturnType = new CodeTypeReference (typeof (IAsyncResult));
484                                 }
485                         }
486                         if (async) {
487                                 args.Add (new CodeArgumentReferenceExpression ("asyncCallback"));
488                                 args.Add (new CodeArgumentReferenceExpression ("userState"));
489                         }
490
491                         CodeExpression call = new CodeMethodInvokeExpression (
492                                 new CodePropertyReferenceExpression (
493                                         new CodeBaseReferenceExpression (),
494                                         "Channel"),
495                                 cm.Name,
496                                 args.ToArray ());
497
498                         if (cm.ReturnType.BaseType == "System.Void")
499                                 cm.Statements.Add (new CodeExpressionStatement (call));
500                         else
501                                 cm.Statements.Add (new CodeMethodReturnStatement (call));
502                         return cm;
503                 }
504
505                 CodeMemberMethod FindByName (CodeTypeDeclaration type, string name)
506                 {
507                         foreach (var m in type.Members) {
508                                 var method = m as CodeMemberMethod;
509                                 if (method != null && method.Name == name)
510                                         return method;
511                         }
512                         return null;
513                 }
514
515                 void GenerateEventBasedAsyncSupport (CodeTypeDeclaration type, OperationDescription od, CodeNamespace cns)
516                 {
517                         var method = FindByName (type, od.Name) ?? FindByName (type, "Begin" + od.Name);
518                         var endMethod = method.Name == od.Name ? null : FindByName (type, "End" + od.Name);
519                         bool methodAsync = method.Name.StartsWith ("Begin", StringComparison.Ordinal);
520
521                         var thisExpr = new CodeThisReferenceExpression ();
522                         var baseExpr = new CodeBaseReferenceExpression ();
523                         var nullExpr = new CodePrimitiveExpression (null);
524                         var asyncResultType = new CodeTypeReference (typeof (IAsyncResult));
525
526                         // OnBeginXxx() implementation
527                         var cm = new CodeMemberMethod () {
528                                 Name = "OnBegin" + od.Name,
529                                 Attributes = MemberAttributes.Private | MemberAttributes.Final,
530                                 ReturnType = asyncResultType
531                                 };
532                         type.Members.Add (cm);
533
534                         AddMethodParam (cm, typeof (object []), "args");
535                         AddMethodParam (cm, typeof (AsyncCallback), "asyncCallback");
536                         AddMethodParam (cm, typeof (object), "userState");
537
538                         var call = new CodeMethodInvokeExpression (
539                                 thisExpr,
540                                 "Begin" + od.Name);
541                         for (int idx = 0; idx < method.Parameters.Count - (methodAsync ? 2 : 0); idx++) {
542                                 var p = method.Parameters [idx];
543                                 cm.Statements.Add (new CodeVariableDeclarationStatement (p.Type, p.Name, new CodeCastExpression (p.Type, new CodeArrayIndexerExpression (new CodeArgumentReferenceExpression ("args"), new CodePrimitiveExpression (idx)))));
544                                 call.Parameters.Add (new CodeVariableReferenceExpression (p.Name));
545                         }
546                         call.Parameters.Add (new CodeArgumentReferenceExpression ("asyncCallback"));
547                         call.Parameters.Add (new CodeArgumentReferenceExpression ("userState"));
548                         cm.Statements.Add (new CodeMethodReturnStatement (call));
549
550                         // OnEndXxx() implementation
551                         cm = new CodeMemberMethod () {
552                                 Name = "OnEnd" + od.Name,
553                                 Attributes = MemberAttributes.Private | MemberAttributes.Final,
554                                 ReturnType = new CodeTypeReference (typeof (object [])) };
555                         type.Members.Add (cm);
556
557                         AddMethodParam (cm, typeof (IAsyncResult), "result");
558
559                         var outArgRefs = new List<CodeVariableReferenceExpression> ();
560
561                         for (int idx = 0; idx < method.Parameters.Count; idx++) {
562                                 var p = method.Parameters [idx];
563                                 if (p.Direction != FieldDirection.In) {
564                                         cm.Statements.Add (new CodeVariableDeclarationStatement (p.Type, p.Name));
565                                         outArgRefs.Add (new CodeVariableReferenceExpression (p.Name)); // FIXME: should this work? They need "out" or "ref" modifiers.
566                                 }
567                         }
568
569                         call = new CodeMethodInvokeExpression (
570                                 thisExpr,
571                                 "End" + od.Name,
572                                 new CodeArgumentReferenceExpression ("result"));
573                         call.Parameters.AddRange (outArgRefs.Cast<CodeExpression> ().ToArray ()); // questionable
574
575                         cm.Statements.Add (new CodeVariableDeclarationStatement (typeof (object), "__ret", call));
576                         var retCreate = new CodeArrayCreateExpression (typeof (object));
577                         retCreate.Initializers.Add (new CodeVariableReferenceExpression ("__ret"));
578                         foreach (var outArgRef in outArgRefs)
579                                 retCreate.Initializers.Add (new CodeVariableReferenceExpression (outArgRef.VariableName));
580
581                         cm.Statements.Add (new CodeMethodReturnStatement (retCreate));
582
583                         // OnXxxCompleted() implementation
584                         cm = new CodeMemberMethod () {
585                                 Name = "On" + od.Name + "Completed",
586                                 Attributes = MemberAttributes.Private | MemberAttributes.Final };
587                         type.Members.Add (cm);
588
589                         AddMethodParam (cm, typeof (object), "state");
590
591                         string argsname = identifiers.AddUnique (od.Name + "CompletedEventArgs", null);
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 (argsname),
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 (argsname);
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.Direction == MessageDirection.Output) {
729                                         if (md.Body.ReturnValue != null) {
730                                                 ExportDataContract (md.Body.ReturnValue);
731                                                 method.ReturnType = md.Body.ReturnValue.CodeTypeReference;
732                                         }
733                                         continue;
734                                 }
735
736                                 if (return_args)
737                                         args = new CodeExpression [md.Body.Parts.Count];
738
739                                 MessagePartDescriptionCollection parts = md.Body.Parts;
740                                 for (int i = 0; i < parts.Count; i++) {
741                                         ExportDataContract (parts [i]);
742
743                                         method.Parameters.Add (
744                                                 new CodeParameterDeclarationExpression (
745                                                         parts [i].CodeTypeReference,
746                                                         parts [i].Name));
747
748                                         if (return_args)
749                                                 args [i] = new CodeArgumentReferenceExpression (parts [i].Name);
750                                 }
751                         }
752
753                         return args;
754                 }
755
756                 #endregion
757
758                 [MonoTODO]
759                 public CodeTypeReference GenerateServiceEndpoint (
760                         ServiceEndpoint endpoint,
761                         out ChannelEndpointElement channelElement)
762                 {
763                         throw new NotImplementedException ();
764                 }
765
766                 void MergeCompileUnit (CodeCompileUnit from, CodeCompileUnit to)
767                 {
768                         if (from == to)
769                                 return;
770                         foreach (CodeNamespace fns in from.Namespaces) {
771                                 bool merged = false;
772                                 foreach (CodeNamespace tns in to.Namespaces)
773                                         if (fns.Name == tns.Name) {
774                                                 // namespaces are merged.
775                                                 MergeNamespace (fns, tns);
776                                                 merged = true;
777                                                 break;
778                                         }
779                                 if (!merged)
780                                         to.Namespaces.Add (fns);
781                         }
782                 }
783
784                 // existing type is skipped.
785                 void MergeNamespace (CodeNamespace from, CodeNamespace to)
786                 {
787                         foreach (CodeTypeDeclaration ftd in from.Types) {
788                                 bool skip = false;
789                                 foreach (CodeTypeDeclaration ttd in to.Types)
790                                         if (ftd.Name == ttd.Name) {
791                                                 skip = true;
792                                                 break;
793                                         }
794                                 if (!skip)
795                                         to.Types.Add (ftd);
796                         }
797                 }
798
799                 private void ExportDataContract (MessagePartDescription md)
800                 {
801                         if (xsd_data_importer == null)
802                                 xsd_data_importer = md.Importer;
803                 }
804                 
805                 private string GetXmlNamespace (CodeTypeDeclaration type)
806                 {
807                         foreach (CodeAttributeDeclaration attr in type.CustomAttributes) {
808                                 if (attr.Name == "System.Xml.Serialization.XmlTypeAttribute" ||
809                                         attr.Name == "System.Xml.Serialization.XmlRootAttribute") {
810
811                                         foreach (CodeAttributeArgument arg in attr.Arguments)
812                                                 if (arg.Name == "Namespace")
813                                                         return ((CodePrimitiveExpression)arg.Value).Value as string;
814
815                                         //Could not find Namespace arg!
816                                         return null;    
817                                 }
818                         }
819                         
820                         return null;
821                 }
822
823
824         }
825 }