43f4e405bd4a299b92ec11c87c1df71bbce234fc
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Description / ProtocolImporter.cs
1 // 
2 // System.Web.Services.Description.ProtocolImporter.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //   Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // Copyright (C) Tim Coleman, 2002
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.CodeDom;
34 using System.CodeDom.Compiler;
35 using System.Web.Services;
36 using System.Web.Services.Protocols;
37 using System.Xml.Serialization;
38 using System.Xml;
39 using System.Xml.Schema;
40 using System.Collections;
41 using System.Configuration;
42
43 namespace System.Web.Services.Description {
44         public abstract class ProtocolImporter {
45
46                 #region Fields
47
48                 Binding binding;
49                 string className;
50                 CodeIdentifiers classNames;
51                 CodeNamespace codeNamespace;
52                 CodeCompileUnit codeCompileUnit;
53                 CodeTypeDeclaration codeTypeDeclaration;
54                 Message inputMessage;
55                 string methodName;
56                 Operation operation;
57                 OperationBinding operationBinding;
58                 Message outputMessage;          
59                 Port port;
60                 PortType portType;
61                 string protocolName;
62                 Service service;
63                 ServiceDescriptionImportWarnings warnings = (ServiceDescriptionImportWarnings)0;        
64                 ServiceDescriptionImporter descriptionImporter;
65                 ImportInfo iinfo;
66                 XmlSchemas xmlSchemas;
67                 XmlSchemas soapSchemas;
68                 
69 #if NET_2_0
70                 ArrayList asyncTypes = new ArrayList ();
71 #endif
72
73                 #endregion // Fields
74
75                 #region Constructors
76         
77                 protected ProtocolImporter ()
78                 {
79                 }
80                 
81                 #endregion // Constructors
82
83                 #region Properties
84
85                 [MonoTODO]
86                 public XmlSchemas AbstractSchemas {
87                         get { return descriptionImporter.Schemas; }
88                 }
89
90                 public Binding Binding {
91                         get { return binding; }
92                 }
93
94                 public string ClassName {
95                         get { return className; }
96                 }
97
98                 public CodeIdentifiers ClassNames {
99                         get { return classNames; }
100                 }
101
102                 public CodeNamespace CodeNamespace {
103                         get { return codeNamespace; }
104                 }
105
106                 public CodeTypeDeclaration CodeTypeDeclaration {
107                         get { return codeTypeDeclaration; }
108                 }
109
110                 [MonoTODO]
111                 public XmlSchemas ConcreteSchemas {
112                         get { return descriptionImporter.Schemas; }
113                 }
114
115                 public Message InputMessage {
116                         get { return inputMessage; }
117                 }
118
119                 public string MethodName {
120                         get { return methodName; }
121                 }
122
123                 public Operation Operation {
124                         get { return operation; }
125                 }
126
127                 public OperationBinding OperationBinding {
128                         get { return operationBinding; }
129                 }
130
131                 public Message OutputMessage {
132                         get { return outputMessage; }
133                 }
134
135                 public Port Port {
136                         get { return port; }
137                 }
138
139                 public PortType PortType {
140                         get { return portType; }
141                 }
142
143                 public abstract string ProtocolName {
144                         get; 
145                 }
146
147                 public XmlSchemas Schemas {
148                         get { return descriptionImporter.Schemas; }
149                 }
150
151                 public Service Service {
152                         get { return service; } 
153                 }
154
155                 public ServiceDescriptionCollection ServiceDescriptions {
156                         get { return descriptionImporter.ServiceDescriptions; }
157                 }
158
159                 public ServiceDescriptionImportStyle Style {
160                         get { return descriptionImporter.Style; }
161                 }
162
163                 public ServiceDescriptionImportWarnings Warnings {
164                         get { return warnings; }
165                         set { warnings = value; }
166                 }
167                 
168                 internal ImportInfo ImportInfo
169                 {
170                         get { return iinfo; }
171                 }
172                 
173                 internal XmlSchemas LiteralSchemas
174                 {
175                         get { return xmlSchemas; }
176                 }
177                 
178                 internal XmlSchemas EncodedSchemas
179                 {
180                         get { return soapSchemas; }
181                 }
182                 
183 #if NET_2_0
184                 internal CodeGenerationOptions CodeGenerationOptions {
185                         get { return descriptionImporter.CodeGenerationOptions; }
186                 }
187                 
188                 internal ICodeGenerator CodeGenerator {
189                         get { return descriptionImporter.CodeGenerator; }
190                 }
191
192                 internal ImportContext ImportContext {
193                         get { return descriptionImporter.Context; }
194                 }
195 #endif
196
197                 #endregion // Properties
198
199                 #region Methods
200                 
201                 internal bool Import (ServiceDescriptionImporter descriptionImporter, CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, ArrayList importInfo)
202                 {
203                         this.descriptionImporter = descriptionImporter;
204                         this.classNames = new CodeIdentifiers();;
205                         this.codeNamespace = codeNamespace;
206                         this.codeCompileUnit = codeCompileUnit;
207
208                         warnings = (ServiceDescriptionImportWarnings) 0;
209                         
210                         bool found = false;
211                         
212                         ClasifySchemas (importInfo);
213
214                         BeginNamespace ();
215                         
216                         foreach (ImportInfo info in importInfo)
217                         {
218                                 foreach (Service service in info.ServiceDescription.Services)
219                                 {
220                                         this.service = service;
221                                         int bindingCount = 0;
222                                         foreach (Port port in service.Ports)
223                                         {
224                                                 binding = ServiceDescriptions.GetBinding (port.Binding);
225                                                 if (IsBindingSupported ()) bindingCount ++;
226                                         }
227                                         
228                                         foreach (Port port in service.Ports)
229                                         {
230                                                 this.iinfo = info;
231                                                 this.port = port;
232                                                 binding = ServiceDescriptions.GetBinding (port.Binding);
233                                                 if (!IsBindingSupported ()) continue;
234                                                 
235                                                 found = true;
236                                                 ImportPortBinding (bindingCount > 1);
237                                         }
238                                 }
239                         }
240                         
241                         if (!found)
242                         {
243                                 // Looks like MS.NET generates classes for all bindings if
244                                 // no services are present
245                                 
246                                 foreach (ImportInfo info in importInfo)
247                                 {
248                                         this.iinfo = info;
249                                         foreach (Binding b in info.ServiceDescription.Bindings)
250                                         {
251                                                 this.binding = b;
252                                                 this.service = null;
253                                                 this.port = null;
254                                                 if (!IsBindingSupported ()) continue;
255                                                 found = true;
256                                                 ImportPortBinding (true);
257                                         }
258                                 }
259                         }
260
261                         EndNamespace ();
262                         
263                         if (!found) warnings = ServiceDescriptionImportWarnings.NoCodeGenerated;
264                         return true;
265                 }
266
267                 void ImportPortBinding (bool multipleBindings)
268                 {
269                         if (port != null) {
270                                 if (multipleBindings) className = port.Name;
271                                 else className = service.Name;
272                         }
273                         else
274                                 className = binding.Name;
275                         
276                         className = classNames.AddUnique (CodeIdentifier.MakeValid (className), port);
277                         className = className.Replace ("_x0020_", "");  // MS.NET seems to do this
278                         
279                         try
280                         {
281                                 portType = ServiceDescriptions.GetPortType (binding.Type);
282                                 if (portType == null) throw new Exception ("Port type not found: " + binding.Type);
283
284                                 CodeTypeDeclaration codeClass = BeginClass ();
285                                 codeTypeDeclaration = codeClass;
286                                 AddCodeType (codeClass, port != null ? port.Documentation : null);
287                                 codeClass.Attributes = MemberAttributes.Public;
288                         
289                                 if (service != null && service.Documentation != null && service.Documentation != "")
290                                         AddComments (codeClass, service.Documentation);
291
292                                 if (Style == ServiceDescriptionImportStyle.Client) {
293                                         CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Diagnostics.DebuggerStepThroughAttribute");
294                                         AddCustomAttribute (codeClass, att, true);
295         
296                                         att = new CodeAttributeDeclaration ("System.ComponentModel.DesignerCategoryAttribute");
297                                         att.Arguments.Add (GetArg ("code"));
298                                         AddCustomAttribute (codeClass, att, true);
299                                 }
300                                 else
301                                         codeClass.TypeAttributes = System.Reflection.TypeAttributes.Abstract | System.Reflection.TypeAttributes.Public;
302
303                                 if (binding.Operations.Count == 0) {
304                                         warnings |= ServiceDescriptionImportWarnings.NoMethodsGenerated;
305                                         return;
306                                 }
307                                 
308                                 foreach (OperationBinding oper in binding.Operations) 
309                                 {
310                                         operationBinding = oper;
311                                         operation = FindPortOperation ();
312                                         if (operation == null) throw new Exception ("Operation " + operationBinding.Name + " not found in portType " + PortType.Name);
313
314                                         foreach (OperationMessage omsg in operation.Messages)
315                                         {
316                                                 Message msg = ServiceDescriptions.GetMessage (omsg.Message);
317                                                 if (msg == null) throw new Exception ("Message not found: " + omsg.Message);
318                                                 
319                                                 if (omsg is OperationInput)
320                                                         inputMessage = msg;
321                                                 else
322                                                         outputMessage = msg;
323                                         }
324                                         
325                                         CodeMemberMethod method = GenerateMethod ();
326                                         
327                                         if (method != null)
328                                         {
329                                                 methodName = method.Name;
330                                                 if (operation.Documentation != null && operation.Documentation != "")
331                                                         AddComments (method, operation.Documentation);
332 #if NET_2_0
333                                                 if (Style == ServiceDescriptionImportStyle.Client)
334                                                         AddAsyncMembers (method.Name, method);
335 #endif
336                                         }
337                                 }
338                                 
339 #if NET_2_0
340                         if (Style == ServiceDescriptionImportStyle.Client)
341                                 AddAsyncTypes ();
342 #endif
343                                 
344                                 EndClass ();
345                         }
346                         catch (InvalidOperationException ex)
347                         {
348                                 warnings |= ServiceDescriptionImportWarnings.NoCodeGenerated;
349                                 UnsupportedBindingWarning (ex.Message);
350                         }
351                 }
352
353                 Operation FindPortOperation ()
354                 {
355                         string inMessage = null;
356                         string outMessage = null;
357                         int numMsg = 1;
358                         
359                         if (operationBinding.Input == null) throw new InvalidOperationException ("Input operation binding not found");
360                         inMessage = (operationBinding.Input.Name != null) ? operationBinding.Input.Name : operationBinding.Name;
361                                 
362                         if (operationBinding.Output != null) {
363                                 outMessage = (operationBinding.Output.Name != null) ? operationBinding.Output.Name : operationBinding.Name;
364                                 numMsg++;
365                         }
366                         
367                         string operName = operationBinding.Name;
368                         
369                         Operation foundOper = null;
370                         foreach (Operation oper in PortType.Operations)
371                         {
372                                 if (oper.Name == operName)
373                                 {
374                                         int hits = 0;
375                                         foreach (OperationMessage omsg in oper.Messages)
376                                         {
377                                                 if (omsg is OperationInput && GetOperMessageName (omsg, operName) == inMessage) hits++;
378                                                 if (omsg is OperationOutput && GetOperMessageName (omsg, operName) == outMessage) hits++;
379                                         }
380                                         if (hits == numMsg) return oper;
381                                         foundOper = oper;
382                                 }
383                         }
384                         return foundOper;
385                 }
386                 
387                 string GetOperMessageName (OperationMessage msg, string operName)
388                 {
389                         if (msg.Name == null) return operName;
390                         else return msg.Name;
391                 }
392                 
393                 internal void GenerateServiceUrl (string location, CodeStatementCollection stms)
394                 {
395                         if (ImportInfo.AppSettingUrlKey == null || ImportInfo.AppSettingUrlKey == string.Empty) {
396                                 if (location != null) {
397                                         CodeExpression ce = new CodeFieldReferenceExpression (new CodeThisReferenceExpression(), "Url");
398                                         CodeAssignStatement cas = new CodeAssignStatement (ce, new CodePrimitiveExpression (location));
399                                         stms.Add (cas);
400                                 }
401                         }
402                         else
403                         {
404                                 CodeExpression prop = new CodePropertyReferenceExpression (new CodeTypeReferenceExpression ("System.Configuration.ConfigurationSettings"), "AppSettings");
405                                 prop = new CodeIndexerExpression (prop, new CodePrimitiveExpression (ImportInfo.AppSettingUrlKey));
406                                 stms.Add (new CodeVariableDeclarationStatement (typeof(string), "urlSetting", prop));
407                                 
408                                 CodeExpression urlSetting = new CodeVariableReferenceExpression ("urlSetting");
409                                 CodeExpression thisUrl = new CodeFieldReferenceExpression (new CodeThisReferenceExpression(), "Url");
410                                 
411                                 CodeStatement[] trueStms = new CodeStatement [1];
412                                 CodeExpression ce = urlSetting;
413                                 CodeExpression cond = new CodeBinaryOperatorExpression (urlSetting, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression (null));
414                                 
415                                 if (ImportInfo.AppSettingBaseUrl != null)
416                                         ce = new CodeMethodInvokeExpression (new CodeTypeReferenceExpression (typeof(string)), "Concat", ce, new CodePrimitiveExpression (ImportInfo.AppSettingBaseUrl));
417                                 trueStms [0] = new CodeAssignStatement (thisUrl, ce);
418                                 
419                                 if (location != null) {
420                                         CodeStatement[] falseStms = new CodeStatement [1];
421                                         falseStms [0] = new CodeAssignStatement (thisUrl, new CodePrimitiveExpression (location));
422                                         stms.Add (new CodeConditionStatement (cond, trueStms, falseStms));
423                                 }
424                                 else
425                                         stms.Add (new CodeConditionStatement (cond, trueStms));
426                         }
427                 }
428                 
429                 void ClasifySchemas (ArrayList importInfo)
430                 {
431                         // I don't like this, but I could not find any other way of clasifying
432                         // schemas between encoded and literal.
433                         
434                         xmlSchemas = new XmlSchemas ();
435                         soapSchemas = new XmlSchemas ();
436                         
437                         foreach (ImportInfo info in importInfo)
438                         {
439                                 foreach (Service service in info.ServiceDescription.Services)
440                                 {
441                                         foreach (Port port in service.Ports)
442                                         {
443                                                 this.iinfo = info;
444                                                 this.port = port;
445                                                 binding = ServiceDescriptions.GetBinding (port.Binding);
446                                                 if (binding == null) continue;
447                                                 portType = ServiceDescriptions.GetPortType (binding.Type);
448                                                 if (portType == null) continue;
449                                                 
450                                                 foreach (OperationBinding oper in binding.Operations) 
451                                                 {
452                                                         operationBinding = oper;
453                                                         operation = FindPortOperation ();
454                                                         if (operation == null) continue;
455                 
456                                                         foreach (OperationMessage omsg in operation.Messages)
457                                                         {
458                                                                 Message msg = ServiceDescriptions.GetMessage (omsg.Message);
459                                                                 if (msg == null) continue;
460                                                                 
461                                                                 if (omsg is OperationInput)
462                                                                         inputMessage = msg;
463                                                                 else
464                                                                         outputMessage = msg;
465                                                         }
466                                                         
467                                                         if (GetMessageEncoding (oper.Input) == SoapBindingUse.Encoded)
468                                                                 AddMessageSchema (soapSchemas, oper.Input, inputMessage);
469                                                         else
470                                                                 AddMessageSchema (xmlSchemas, oper.Input, inputMessage);
471                                                         
472                                                         if (oper.Output != null) {
473                                                                 if (GetMessageEncoding (oper.Output) == SoapBindingUse.Encoded)
474                                                                         AddMessageSchema (soapSchemas, oper.Output, outputMessage);
475                                                                 else
476                                                                         AddMessageSchema (xmlSchemas, oper.Output, outputMessage);
477                                                         }
478                                                 }
479                                         }
480                                 }
481                         }
482                         
483                         XmlSchemas defaultList = xmlSchemas;
484                                 
485                         if (xmlSchemas.Count == 0 && soapSchemas.Count > 0)
486                                 defaultList = soapSchemas;
487                                 
488                         // Schemas not referenced by any message
489                         foreach (XmlSchema sc in Schemas)
490                         {
491                                 if (!soapSchemas.Contains (sc) && !xmlSchemas.Contains (sc)) {
492                                         if (ImportsEncodedNamespace (sc))
493                                                 soapSchemas.Add (sc);
494                                         else
495                                                 defaultList.Add (sc);
496                                 }
497                         }
498                 }
499                         
500                 void AddMessageSchema (XmlSchemas schemas, MessageBinding mb, Message msg)
501                 {
502                         foreach (MessagePart part in msg.Parts)
503                         {
504                                 if (part.Element != XmlQualifiedName.Empty)
505                                         AddIncludingSchema (schemas, part.Element.Namespace);
506                                 else if (part.Type != XmlQualifiedName.Empty)
507                                         AddIncludingSchema (schemas, part.Type.Namespace);
508                         }
509                         SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
510                         if (sbb != null) AddIncludingSchema (schemas, sbb.Namespace);
511                 }
512                 
513                 void AddIncludingSchema (XmlSchemas list, string ns)
514                 {
515                         XmlSchema sc = Schemas [ns];
516                         if (sc == null || list.Contains (sc)) return;
517                         list.Add (sc);
518                         foreach (XmlSchemaObject ob in sc.Includes)
519                         {
520                                 XmlSchemaImport import = ob as XmlSchemaImport;
521                                 if (import != null) AddIncludingSchema (list, import.Namespace);
522                         }
523                 }
524                 
525                 SoapBindingUse GetMessageEncoding (MessageBinding mb)
526                 {
527                         SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
528                         if (sbb == null)
529                         {
530                                 if (mb is InputBinding) return SoapBindingUse.Encoded;
531                                 else return SoapBindingUse.Literal;
532                         }
533                         else 
534                                 if (sbb.Use == SoapBindingUse.Encoded) return SoapBindingUse.Encoded;
535                         else
536                                 return SoapBindingUse.Literal;
537                 }
538                 
539                 bool ImportsEncodedNamespace (XmlSchema sc)
540                 {
541                         foreach (XmlSchemaObject ob in sc.Includes)
542                         {
543                                 XmlSchemaImport import = ob as XmlSchemaImport;
544                                 if (import.Namespace == SoapProtocolReflector.EncodingNamespace) return true;
545                         }
546                         return false;
547                 }
548                 
549 #if NET_2_0
550
551                 void AddAsyncTypes ()
552                 {
553                         foreach (CodeTypeDeclaration type in asyncTypes)
554                                 codeNamespace.Types.Add (type);
555                         asyncTypes.Clear ();
556                 }
557
558                 void AddAsyncMembers (string messageName, CodeMemberMethod method)
559                 {
560                         CodeThisReferenceExpression ethis = new CodeThisReferenceExpression();
561                         CodePrimitiveExpression enull = new CodePrimitiveExpression (null);
562                         
563                         CodeMemberField codeField = new CodeMemberField (typeof(System.Threading.SendOrPostCallback), messageName + "OperationCompleted");
564                         codeField.Attributes = MemberAttributes.Private;
565                         CodeTypeDeclaration.Members.Add (codeField);
566                         
567                         // Event arguments class
568                         
569                         string argsClassName = classNames.AddUnique (messageName + "CompletedEventArgs", null);
570                         CodeTypeDeclaration argsClass = new CodeTypeDeclaration (argsClassName);
571                         argsClass.BaseTypes.Add (new CodeTypeReference ("System.ComponentModel.AsyncCompletedEventArgs"));
572
573                         CodeMemberField resultsField = new CodeMemberField (typeof(object[]), "results");
574                         resultsField.Attributes = MemberAttributes.Private;
575                         argsClass.Members.Add (resultsField);
576                         
577                         CodeConstructor cc = new CodeConstructor ();
578                         cc.Attributes = MemberAttributes.Assembly;
579                         cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object[]), "results"));
580                         cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(System.Exception), "exception"));
581                         cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(bool), "cancelled"));
582                         cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), "userState"));
583                         cc.BaseConstructorArgs.Add (new CodeVariableReferenceExpression ("exception"));
584                         cc.BaseConstructorArgs.Add (new CodeVariableReferenceExpression ("cancelled"));
585                         cc.BaseConstructorArgs.Add (new CodeVariableReferenceExpression ("userState"));
586                         CodeExpression thisResults = new CodeFieldReferenceExpression (ethis, "results");
587                         cc.Statements.Add (new CodeAssignStatement (thisResults, new CodeVariableReferenceExpression ("results")));
588                         argsClass.Members.Add (cc);
589                         
590                         int ind = 0;
591                         
592                         if (method.ReturnType.BaseType != "System.Void")
593                                 argsClass.Members.Add (CreateArgsProperty (method.ReturnType, "Result", ind++));
594                         
595                         foreach (CodeParameterDeclarationExpression par in method.Parameters) 
596                         {
597                                 if (par.Direction == FieldDirection.Out || par.Direction == FieldDirection.Ref)
598                                         argsClass.Members.Add (CreateArgsProperty (par.Type, par.Name, ind++));
599                         }
600                         
601                         bool needsArgsClass = (ind > 0);
602                         if (needsArgsClass)
603                                 asyncTypes.Add (argsClass);
604                         else
605                                 argsClassName = "System.ComponentModel.AsyncCompletedEventArgs";
606                         
607                         // Event delegate type
608                         
609                         CodeTypeDelegate delegateType = new CodeTypeDelegate (messageName + "CompletedEventHandler");
610                         delegateType.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), "sender"));
611                         delegateType.Parameters.Add (new CodeParameterDeclarationExpression (argsClassName, "args"));
612                         
613                         // Event member
614                         
615                         CodeMemberEvent codeEvent = new CodeMemberEvent ();
616                         codeEvent.Name = messageName + "Completed";
617                         codeEvent.Type = new CodeTypeReference (delegateType.Name);
618                         CodeTypeDeclaration.Members.Add (codeEvent);
619                         
620                         // Async method (without user state param)
621                         
622                         CodeMemberMethod am = new CodeMemberMethod ();
623                         am.Attributes = MemberAttributes.Public | MemberAttributes.Final;
624                         am.Name = method.Name + "Async";
625                         am.ReturnType = new CodeTypeReference (typeof(void));
626                         CodeMethodInvokeExpression inv;
627                         inv = new CodeMethodInvokeExpression (ethis, am.Name);
628                         am.Statements.Add (inv);
629                         
630                         // On...Completed method
631                         
632                         CodeMemberMethod onCompleted = new CodeMemberMethod ();
633                         onCompleted.Name = "On" + messageName + "Completed";
634                         onCompleted.Attributes = MemberAttributes.Private | MemberAttributes.Final;
635                         onCompleted.ReturnType = new CodeTypeReference (typeof(void));
636                         onCompleted.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), "arg"));
637                         
638                         CodeConditionStatement anIf = new CodeConditionStatement ();
639                         
640                         CodeExpression eventField = new CodeEventReferenceExpression (ethis, codeEvent.Name);
641                         anIf.Condition = new CodeBinaryOperatorExpression (eventField, CodeBinaryOperatorType.IdentityInequality, enull);
642                         CodeExpression castedArg = new CodeCastExpression (typeof(System.Web.Services.Protocols.InvokeCompletedEventArgs), new CodeVariableReferenceExpression ("arg"));
643                         CodeStatement invokeArgs = new CodeVariableDeclarationStatement (typeof(System.Web.Services.Protocols.InvokeCompletedEventArgs), "invokeArgs", castedArg);
644                         anIf.TrueStatements.Add (invokeArgs);
645                         
646                         CodeDelegateInvokeExpression delegateInvoke = new CodeDelegateInvokeExpression ();
647                         delegateInvoke.TargetObject = eventField;
648                         delegateInvoke.Parameters.Add (ethis);
649                         CodeObjectCreateExpression argsInstance = new CodeObjectCreateExpression (argsClassName);
650                         CodeExpression invokeArgsVar = new CodeVariableReferenceExpression ("invokeArgs");
651                         if (needsArgsClass) argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "Results"));
652                         argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "Error"));
653                         argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "Cancelled"));
654                         argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "UserState"));
655                         delegateInvoke.Parameters.Add (argsInstance);
656                         anIf.TrueStatements.Add (delegateInvoke);
657                         
658                         onCompleted.Statements.Add (anIf);
659                         
660                         // Async method
661                         
662                         CodeMemberMethod asyncMethod = new CodeMemberMethod ();
663                         asyncMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
664                         asyncMethod.Name = method.Name + "Async";
665                         asyncMethod.ReturnType = new CodeTypeReference (typeof(void));
666                         
667                         CodeExpression delegateField = new CodeFieldReferenceExpression (ethis, codeField.Name);
668                         anIf = new CodeConditionStatement ();
669                         anIf.Condition = new CodeBinaryOperatorExpression (delegateField, CodeBinaryOperatorType.IdentityEquality, enull);;
670                         CodeExpression delegateRef = new CodeMethodReferenceExpression (ethis, onCompleted.Name);
671                         CodeExpression newDelegate = new CodeObjectCreateExpression (typeof(System.Threading.SendOrPostCallback), delegateRef);
672                         CodeAssignStatement cas = new CodeAssignStatement (delegateField, newDelegate);
673                         anIf.TrueStatements.Add (cas);
674                         asyncMethod.Statements.Add (anIf);
675                         
676                         CodeArrayCreateExpression paramsArray = new CodeArrayCreateExpression (typeof(object));
677                         
678                         // Assign parameters
679                         
680                         CodeIdentifiers paramsIds = new CodeIdentifiers ();
681                         
682                         foreach (CodeParameterDeclarationExpression par in method.Parameters) 
683                         {
684                                 paramsIds.Add (par.Name, null);
685                                 if (par.Direction == FieldDirection.In || par.Direction == FieldDirection.Ref) {
686                                         CodeParameterDeclarationExpression inpar = new CodeParameterDeclarationExpression (par.Type, par.Name);
687                                         am.Parameters.Add (inpar);
688                                         asyncMethod.Parameters.Add (inpar);
689                                         inv.Parameters.Add (new CodeVariableReferenceExpression (par.Name));
690                                         paramsArray.Initializers.Add (new CodeVariableReferenceExpression (par.Name));
691                                 }
692                         }
693
694
695                         inv.Parameters.Add (enull);
696                         
697                         string userStateName = paramsIds.AddUnique ("userState", null);
698                         asyncMethod.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), userStateName));
699                         
700                         CodeExpression userStateVar = new CodeVariableReferenceExpression (userStateName);
701                         asyncMethod.Statements.Add (BuildInvokeAsync (messageName, paramsArray, delegateField, userStateVar));
702                         
703                         CodeTypeDeclaration.Members.Add (am);
704                         CodeTypeDeclaration.Members.Add (asyncMethod);
705                         CodeTypeDeclaration.Members.Add (onCompleted);
706                         
707                         asyncTypes.Add (delegateType);
708                 }
709                 
710                 CodeMemberProperty CreateArgsProperty (CodeTypeReference type, string name, int ind)
711                 {
712                         CodeMemberProperty prop = new CodeMemberProperty ();
713                         prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
714                         prop.HasGet = true;
715                         prop.HasSet = false;
716                         prop.Name = name;
717                         prop.Type = type;
718                         CodeThisReferenceExpression ethis = new CodeThisReferenceExpression();
719                         CodeExpression thisResults = new CodeFieldReferenceExpression (ethis, "results");
720                         prop.GetStatements.Add (new CodeMethodInvokeExpression (ethis, "RaiseExceptionIfNecessary"));
721                         CodeArrayIndexerExpression arrValue = new CodeArrayIndexerExpression (thisResults, new CodePrimitiveExpression (ind));
722                         CodeExpression retval = new CodeCastExpression (type, arrValue);
723                         prop.GetStatements.Add (new CodeMethodReturnStatement (retval));
724                         return prop;
725                 }
726                 
727                 internal virtual CodeExpression BuildInvokeAsync (string messageName, CodeArrayCreateExpression paramsArray, CodeExpression delegateField, CodeExpression userStateVar)
728                 {
729                         CodeThisReferenceExpression ethis = new CodeThisReferenceExpression();
730                         CodeMethodInvokeExpression inv2 = new CodeMethodInvokeExpression (ethis, "InvokeAsync");
731                         inv2.Parameters.Add (new CodePrimitiveExpression (messageName));
732                         inv2.Parameters.Add (paramsArray);
733                         inv2.Parameters.Add (delegateField);
734                         inv2.Parameters.Add (userStateVar);
735                         return inv2;
736                 }
737 #endif
738                 
739                 [MonoTODO]
740                 public void AddExtensionWarningComments (CodeCommentStatementCollection comments, ServiceDescriptionFormatExtensionCollection extensions) 
741                 {
742                         throw new NotImplementedException ();
743                 }
744
745                 protected abstract CodeTypeDeclaration BeginClass ();
746
747                 protected virtual void BeginNamespace ()
748                 {
749                 }
750
751                 protected virtual void EndClass ()
752                 {
753                 }
754
755                 protected virtual void EndNamespace ()
756                 {
757                 }
758
759                 protected abstract CodeMemberMethod GenerateMethod ();
760                 protected abstract bool IsBindingSupported ();
761                 protected abstract bool IsOperationFlowSupported (OperationFlow flow);
762                 
763                 [MonoTODO]
764                 public Exception OperationBindingSyntaxException (string text)
765                 {
766                         throw new NotImplementedException ();
767                 }
768
769                 [MonoTODO]
770                 public Exception OperationSyntaxException (string text)
771                 {
772                         throw new NotImplementedException ();
773                 }
774
775                 public void UnsupportedBindingWarning (string text)
776                 {
777                         warnings |= ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored;
778                         AddGlobalComments ("WARNING: Could not generate proxy for binding " + binding.Name + ". " + text);
779                 }
780
781                 public void UnsupportedOperationBindingWarning (string text)
782                 {
783                         warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
784                         AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
785                 }
786
787                 public void UnsupportedOperationWarning (string text)
788                 {
789                         warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
790                         AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
791                 }
792
793                 void AddGlobalComments (string comments)
794                 {
795                         codeNamespace.Comments.Add (new CodeCommentStatement (comments, false));
796                 }
797
798                 void AddComments (CodeTypeMember member, string comments)
799                 {
800                         if (comments == null || comments == "") member.Comments.Add (new CodeCommentStatement ("<remarks/>", true));
801                         else member.Comments.Add (new CodeCommentStatement ("<remarks>\n" + comments + "\n</remarks>", true));
802                 }
803
804                 void AddCodeType (CodeTypeDeclaration type, string comments)
805                 {
806                         AddComments (type, comments);
807                         codeNamespace.Types.Add (type);
808                 }
809
810                 internal void AddCustomAttribute (CodeTypeMember ctm, CodeAttributeDeclaration att, bool addIfNoParams)
811                 {
812                         if (att.Arguments.Count == 0 && !addIfNoParams) return;
813                         
814                         if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
815                         ctm.CustomAttributes.Add (att);
816                 }
817
818                 internal void AddCustomAttribute (CodeTypeMember ctm, string name, params CodeAttributeArgument[] args)
819                 {
820                         if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
821                         ctm.CustomAttributes.Add (new CodeAttributeDeclaration (name, args));
822                 }
823
824                 internal CodeAttributeArgument GetArg (string name, object value)
825                 {
826                         return new CodeAttributeArgument (name, new CodePrimitiveExpression(value));
827                 }
828
829                 internal CodeAttributeArgument GetEnumArg (string name, string enumType, string enumValue)
830                 {
831                         return new CodeAttributeArgument (name, new CodeFieldReferenceExpression (new CodeTypeReferenceExpression(enumType), enumValue));
832                 }
833
834                 internal CodeAttributeArgument GetArg (object value)
835                 {
836                         return new CodeAttributeArgument (new CodePrimitiveExpression(value));
837                 }
838
839                 internal CodeAttributeArgument GetTypeArg (string name, string typeName)
840                 {
841                         return new CodeAttributeArgument (name, new CodeTypeOfExpression(typeName));
842                 }
843                 
844                 #endregion
845         }
846 }