Merge pull request #5714 from alexischr/update_bockbuild
[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                 CodeTypeDeclaration codeTypeDeclaration;
53                 Message inputMessage;
54                 string methodName;
55                 Operation operation;
56                 OperationBinding operationBinding;
57                 Message outputMessage;          
58                 Port port;
59                 PortType portType;
60                 Service service;
61                 ServiceDescriptionImportWarnings warnings = (ServiceDescriptionImportWarnings)0;        
62                 ServiceDescriptionImporter descriptionImporter;
63                 ImportInfo iinfo;
64                 XmlSchemas xmlSchemas;
65                 XmlSchemas soapSchemas;
66                 
67                 ArrayList asyncTypes = new ArrayList ();
68
69                 #endregion // Fields
70
71                 #region Constructors
72         
73                 protected ProtocolImporter ()
74                 {
75                 }
76                 
77                 #endregion // Constructors
78
79                 #region Properties
80
81                 [MonoTODO]
82                 public XmlSchemas AbstractSchemas {
83                         get { return descriptionImporter.Schemas; }
84                 }
85
86                 public Binding Binding {
87                         get { return binding; }
88                 }
89
90                 public string ClassName {
91                         get { return className; }
92                 }
93
94                 public CodeIdentifiers ClassNames {
95                         get { return classNames; }
96                 }
97
98                 public CodeNamespace CodeNamespace {
99                         get { return codeNamespace; }
100                 }
101
102                 public CodeTypeDeclaration CodeTypeDeclaration {
103                         get { return codeTypeDeclaration; }
104                 }
105
106                 [MonoTODO]
107                 public XmlSchemas ConcreteSchemas {
108                         get { return descriptionImporter.Schemas; }
109                 }
110
111                 public Message InputMessage {
112                         get { return inputMessage; }
113                 }
114
115                 public string MethodName {
116                         get { return methodName; }
117                 }
118
119                 public Operation Operation {
120                         get { return operation; }
121                 }
122
123                 public OperationBinding OperationBinding {
124                         get { return operationBinding; }
125                 }
126
127                 public Message OutputMessage {
128                         get { return outputMessage; }
129                 }
130
131                 public Port Port {
132                         get { return port; }
133                 }
134
135                 public PortType PortType {
136                         get { return portType; }
137                 }
138
139                 public abstract string ProtocolName {
140                         get; 
141                 }
142
143                 public XmlSchemas Schemas {
144                         get { return descriptionImporter.Schemas; }
145                 }
146
147                 public Service Service {
148                         get { return service; } 
149                 }
150
151                 public ServiceDescriptionCollection ServiceDescriptions {
152                         get { return descriptionImporter.ServiceDescriptions; }
153                 }
154
155                 public ServiceDescriptionImportStyle Style {
156                         get { return descriptionImporter.Style; }
157                 }
158
159                 public ServiceDescriptionImportWarnings Warnings {
160                         get { return warnings; }
161                         set { warnings = value; }
162                 }
163                 
164                 internal ImportInfo ImportInfo
165                 {
166                         get { return iinfo; }
167                 }
168                 
169                 internal XmlSchemas LiteralSchemas
170                 {
171                         get { return xmlSchemas; }
172                 }
173                 
174                 internal XmlSchemas EncodedSchemas
175                 {
176                         get { return soapSchemas; }
177                 }
178                 
179                 internal CodeGenerationOptions CodeGenerationOptions {
180                         get { return descriptionImporter.CodeGenerationOptions; }
181                 }
182                 
183                 internal CodeDomProvider CodeGenerator {
184                         get { return descriptionImporter.CodeGenerator; }
185                 }
186
187                 internal ImportContext ImportContext {
188                         get { return descriptionImporter.Context; }
189                 }
190
191                 #endregion // Properties
192
193                 #region Methods
194                 
195                 internal bool Import (ServiceDescriptionImporter descriptionImporter, CodeNamespace codeNamespace, ArrayList importInfo)
196                 {
197                         this.descriptionImporter = descriptionImporter;
198                         this.classNames = new CodeIdentifiers();;
199                         this.codeNamespace = codeNamespace;
200
201                         warnings = (ServiceDescriptionImportWarnings) 0;
202                         
203                         bool found = false;
204                         
205                         ClasifySchemas (importInfo);
206
207                         BeginNamespace ();
208                         
209                         foreach (ImportInfo info in importInfo)
210                         {
211                                 foreach (Service service in info.ServiceDescription.Services)
212                                 {
213                                         this.service = service;
214                                         int bindingCount = 0;
215                                         foreach (Port port in service.Ports)
216                                         {
217                                                 binding = ServiceDescriptions.GetBinding (port.Binding);
218                                                 if (IsBindingSupported ()) bindingCount ++;
219                                         }
220                                         
221                                         foreach (Port port in service.Ports)
222                                         {
223                                                 this.iinfo = info;
224                                                 this.port = port;
225                                                 binding = ServiceDescriptions.GetBinding (port.Binding);
226                                                 if (!IsBindingSupported ()) continue;
227                                                 
228                                                 found = true;
229                                                 ImportPortBinding (bindingCount > 1);
230                                         }
231                                 }
232                         }
233                         
234                         if (!found)
235                         {
236                                 // Looks like MS.NET generates classes for all bindings if
237                                 // no services are present
238                                 
239                                 foreach (ImportInfo info in importInfo)
240                                 {
241                                         this.iinfo = info;
242                                         foreach (Binding b in info.ServiceDescription.Bindings)
243                                         {
244                                                 this.binding = b;
245                                                 this.service = null;
246                                                 this.port = null;
247                                                 if (!IsBindingSupported ()) continue;
248                                                 found = true;
249                                                 ImportPortBinding (true);
250                                         }
251                                 }
252                         }
253
254                         EndNamespace ();
255                         
256                         if (!found) warnings = ServiceDescriptionImportWarnings.NoCodeGenerated;
257                         return found;
258                 }
259
260                 void ImportPortBinding (bool multipleBindings)
261                 {
262                         if (port != null) {
263                                 if (multipleBindings) className = binding.Name;
264                                 else className = service.Name;
265                         }
266                         else
267                                 className = binding.Name;
268                         
269                         className = classNames.AddUnique (CodeIdentifier.MakeValid (className), port);
270                         className = className.Replace ("_x0020_", "");  // MS.NET seems to do this
271                         
272                         try
273                         {
274                                 portType = ServiceDescriptions.GetPortType (binding.Type);
275                                 if (portType == null) throw new Exception ("Port type not found: " + binding.Type);
276
277                                 CodeTypeDeclaration codeClass = BeginClass ();
278                                 codeTypeDeclaration = codeClass;
279                                 AddCodeType (codeClass, port != null ? port.Documentation : null);
280                                 codeClass.Attributes = MemberAttributes.Public;
281                         
282                                 if (service != null && service.Documentation != null && service.Documentation != "")
283                                         AddComments (codeClass, service.Documentation);
284
285                                 if (Style == ServiceDescriptionImportStyle.Client) {
286                                         CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Diagnostics.DebuggerStepThroughAttribute");
287                                         AddCustomAttribute (codeClass, att, true);
288         
289                                         att = new CodeAttributeDeclaration ("System.ComponentModel.DesignerCategoryAttribute");
290                                         att.Arguments.Add (GetArg ("code"));
291                                         AddCustomAttribute (codeClass, att, true);
292                                 }
293                                 else
294                                         codeClass.TypeAttributes = System.Reflection.TypeAttributes.Abstract | System.Reflection.TypeAttributes.Public;
295
296                                 if (binding.Operations.Count == 0) {
297                                         warnings |= ServiceDescriptionImportWarnings.NoMethodsGenerated;
298                                         return;
299                                 }
300                                 
301                                 foreach (OperationBinding oper in binding.Operations) 
302                                 {
303                                         operationBinding = oper;
304                                         operation = FindPortOperation ();
305                                         if (operation == null)
306                                                 throw new Exception ("Operation " + operationBinding.Name + " not found in portType " + PortType.Name);
307
308                                         inputMessage = null;
309                                         outputMessage = null;
310
311                                         foreach (OperationMessage omsg in operation.Messages)
312                                         {
313                                                 Message msg = ServiceDescriptions.GetMessage (omsg.Message);
314                                                 if (msg == null) throw new Exception ("Message not found: " + omsg.Message);
315                                                 
316                                                 if (omsg is OperationInput)
317                                                         inputMessage = msg;
318                                                 else
319                                                         outputMessage = msg;
320                                         }
321                                         
322                                         CodeMemberMethod method = GenerateMethod ();
323                                         
324                                         if (method != null)
325                                         {
326                                                 methodName = method.Name;
327                                                 if (operation.Documentation != null && operation.Documentation != "")
328                                                         AddComments (method, operation.Documentation);
329                                                 if (Style == ServiceDescriptionImportStyle.Client)
330                                                         AddAsyncMembers (method.Name, method);
331                                         }
332                                 }
333                                 
334                         if (Style == ServiceDescriptionImportStyle.Client)
335                                 AddAsyncTypes ();
336                                 
337                                 EndClass ();
338                         }
339                         catch (InvalidOperationException ex)
340                         {
341                                 warnings |= ServiceDescriptionImportWarnings.NoCodeGenerated;
342                                 UnsupportedBindingWarning (ex.Message);
343                         }
344                 }
345
346                 Operation FindPortOperation ()
347                 {
348                         string inMessage = null;
349                         string outMessage = null;
350                         int numMsg = 1;
351                         
352                         if (operationBinding.Input == null) throw new InvalidOperationException ("Input operation binding not found");
353                         inMessage = (operationBinding.Input.Name != null) ? operationBinding.Input.Name : operationBinding.Name;
354                                 
355                         if (operationBinding.Output != null) {
356                                 outMessage = (operationBinding.Output.Name != null) ? operationBinding.Output.Name : operationBinding.Name;
357                                 numMsg++;
358                         }
359                         
360                         string operName = operationBinding.Name;
361                         
362                         Operation foundOper = null;
363                         foreach (Operation oper in PortType.Operations)
364                         {
365                                 if (oper.Name == operName)
366                                 {
367                                         int hits = 0;
368                                         foreach (OperationMessage omsg in oper.Messages)
369                                         {
370                                                 if (omsg is OperationInput && GetOperMessageName (omsg, operName) == inMessage) hits++;
371                                                 if (omsg is OperationOutput && GetOperMessageName (omsg, operName) == outMessage) hits++;
372                                         }
373                                         if (hits == numMsg) return oper;
374                                         foundOper = oper;
375                                 }
376                         }
377                         return foundOper;
378                 }
379                 
380                 string GetOperMessageName (OperationMessage msg, string operName)
381                 {
382                         if (msg.Name == null) return operName;
383                         else return msg.Name;
384                 }
385                 
386                 internal void GenerateServiceUrl (string location, CodeStatementCollection stms)
387                 {
388                         if (ImportInfo.AppSettingUrlKey == null || ImportInfo.AppSettingUrlKey == string.Empty) {
389                                 if (location != null) {
390                                         CodeExpression ce = new CodeFieldReferenceExpression (new CodeThisReferenceExpression(), "Url");
391                                         CodeAssignStatement cas = new CodeAssignStatement (ce, new CodePrimitiveExpression (location));
392                                         stms.Add (cas);
393                                 }
394                         }
395                         else
396                         {
397                                 CodeExpression prop = new CodePropertyReferenceExpression (new CodeTypeReferenceExpression ("System.Configuration.ConfigurationSettings"), "AppSettings");
398                                 prop = new CodeIndexerExpression (prop, new CodePrimitiveExpression (ImportInfo.AppSettingUrlKey));
399                                 stms.Add (new CodeVariableDeclarationStatement (typeof(string), "urlSetting", prop));
400                                 
401                                 CodeExpression urlSetting = new CodeVariableReferenceExpression ("urlSetting");
402                                 CodeExpression thisUrl = new CodeFieldReferenceExpression (new CodeThisReferenceExpression(), "Url");
403                                 
404                                 CodeStatement[] trueStms = new CodeStatement [1];
405                                 CodeExpression ce = urlSetting;
406                                 CodeExpression cond = new CodeBinaryOperatorExpression (urlSetting, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression (null));
407                                 
408                                 if (ImportInfo.AppSettingBaseUrl != null)
409                                         ce = new CodeMethodInvokeExpression (new CodeTypeReferenceExpression (typeof(string)), "Concat", ce, new CodePrimitiveExpression (ImportInfo.AppSettingBaseUrl));
410                                 trueStms [0] = new CodeAssignStatement (thisUrl, ce);
411                                 
412                                 if (location != null) {
413                                         CodeStatement[] falseStms = new CodeStatement [1];
414                                         falseStms [0] = new CodeAssignStatement (thisUrl, new CodePrimitiveExpression (location));
415                                         stms.Add (new CodeConditionStatement (cond, trueStms, falseStms));
416                                 }
417                                 else
418                                         stms.Add (new CodeConditionStatement (cond, trueStms));
419                         }
420                 }
421                 
422                 void ClasifySchemas (ArrayList importInfo)
423                 {
424                         // I don't like this, but I could not find any other way of clasifying
425                         // schemas between encoded and literal.
426                         
427                         xmlSchemas = new XmlSchemas ();
428                         soapSchemas = new XmlSchemas ();
429                         
430                         foreach (ImportInfo info in importInfo)
431                         {
432                                 foreach (Service service in info.ServiceDescription.Services)
433                                 {
434                                         foreach (Port port in service.Ports)
435                                         {
436                                                 this.iinfo = info;
437                                                 this.port = port;
438                                                 binding = ServiceDescriptions.GetBinding (port.Binding);
439                                                 if (binding == null) continue;
440                                                 portType = ServiceDescriptions.GetPortType (binding.Type);
441                                                 if (portType == null) continue;
442                                                 
443                                                 foreach (OperationBinding oper in binding.Operations) 
444                                                 {
445                                                         operationBinding = oper;
446                                                         operation = FindPortOperation ();
447                                                         if (operation == null) continue;
448                 
449                                                         foreach (OperationMessage omsg in operation.Messages)
450                                                         {
451                                                                 Message msg = ServiceDescriptions.GetMessage (omsg.Message);
452                                                                 if (msg == null) continue;
453                                                                 
454                                                                 if (omsg is OperationInput)
455                                                                         inputMessage = msg;
456                                                                 else
457                                                                         outputMessage = msg;
458                                                         }
459                                                         
460                                                         if (GetMessageEncoding (oper.Input) == SoapBindingUse.Encoded)
461                                                                 AddMessageSchema (soapSchemas, oper.Input, inputMessage);
462                                                         else
463                                                                 AddMessageSchema (xmlSchemas, oper.Input, inputMessage);
464                                                         
465                                                         if (oper.Output != null) {
466                                                                 if (GetMessageEncoding (oper.Output) == SoapBindingUse.Encoded)
467                                                                         AddMessageSchema (soapSchemas, oper.Output, outputMessage);
468                                                                 else
469                                                                         AddMessageSchema (xmlSchemas, oper.Output, outputMessage);
470                                                         }
471                                                 }
472                                         }
473                                 }
474                         }
475                         
476                         XmlSchemas defaultList = xmlSchemas;
477                                 
478                         if (xmlSchemas.Count == 0 && soapSchemas.Count > 0)
479                                 defaultList = soapSchemas;
480                                 
481                         // Schemas not referenced by any message
482                         foreach (XmlSchema sc in Schemas)
483                         {
484                                 if (!soapSchemas.Contains (sc) && !xmlSchemas.Contains (sc)) {
485                                         if (ImportsEncodedNamespace (sc))
486                                                 soapSchemas.Add (sc);
487                                         else
488                                                 defaultList.Add (sc);
489                                 }
490                         }
491                 }
492                         
493                 void AddMessageSchema (XmlSchemas schemas, MessageBinding mb, Message msg)
494                 {
495                         foreach (MessagePart part in msg.Parts)
496                         {
497                                 if (part.Element != XmlQualifiedName.Empty)
498                                         AddIncludingSchema (schemas, part.Element.Namespace);
499                                 else if (part.Type != XmlQualifiedName.Empty)
500                                         AddIncludingSchema (schemas, part.Type.Namespace);
501                         }
502                         SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
503                         if (sbb != null) AddIncludingSchema (schemas, sbb.Namespace);
504                 }
505                 
506                 void AddIncludingSchema (XmlSchemas list, string ns)
507                 {
508                         foreach (XmlSchema sc in Schemas) {
509                                 if (sc.TargetNamespace == ns && !list.Contains (sc)) {
510                                         list.Add (sc);
511                                         foreach (XmlSchemaObject ob in sc.Includes) {
512                                                 XmlSchemaImport import = ob as XmlSchemaImport;
513                                                 if (import != null) AddIncludingSchema (list, import.Namespace);
514                                         }
515                                 }
516                         }
517                 }
518                 
519                 SoapBindingUse GetMessageEncoding (MessageBinding mb)
520                 {
521                         SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
522                         if (sbb == null)
523                         {
524                                 if (mb is InputBinding) return SoapBindingUse.Encoded;
525                                 else return SoapBindingUse.Literal;
526                         }
527                         else 
528                                 if (sbb.Use == SoapBindingUse.Encoded) return SoapBindingUse.Encoded;
529                         else
530                                 return SoapBindingUse.Literal;
531                 }
532                 
533                 bool ImportsEncodedNamespace (XmlSchema sc)
534                 {
535                         foreach (XmlSchemaObject ob in sc.Includes)
536                         {
537                                 XmlSchemaImport import = ob as XmlSchemaImport;
538                                 if (import == null)
539                                         continue;
540                                 if (import.Namespace == Soap11BindingExtensionReflector.EncodingNamespace
541                                     || import.Namespace == Soap12BindingExtensionReflector.EncodingNamespace)
542                                         return true;
543                         }
544                         return false;
545                 }
546                 
547
548                 void AddAsyncTypes ()
549                 {
550                         foreach (CodeTypeDeclaration type in asyncTypes)
551                                 codeNamespace.Types.Add (type);
552                         asyncTypes.Clear ();
553                 }
554
555                 void AddAsyncMembers (string messageName, CodeMemberMethod method)
556                 {
557                         CodeThisReferenceExpression ethis = new CodeThisReferenceExpression();
558                         CodePrimitiveExpression enull = new CodePrimitiveExpression (null);
559                         
560                         CodeMemberField codeField = new CodeMemberField (typeof(System.Threading.SendOrPostCallback), messageName + "OperationCompleted");
561                         codeField.Attributes = MemberAttributes.Private;
562                         CodeTypeDeclaration.Members.Add (codeField);
563                         
564                         // Event arguments class
565                         
566                         string argsClassName = classNames.AddUnique (messageName + "CompletedEventArgs", null);
567                         CodeTypeDeclaration argsClass = new CodeTypeDeclaration (argsClassName);
568                         argsClass.Attributes |= MemberAttributes.Public;
569                         argsClass.IsPartial = true;
570                         argsClass.BaseTypes.Add (new CodeTypeReference ("System.ComponentModel.AsyncCompletedEventArgs"));
571
572                         CodeMemberField resultsField = new CodeMemberField (typeof(object[]), "results");
573                         resultsField.Attributes = MemberAttributes.Private;
574                         argsClass.Members.Add (resultsField);
575                         
576                         CodeConstructor cc = new CodeConstructor ();
577                         cc.Attributes = MemberAttributes.Assembly;
578                         cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object[]), "results"));
579                         cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(System.Exception), "exception"));
580                         cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(bool), "cancelled"));
581                         cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), "userState"));
582                         cc.BaseConstructorArgs.Add (new CodeVariableReferenceExpression ("exception"));
583                         cc.BaseConstructorArgs.Add (new CodeVariableReferenceExpression ("cancelled"));
584                         cc.BaseConstructorArgs.Add (new CodeVariableReferenceExpression ("userState"));
585                         CodeExpression thisResults = new CodeFieldReferenceExpression (ethis, "results");
586                         cc.Statements.Add (new CodeAssignStatement (thisResults, new CodeVariableReferenceExpression ("results")));
587                         argsClass.Members.Add (cc);
588                         
589                         int ind = 0;
590                         
591                         if (method.ReturnType.BaseType != "System.Void")
592                                 argsClass.Members.Add (CreateArgsProperty (method.ReturnType, "Result", ind++));
593                         
594                         foreach (CodeParameterDeclarationExpression par in method.Parameters) 
595                         {
596                                 if (par.Direction == FieldDirection.Out || par.Direction == FieldDirection.Ref)
597                                         argsClass.Members.Add (CreateArgsProperty (par.Type, par.Name, ind++));
598                         }
599                         
600                         bool needsArgsClass = (ind > 0);
601                         if (needsArgsClass)
602                                 asyncTypes.Add (argsClass);
603                         else
604                                 argsClassName = "System.ComponentModel.AsyncCompletedEventArgs";
605                         
606                         // Event delegate type
607                         
608                         CodeTypeDelegate delegateType = new CodeTypeDelegate (messageName + "CompletedEventHandler");
609                         delegateType.Attributes |= MemberAttributes.Public;
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.Attributes = codeEvent.Attributes & ~MemberAttributes.AccessMask | MemberAttributes.Public;
617                         codeEvent.Name = messageName + "Completed";
618                         codeEvent.Type = new CodeTypeReference (delegateType.Name);
619                         CodeTypeDeclaration.Members.Add (codeEvent);
620                         
621                         // Async method (without user state param)
622                         
623                         CodeMemberMethod am = new CodeMemberMethod ();
624                         am.Attributes = MemberAttributes.Public | MemberAttributes.Final;
625                         am.Name = method.Name + "Async";
626                         am.ReturnType = new CodeTypeReference (typeof(void));
627                         CodeMethodInvokeExpression inv;
628                         inv = new CodeMethodInvokeExpression (ethis, am.Name);
629                         am.Statements.Add (inv);
630                         
631                         // On...Completed method
632                         
633                         CodeMemberMethod onCompleted = new CodeMemberMethod ();
634                         onCompleted.Name = "On" + messageName + "Completed";
635                         onCompleted.Attributes = MemberAttributes.Private | MemberAttributes.Final;
636                         onCompleted.ReturnType = new CodeTypeReference (typeof(void));
637                         onCompleted.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), "arg"));
638                         
639                         CodeConditionStatement anIf = new CodeConditionStatement ();
640                         
641                         CodeExpression eventField = new CodeEventReferenceExpression (ethis, codeEvent.Name);
642                         anIf.Condition = new CodeBinaryOperatorExpression (eventField, CodeBinaryOperatorType.IdentityInequality, enull);
643                         CodeExpression castedArg = new CodeCastExpression (typeof(System.Web.Services.Protocols.InvokeCompletedEventArgs), new CodeVariableReferenceExpression ("arg"));
644                         CodeStatement invokeArgs = new CodeVariableDeclarationStatement (typeof(System.Web.Services.Protocols.InvokeCompletedEventArgs), "invokeArgs", castedArg);
645                         anIf.TrueStatements.Add (invokeArgs);
646                         
647                         CodeDelegateInvokeExpression delegateInvoke = new CodeDelegateInvokeExpression ();
648                         delegateInvoke.TargetObject = eventField;
649                         delegateInvoke.Parameters.Add (ethis);
650                         CodeObjectCreateExpression argsInstance = new CodeObjectCreateExpression (argsClassName);
651                         CodeExpression invokeArgsVar = new CodeVariableReferenceExpression ("invokeArgs");
652                         if (needsArgsClass) argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "Results"));
653                         argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "Error"));
654                         argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "Cancelled"));
655                         argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "UserState"));
656                         delegateInvoke.Parameters.Add (argsInstance);
657                         anIf.TrueStatements.Add (delegateInvoke);
658                         
659                         onCompleted.Statements.Add (anIf);
660                         
661                         // Async method
662                         
663                         CodeMemberMethod asyncMethod = new CodeMemberMethod ();
664                         asyncMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
665                         asyncMethod.Name = method.Name + "Async";
666                         asyncMethod.ReturnType = new CodeTypeReference (typeof(void));
667                         
668                         CodeExpression delegateField = new CodeFieldReferenceExpression (ethis, codeField.Name);
669                         anIf = new CodeConditionStatement ();
670                         anIf.Condition = new CodeBinaryOperatorExpression (delegateField, CodeBinaryOperatorType.IdentityEquality, enull);;
671                         CodeExpression delegateRef = new CodeMethodReferenceExpression (ethis, onCompleted.Name);
672                         CodeExpression newDelegate = new CodeObjectCreateExpression (typeof(System.Threading.SendOrPostCallback), delegateRef);
673                         CodeAssignStatement cas = new CodeAssignStatement (delegateField, newDelegate);
674                         anIf.TrueStatements.Add (cas);
675                         asyncMethod.Statements.Add (anIf);
676                         
677                         CodeArrayCreateExpression paramsArray = new CodeArrayCreateExpression (typeof(object));
678                         
679                         // Assign parameters
680                         
681                         CodeIdentifiers paramsIds = new CodeIdentifiers ();
682                         
683                         foreach (CodeParameterDeclarationExpression par in method.Parameters) 
684                         {
685                                 paramsIds.Add (par.Name, null);
686                                 if (par.Direction == FieldDirection.In || par.Direction == FieldDirection.Ref) {
687                                         CodeParameterDeclarationExpression inpar = new CodeParameterDeclarationExpression (par.Type, par.Name);
688                                         am.Parameters.Add (inpar);
689                                         asyncMethod.Parameters.Add (inpar);
690                                         inv.Parameters.Add (new CodeVariableReferenceExpression (par.Name));
691                                         paramsArray.Initializers.Add (new CodeVariableReferenceExpression (par.Name));
692                                 }
693                         }
694
695
696                         inv.Parameters.Add (enull);
697                         
698                         string userStateName = paramsIds.AddUnique ("userState", null);
699                         asyncMethod.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), userStateName));
700                         
701                         CodeExpression userStateVar = new CodeVariableReferenceExpression (userStateName);
702                         asyncMethod.Statements.Add (BuildInvokeAsync (messageName, paramsArray, delegateField, userStateVar));
703                         
704                         CodeTypeDeclaration.Members.Add (am);
705                         CodeTypeDeclaration.Members.Add (asyncMethod);
706                         CodeTypeDeclaration.Members.Add (onCompleted);
707                         
708                         asyncTypes.Add (delegateType);
709                 }
710                 
711                 CodeMemberProperty CreateArgsProperty (CodeTypeReference type, string name, int ind)
712                 {
713                         CodeMemberProperty prop = new CodeMemberProperty ();
714                         prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
715                         prop.HasGet = true;
716                         prop.HasSet = false;
717                         prop.Name = name;
718                         prop.Type = type;
719                         CodeThisReferenceExpression ethis = new CodeThisReferenceExpression();
720                         CodeExpression thisResults = new CodeFieldReferenceExpression (ethis, "results");
721                         prop.GetStatements.Add (new CodeMethodInvokeExpression (ethis, "RaiseExceptionIfNecessary"));
722                         CodeArrayIndexerExpression arrValue = new CodeArrayIndexerExpression (thisResults, new CodePrimitiveExpression (ind));
723                         CodeExpression retval = new CodeCastExpression (type, arrValue);
724                         prop.GetStatements.Add (new CodeMethodReturnStatement (retval));
725                         return prop;
726                 }
727                 
728                 internal virtual CodeExpression BuildInvokeAsync (string messageName, CodeArrayCreateExpression paramsArray, CodeExpression delegateField, CodeExpression userStateVar)
729                 {
730                         CodeThisReferenceExpression ethis = new CodeThisReferenceExpression();
731                         CodeMethodInvokeExpression inv2 = new CodeMethodInvokeExpression (ethis, "InvokeAsync");
732                         inv2.Parameters.Add (new CodePrimitiveExpression (messageName));
733                         inv2.Parameters.Add (paramsArray);
734                         inv2.Parameters.Add (delegateField);
735                         inv2.Parameters.Add (userStateVar);
736                         return inv2;
737                 }
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 binding " + OperationBinding.Name + ". " + text);
785                 }
786
787                 public void UnsupportedOperationWarning (string text)
788                 {
789                         warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
790                         AddGlobalComments ("WARNING: Could not generate operation " + Operation.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 }