* roottypes.cs: Rename from tree.cs.
[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 CodeDomProvider 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)
313                                                 throw new Exception ("Operation " + operationBinding.Name + " not found in portType " + PortType.Name);
314
315                                         inputMessage = null;
316                                         outputMessage = null;
317
318                                         foreach (OperationMessage omsg in operation.Messages)
319                                         {
320                                                 Message msg = ServiceDescriptions.GetMessage (omsg.Message);
321                                                 if (msg == null) throw new Exception ("Message not found: " + omsg.Message);
322                                                 
323                                                 if (omsg is OperationInput)
324                                                         inputMessage = msg;
325                                                 else
326                                                         outputMessage = msg;
327                                         }
328                                         
329                                         CodeMemberMethod method = GenerateMethod ();
330                                         
331                                         if (method != null)
332                                         {
333                                                 methodName = method.Name;
334                                                 if (operation.Documentation != null && operation.Documentation != "")
335                                                         AddComments (method, operation.Documentation);
336 #if NET_2_0
337                                                 if (Style == ServiceDescriptionImportStyle.Client)
338                                                         AddAsyncMembers (method.Name, method);
339 #endif
340                                         }
341                                 }
342                                 
343 #if NET_2_0
344                         if (Style == ServiceDescriptionImportStyle.Client)
345                                 AddAsyncTypes ();
346 #endif
347                                 
348                                 EndClass ();
349                         }
350                         catch (InvalidOperationException ex)
351                         {
352                                 warnings |= ServiceDescriptionImportWarnings.NoCodeGenerated;
353                                 UnsupportedBindingWarning (ex.Message);
354                         }
355                 }
356
357                 Operation FindPortOperation ()
358                 {
359                         string inMessage = null;
360                         string outMessage = null;
361                         int numMsg = 1;
362                         
363                         if (operationBinding.Input == null) throw new InvalidOperationException ("Input operation binding not found");
364                         inMessage = (operationBinding.Input.Name != null) ? operationBinding.Input.Name : operationBinding.Name;
365                                 
366                         if (operationBinding.Output != null) {
367                                 outMessage = (operationBinding.Output.Name != null) ? operationBinding.Output.Name : operationBinding.Name;
368                                 numMsg++;
369                         }
370                         
371                         string operName = operationBinding.Name;
372                         
373                         Operation foundOper = null;
374                         foreach (Operation oper in PortType.Operations)
375                         {
376                                 if (oper.Name == operName)
377                                 {
378                                         int hits = 0;
379                                         foreach (OperationMessage omsg in oper.Messages)
380                                         {
381                                                 if (omsg is OperationInput && GetOperMessageName (omsg, operName) == inMessage) hits++;
382                                                 if (omsg is OperationOutput && GetOperMessageName (omsg, operName) == outMessage) hits++;
383                                         }
384                                         if (hits == numMsg) return oper;
385                                         foundOper = oper;
386                                 }
387                         }
388                         return foundOper;
389                 }
390                 
391                 string GetOperMessageName (OperationMessage msg, string operName)
392                 {
393                         if (msg.Name == null) return operName;
394                         else return msg.Name;
395                 }
396                 
397                 internal void GenerateServiceUrl (string location, CodeStatementCollection stms)
398                 {
399                         if (ImportInfo.AppSettingUrlKey == null || ImportInfo.AppSettingUrlKey == string.Empty) {
400                                 if (location != null) {
401                                         CodeExpression ce = new CodeFieldReferenceExpression (new CodeThisReferenceExpression(), "Url");
402                                         CodeAssignStatement cas = new CodeAssignStatement (ce, new CodePrimitiveExpression (location));
403                                         stms.Add (cas);
404                                 }
405                         }
406                         else
407                         {
408                                 CodeExpression prop = new CodePropertyReferenceExpression (new CodeTypeReferenceExpression ("System.Configuration.ConfigurationSettings"), "AppSettings");
409                                 prop = new CodeIndexerExpression (prop, new CodePrimitiveExpression (ImportInfo.AppSettingUrlKey));
410                                 stms.Add (new CodeVariableDeclarationStatement (typeof(string), "urlSetting", prop));
411                                 
412                                 CodeExpression urlSetting = new CodeVariableReferenceExpression ("urlSetting");
413                                 CodeExpression thisUrl = new CodeFieldReferenceExpression (new CodeThisReferenceExpression(), "Url");
414                                 
415                                 CodeStatement[] trueStms = new CodeStatement [1];
416                                 CodeExpression ce = urlSetting;
417                                 CodeExpression cond = new CodeBinaryOperatorExpression (urlSetting, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression (null));
418                                 
419                                 if (ImportInfo.AppSettingBaseUrl != null)
420                                         ce = new CodeMethodInvokeExpression (new CodeTypeReferenceExpression (typeof(string)), "Concat", ce, new CodePrimitiveExpression (ImportInfo.AppSettingBaseUrl));
421                                 trueStms [0] = new CodeAssignStatement (thisUrl, ce);
422                                 
423                                 if (location != null) {
424                                         CodeStatement[] falseStms = new CodeStatement [1];
425                                         falseStms [0] = new CodeAssignStatement (thisUrl, new CodePrimitiveExpression (location));
426                                         stms.Add (new CodeConditionStatement (cond, trueStms, falseStms));
427                                 }
428                                 else
429                                         stms.Add (new CodeConditionStatement (cond, trueStms));
430                         }
431                 }
432                 
433                 void ClasifySchemas (ArrayList importInfo)
434                 {
435                         // I don't like this, but I could not find any other way of clasifying
436                         // schemas between encoded and literal.
437                         
438                         xmlSchemas = new XmlSchemas ();
439                         soapSchemas = new XmlSchemas ();
440                         
441                         foreach (ImportInfo info in importInfo)
442                         {
443                                 foreach (Service service in info.ServiceDescription.Services)
444                                 {
445                                         foreach (Port port in service.Ports)
446                                         {
447                                                 this.iinfo = info;
448                                                 this.port = port;
449                                                 binding = ServiceDescriptions.GetBinding (port.Binding);
450                                                 if (binding == null) continue;
451                                                 portType = ServiceDescriptions.GetPortType (binding.Type);
452                                                 if (portType == null) continue;
453                                                 
454                                                 foreach (OperationBinding oper in binding.Operations) 
455                                                 {
456                                                         operationBinding = oper;
457                                                         operation = FindPortOperation ();
458                                                         if (operation == null) continue;
459                 
460                                                         foreach (OperationMessage omsg in operation.Messages)
461                                                         {
462                                                                 Message msg = ServiceDescriptions.GetMessage (omsg.Message);
463                                                                 if (msg == null) continue;
464                                                                 
465                                                                 if (omsg is OperationInput)
466                                                                         inputMessage = msg;
467                                                                 else
468                                                                         outputMessage = msg;
469                                                         }
470                                                         
471                                                         if (GetMessageEncoding (oper.Input) == SoapBindingUse.Encoded)
472                                                                 AddMessageSchema (soapSchemas, oper.Input, inputMessage);
473                                                         else
474                                                                 AddMessageSchema (xmlSchemas, oper.Input, inputMessage);
475                                                         
476                                                         if (oper.Output != null) {
477                                                                 if (GetMessageEncoding (oper.Output) == SoapBindingUse.Encoded)
478                                                                         AddMessageSchema (soapSchemas, oper.Output, outputMessage);
479                                                                 else
480                                                                         AddMessageSchema (xmlSchemas, oper.Output, outputMessage);
481                                                         }
482                                                 }
483                                         }
484                                 }
485                         }
486                         
487                         XmlSchemas defaultList = xmlSchemas;
488                                 
489                         if (xmlSchemas.Count == 0 && soapSchemas.Count > 0)
490                                 defaultList = soapSchemas;
491                                 
492                         // Schemas not referenced by any message
493                         foreach (XmlSchema sc in Schemas)
494                         {
495                                 if (!soapSchemas.Contains (sc) && !xmlSchemas.Contains (sc)) {
496                                         if (ImportsEncodedNamespace (sc))
497                                                 soapSchemas.Add (sc);
498                                         else
499                                                 defaultList.Add (sc);
500                                 }
501                         }
502                 }
503                         
504                 void AddMessageSchema (XmlSchemas schemas, MessageBinding mb, Message msg)
505                 {
506                         foreach (MessagePart part in msg.Parts)
507                         {
508                                 if (part.Element != XmlQualifiedName.Empty)
509                                         AddIncludingSchema (schemas, part.Element.Namespace);
510                                 else if (part.Type != XmlQualifiedName.Empty)
511                                         AddIncludingSchema (schemas, part.Type.Namespace);
512                         }
513                         SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
514                         if (sbb != null) AddIncludingSchema (schemas, sbb.Namespace);
515                 }
516                 
517                 void AddIncludingSchema (XmlSchemas list, string ns)
518                 {
519                         XmlSchema sc = Schemas [ns];
520                         if (sc == null || list.Contains (sc)) return;
521                         list.Add (sc);
522                         foreach (XmlSchemaObject ob in sc.Includes)
523                         {
524                                 XmlSchemaImport import = ob as XmlSchemaImport;
525                                 if (import != null) AddIncludingSchema (list, import.Namespace);
526                         }
527                 }
528                 
529                 SoapBindingUse GetMessageEncoding (MessageBinding mb)
530                 {
531                         SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
532                         if (sbb == null)
533                         {
534                                 if (mb is InputBinding) return SoapBindingUse.Encoded;
535                                 else return SoapBindingUse.Literal;
536                         }
537                         else 
538                                 if (sbb.Use == SoapBindingUse.Encoded) return SoapBindingUse.Encoded;
539                         else
540                                 return SoapBindingUse.Literal;
541                 }
542                 
543                 bool ImportsEncodedNamespace (XmlSchema sc)
544                 {
545                         foreach (XmlSchemaObject ob in sc.Includes)
546                         {
547                                 XmlSchemaImport import = ob as XmlSchemaImport;
548                                 if (import.Namespace == SoapProtocolReflector.EncodingNamespace) return true;
549                         }
550                         return false;
551                 }
552                 
553 #if NET_2_0
554
555                 void AddAsyncTypes ()
556                 {
557                         foreach (CodeTypeDeclaration type in asyncTypes)
558                                 codeNamespace.Types.Add (type);
559                         asyncTypes.Clear ();
560                 }
561
562                 void AddAsyncMembers (string messageName, CodeMemberMethod method)
563                 {
564                         CodeThisReferenceExpression ethis = new CodeThisReferenceExpression();
565                         CodePrimitiveExpression enull = new CodePrimitiveExpression (null);
566                         
567                         CodeMemberField codeField = new CodeMemberField (typeof(System.Threading.SendOrPostCallback), messageName + "OperationCompleted");
568                         codeField.Attributes = MemberAttributes.Private;
569                         CodeTypeDeclaration.Members.Add (codeField);
570                         
571                         // Event arguments class
572                         
573                         string argsClassName = classNames.AddUnique (messageName + "CompletedEventArgs", null);
574                         CodeTypeDeclaration argsClass = new CodeTypeDeclaration (argsClassName);
575                         argsClass.BaseTypes.Add (new CodeTypeReference ("System.ComponentModel.AsyncCompletedEventArgs"));
576
577                         CodeMemberField resultsField = new CodeMemberField (typeof(object[]), "results");
578                         resultsField.Attributes = MemberAttributes.Private;
579                         argsClass.Members.Add (resultsField);
580                         
581                         CodeConstructor cc = new CodeConstructor ();
582                         cc.Attributes = MemberAttributes.Assembly;
583                         cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object[]), "results"));
584                         cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(System.Exception), "exception"));
585                         cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(bool), "cancelled"));
586                         cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), "userState"));
587                         cc.BaseConstructorArgs.Add (new CodeVariableReferenceExpression ("exception"));
588                         cc.BaseConstructorArgs.Add (new CodeVariableReferenceExpression ("cancelled"));
589                         cc.BaseConstructorArgs.Add (new CodeVariableReferenceExpression ("userState"));
590                         CodeExpression thisResults = new CodeFieldReferenceExpression (ethis, "results");
591                         cc.Statements.Add (new CodeAssignStatement (thisResults, new CodeVariableReferenceExpression ("results")));
592                         argsClass.Members.Add (cc);
593                         
594                         int ind = 0;
595                         
596                         if (method.ReturnType.BaseType != "System.Void")
597                                 argsClass.Members.Add (CreateArgsProperty (method.ReturnType, "Result", ind++));
598                         
599                         foreach (CodeParameterDeclarationExpression par in method.Parameters) 
600                         {
601                                 if (par.Direction == FieldDirection.Out || par.Direction == FieldDirection.Ref)
602                                         argsClass.Members.Add (CreateArgsProperty (par.Type, par.Name, ind++));
603                         }
604                         
605                         bool needsArgsClass = (ind > 0);
606                         if (needsArgsClass)
607                                 asyncTypes.Add (argsClass);
608                         else
609                                 argsClassName = "System.ComponentModel.AsyncCompletedEventArgs";
610                         
611                         // Event delegate type
612                         
613                         CodeTypeDelegate delegateType = new CodeTypeDelegate (messageName + "CompletedEventHandler");
614                         delegateType.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), "sender"));
615                         delegateType.Parameters.Add (new CodeParameterDeclarationExpression (argsClassName, "args"));
616                         
617                         // Event member
618                         
619                         CodeMemberEvent codeEvent = new CodeMemberEvent ();
620                         codeEvent.Name = messageName + "Completed";
621                         codeEvent.Type = new CodeTypeReference (delegateType.Name);
622                         CodeTypeDeclaration.Members.Add (codeEvent);
623                         
624                         // Async method (without user state param)
625                         
626                         CodeMemberMethod am = new CodeMemberMethod ();
627                         am.Attributes = MemberAttributes.Public | MemberAttributes.Final;
628                         am.Name = method.Name + "Async";
629                         am.ReturnType = new CodeTypeReference (typeof(void));
630                         CodeMethodInvokeExpression inv;
631                         inv = new CodeMethodInvokeExpression (ethis, am.Name);
632                         am.Statements.Add (inv);
633                         
634                         // On...Completed method
635                         
636                         CodeMemberMethod onCompleted = new CodeMemberMethod ();
637                         onCompleted.Name = "On" + messageName + "Completed";
638                         onCompleted.Attributes = MemberAttributes.Private | MemberAttributes.Final;
639                         onCompleted.ReturnType = new CodeTypeReference (typeof(void));
640                         onCompleted.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), "arg"));
641                         
642                         CodeConditionStatement anIf = new CodeConditionStatement ();
643                         
644                         CodeExpression eventField = new CodeEventReferenceExpression (ethis, codeEvent.Name);
645                         anIf.Condition = new CodeBinaryOperatorExpression (eventField, CodeBinaryOperatorType.IdentityInequality, enull);
646                         CodeExpression castedArg = new CodeCastExpression (typeof(System.Web.Services.Protocols.InvokeCompletedEventArgs), new CodeVariableReferenceExpression ("arg"));
647                         CodeStatement invokeArgs = new CodeVariableDeclarationStatement (typeof(System.Web.Services.Protocols.InvokeCompletedEventArgs), "invokeArgs", castedArg);
648                         anIf.TrueStatements.Add (invokeArgs);
649                         
650                         CodeDelegateInvokeExpression delegateInvoke = new CodeDelegateInvokeExpression ();
651                         delegateInvoke.TargetObject = eventField;
652                         delegateInvoke.Parameters.Add (ethis);
653                         CodeObjectCreateExpression argsInstance = new CodeObjectCreateExpression (argsClassName);
654                         CodeExpression invokeArgsVar = new CodeVariableReferenceExpression ("invokeArgs");
655                         if (needsArgsClass) argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "Results"));
656                         argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "Error"));
657                         argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "Cancelled"));
658                         argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "UserState"));
659                         delegateInvoke.Parameters.Add (argsInstance);
660                         anIf.TrueStatements.Add (delegateInvoke);
661                         
662                         onCompleted.Statements.Add (anIf);
663                         
664                         // Async method
665                         
666                         CodeMemberMethod asyncMethod = new CodeMemberMethod ();
667                         asyncMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
668                         asyncMethod.Name = method.Name + "Async";
669                         asyncMethod.ReturnType = new CodeTypeReference (typeof(void));
670                         
671                         CodeExpression delegateField = new CodeFieldReferenceExpression (ethis, codeField.Name);
672                         anIf = new CodeConditionStatement ();
673                         anIf.Condition = new CodeBinaryOperatorExpression (delegateField, CodeBinaryOperatorType.IdentityEquality, enull);;
674                         CodeExpression delegateRef = new CodeMethodReferenceExpression (ethis, onCompleted.Name);
675                         CodeExpression newDelegate = new CodeObjectCreateExpression (typeof(System.Threading.SendOrPostCallback), delegateRef);
676                         CodeAssignStatement cas = new CodeAssignStatement (delegateField, newDelegate);
677                         anIf.TrueStatements.Add (cas);
678                         asyncMethod.Statements.Add (anIf);
679                         
680                         CodeArrayCreateExpression paramsArray = new CodeArrayCreateExpression (typeof(object));
681                         
682                         // Assign parameters
683                         
684                         CodeIdentifiers paramsIds = new CodeIdentifiers ();
685                         
686                         foreach (CodeParameterDeclarationExpression par in method.Parameters) 
687                         {
688                                 paramsIds.Add (par.Name, null);
689                                 if (par.Direction == FieldDirection.In || par.Direction == FieldDirection.Ref) {
690                                         CodeParameterDeclarationExpression inpar = new CodeParameterDeclarationExpression (par.Type, par.Name);
691                                         am.Parameters.Add (inpar);
692                                         asyncMethod.Parameters.Add (inpar);
693                                         inv.Parameters.Add (new CodeVariableReferenceExpression (par.Name));
694                                         paramsArray.Initializers.Add (new CodeVariableReferenceExpression (par.Name));
695                                 }
696                         }
697
698
699                         inv.Parameters.Add (enull);
700                         
701                         string userStateName = paramsIds.AddUnique ("userState", null);
702                         asyncMethod.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), userStateName));
703                         
704                         CodeExpression userStateVar = new CodeVariableReferenceExpression (userStateName);
705                         asyncMethod.Statements.Add (BuildInvokeAsync (messageName, paramsArray, delegateField, userStateVar));
706                         
707                         CodeTypeDeclaration.Members.Add (am);
708                         CodeTypeDeclaration.Members.Add (asyncMethod);
709                         CodeTypeDeclaration.Members.Add (onCompleted);
710                         
711                         asyncTypes.Add (delegateType);
712                 }
713                 
714                 CodeMemberProperty CreateArgsProperty (CodeTypeReference type, string name, int ind)
715                 {
716                         CodeMemberProperty prop = new CodeMemberProperty ();
717                         prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
718                         prop.HasGet = true;
719                         prop.HasSet = false;
720                         prop.Name = name;
721                         prop.Type = type;
722                         CodeThisReferenceExpression ethis = new CodeThisReferenceExpression();
723                         CodeExpression thisResults = new CodeFieldReferenceExpression (ethis, "results");
724                         prop.GetStatements.Add (new CodeMethodInvokeExpression (ethis, "RaiseExceptionIfNecessary"));
725                         CodeArrayIndexerExpression arrValue = new CodeArrayIndexerExpression (thisResults, new CodePrimitiveExpression (ind));
726                         CodeExpression retval = new CodeCastExpression (type, arrValue);
727                         prop.GetStatements.Add (new CodeMethodReturnStatement (retval));
728                         return prop;
729                 }
730                 
731                 internal virtual CodeExpression BuildInvokeAsync (string messageName, CodeArrayCreateExpression paramsArray, CodeExpression delegateField, CodeExpression userStateVar)
732                 {
733                         CodeThisReferenceExpression ethis = new CodeThisReferenceExpression();
734                         CodeMethodInvokeExpression inv2 = new CodeMethodInvokeExpression (ethis, "InvokeAsync");
735                         inv2.Parameters.Add (new CodePrimitiveExpression (messageName));
736                         inv2.Parameters.Add (paramsArray);
737                         inv2.Parameters.Add (delegateField);
738                         inv2.Parameters.Add (userStateVar);
739                         return inv2;
740                 }
741 #endif
742                 
743                 [MonoTODO]
744                 public void AddExtensionWarningComments (CodeCommentStatementCollection comments, ServiceDescriptionFormatExtensionCollection extensions) 
745                 {
746                         throw new NotImplementedException ();
747                 }
748
749                 protected abstract CodeTypeDeclaration BeginClass ();
750
751                 protected virtual void BeginNamespace ()
752                 {
753                 }
754
755                 protected virtual void EndClass ()
756                 {
757                 }
758
759                 protected virtual void EndNamespace ()
760                 {
761                 }
762
763                 protected abstract CodeMemberMethod GenerateMethod ();
764                 protected abstract bool IsBindingSupported ();
765                 protected abstract bool IsOperationFlowSupported (OperationFlow flow);
766                 
767                 [MonoTODO]
768                 public Exception OperationBindingSyntaxException (string text)
769                 {
770                         throw new NotImplementedException ();
771                 }
772
773                 [MonoTODO]
774                 public Exception OperationSyntaxException (string text)
775                 {
776                         throw new NotImplementedException ();
777                 }
778
779                 public void UnsupportedBindingWarning (string text)
780                 {
781                         warnings |= ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored;
782                         AddGlobalComments ("WARNING: Could not generate proxy for binding " + binding.Name + ". " + text);
783                 }
784
785                 public void UnsupportedOperationBindingWarning (string text)
786                 {
787                         warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
788                         AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
789                 }
790
791                 public void UnsupportedOperationWarning (string text)
792                 {
793                         warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
794                         AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
795                 }
796
797                 void AddGlobalComments (string comments)
798                 {
799                         codeNamespace.Comments.Add (new CodeCommentStatement (comments, false));
800                 }
801
802                 void AddComments (CodeTypeMember member, string comments)
803                 {
804                         if (comments == null || comments == "") member.Comments.Add (new CodeCommentStatement ("<remarks/>", true));
805                         else member.Comments.Add (new CodeCommentStatement ("<remarks>\n" + comments + "\n</remarks>", true));
806                 }
807
808                 void AddCodeType (CodeTypeDeclaration type, string comments)
809                 {
810                         AddComments (type, comments);
811                         codeNamespace.Types.Add (type);
812                 }
813
814                 internal void AddCustomAttribute (CodeTypeMember ctm, CodeAttributeDeclaration att, bool addIfNoParams)
815                 {
816                         if (att.Arguments.Count == 0 && !addIfNoParams) return;
817                         
818                         if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
819                         ctm.CustomAttributes.Add (att);
820                 }
821
822                 internal void AddCustomAttribute (CodeTypeMember ctm, string name, params CodeAttributeArgument[] args)
823                 {
824                         if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
825                         ctm.CustomAttributes.Add (new CodeAttributeDeclaration (name, args));
826                 }
827
828                 internal CodeAttributeArgument GetArg (string name, object value)
829                 {
830                         return new CodeAttributeArgument (name, new CodePrimitiveExpression(value));
831                 }
832
833                 internal CodeAttributeArgument GetEnumArg (string name, string enumType, string enumValue)
834                 {
835                         return new CodeAttributeArgument (name, new CodeFieldReferenceExpression (new CodeTypeReferenceExpression(enumType), enumValue));
836                 }
837
838                 internal CodeAttributeArgument GetArg (object value)
839                 {
840                         return new CodeAttributeArgument (new CodePrimitiveExpression(value));
841                 }
842
843                 internal CodeAttributeArgument GetTypeArg (string name, string typeName)
844                 {
845                         return new CodeAttributeArgument (name, new CodeTypeOfExpression(typeName));
846                 }
847                 
848                 #endregion
849         }
850 }