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