2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Description / ProtocolImporter.cs
index 9345cc15590e562a67322c30b1505d058ca03e69..43f4e405bd4a299b92ec11c87c1df71bbce234fc 100644 (file)
@@ -8,8 +8,30 @@
 // Copyright (C) Tim Coleman, 2002
 //
 
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
 using System.CodeDom;
+using System.CodeDom.Compiler;
 using System.Web.Services;
 using System.Web.Services.Protocols;
 using System.Xml.Serialization;
@@ -43,6 +65,10 @@ namespace System.Web.Services.Description {
                ImportInfo iinfo;
                XmlSchemas xmlSchemas;
                XmlSchemas soapSchemas;
+               
+#if NET_2_0
+               ArrayList asyncTypes = new ArrayList ();
+#endif
 
                #endregion // Fields
 
@@ -153,6 +179,20 @@ namespace System.Web.Services.Description {
                {
                        get { return soapSchemas; }
                }
+               
+#if NET_2_0
+               internal CodeGenerationOptions CodeGenerationOptions {
+                       get { return descriptionImporter.CodeGenerationOptions; }
+               }
+               
+               internal ICodeGenerator CodeGenerator {
+                       get { return descriptionImporter.CodeGenerator; }
+               }
+
+               internal ImportContext ImportContext {
+                       get { return descriptionImporter.Context; }
+               }
+#endif
 
                #endregion // Properties
 
@@ -164,7 +204,7 @@ namespace System.Web.Services.Description {
                        this.classNames = new CodeIdentifiers();;
                        this.codeNamespace = codeNamespace;
                        this.codeCompileUnit = codeCompileUnit;
-                       
+
                        warnings = (ServiceDescriptionImportWarnings) 0;
                        
                        bool found = false;
@@ -197,18 +237,44 @@ namespace System.Web.Services.Description {
                                        }
                                }
                        }
+                       
+                       if (!found)
+                       {
+                               // Looks like MS.NET generates classes for all bindings if
+                               // no services are present
+                               
+                               foreach (ImportInfo info in importInfo)
+                               {
+                                       this.iinfo = info;
+                                       foreach (Binding b in info.ServiceDescription.Bindings)
+                                       {
+                                               this.binding = b;
+                                               this.service = null;
+                                               this.port = null;
+                                               if (!IsBindingSupported ()) continue;
+                                               found = true;
+                                               ImportPortBinding (true);
+                                       }
+                               }
+                       }
 
                        EndNamespace ();
                        
+                       if (!found) warnings = ServiceDescriptionImportWarnings.NoCodeGenerated;
                        return true;
                }
 
                void ImportPortBinding (bool multipleBindings)
                {
-                       if (multipleBindings) className = port.Name;
-                       else className = service.Name;
+                       if (port != null) {
+                               if (multipleBindings) className = port.Name;
+                               else className = service.Name;
+                       }
+                       else
+                               className = binding.Name;
                        
                        className = classNames.AddUnique (CodeIdentifier.MakeValid (className), port);
+                       className = className.Replace ("_x0020_", "");  // MS.NET seems to do this
                        
                        try
                        {
@@ -217,18 +283,22 @@ namespace System.Web.Services.Description {
 
                                CodeTypeDeclaration codeClass = BeginClass ();
                                codeTypeDeclaration = codeClass;
-                               AddCodeType (codeClass, port.Documentation);
+                               AddCodeType (codeClass, port != null ? port.Documentation : null);
                                codeClass.Attributes = MemberAttributes.Public;
-                               
-                               if (service.Documentation != null && service.Documentation != "")
+                       
+                               if (service != null && service.Documentation != null && service.Documentation != "")
                                        AddComments (codeClass, service.Documentation);
 
-                               CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Diagnostics.DebuggerStepThroughAttribute");
-                               AddCustomAttribute (codeClass, att, true);
-
-                               att = new CodeAttributeDeclaration ("System.ComponentModel.DesignerCategoryAttribute");
-                               att.Arguments.Add (GetArg ("code"));
-                               AddCustomAttribute (codeClass, att, true);
+                               if (Style == ServiceDescriptionImportStyle.Client) {
+                                       CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Diagnostics.DebuggerStepThroughAttribute");
+                                       AddCustomAttribute (codeClass, att, true);
+       
+                                       att = new CodeAttributeDeclaration ("System.ComponentModel.DesignerCategoryAttribute");
+                                       att.Arguments.Add (GetArg ("code"));
+                                       AddCustomAttribute (codeClass, att, true);
+                               }
+                               else
+                                       codeClass.TypeAttributes = System.Reflection.TypeAttributes.Abstract | System.Reflection.TypeAttributes.Public;
 
                                if (binding.Operations.Count == 0) {
                                        warnings |= ServiceDescriptionImportWarnings.NoMethodsGenerated;
@@ -259,9 +329,18 @@ namespace System.Web.Services.Description {
                                                methodName = method.Name;
                                                if (operation.Documentation != null && operation.Documentation != "")
                                                        AddComments (method, operation.Documentation);
+#if NET_2_0
+                                               if (Style == ServiceDescriptionImportStyle.Client)
+                                                       AddAsyncMembers (method.Name, method);
+#endif
                                        }
                                }
                                
+#if NET_2_0
+                       if (Style == ServiceDescriptionImportStyle.Client)
+                               AddAsyncTypes ();
+#endif
+                               
                                EndClass ();
                        }
                        catch (InvalidOperationException ex)
@@ -311,21 +390,42 @@ namespace System.Web.Services.Description {
                        else return msg.Name;
                }
                
-               internal string GetServiceUrl (string location)
+               internal void GenerateServiceUrl (string location, CodeStatementCollection stms)
                {
-                       if (ImportInfo.AppSettingUrlKey == null || ImportInfo.AppSettingUrlKey == string.Empty)
-                               return location;
+                       if (ImportInfo.AppSettingUrlKey == null || ImportInfo.AppSettingUrlKey == string.Empty) {
+                               if (location != null) {
+                                       CodeExpression ce = new CodeFieldReferenceExpression (new CodeThisReferenceExpression(), "Url");
+                                       CodeAssignStatement cas = new CodeAssignStatement (ce, new CodePrimitiveExpression (location));
+                                       stms.Add (cas);
+                               }
+                       }
                        else
                        {
-                               string url;
-                               if (Style == ServiceDescriptionImportStyle.Server) throw new InvalidOperationException ("Cannot set appSettingUrlKey if Style is Server");
-                               url = ConfigurationSettings.AppSettings [ImportInfo.AppSettingUrlKey];
-                               if (ImportInfo.AppSettingBaseUrl != null && ImportInfo.AppSettingBaseUrl != string.Empty)
-                                       url += "/" + ImportInfo.AppSettingBaseUrl + "/" + location;
-                               return url;
+                               CodeExpression prop = new CodePropertyReferenceExpression (new CodeTypeReferenceExpression ("System.Configuration.ConfigurationSettings"), "AppSettings");
+                               prop = new CodeIndexerExpression (prop, new CodePrimitiveExpression (ImportInfo.AppSettingUrlKey));
+                               stms.Add (new CodeVariableDeclarationStatement (typeof(string), "urlSetting", prop));
+                               
+                               CodeExpression urlSetting = new CodeVariableReferenceExpression ("urlSetting");
+                               CodeExpression thisUrl = new CodeFieldReferenceExpression (new CodeThisReferenceExpression(), "Url");
+                               
+                               CodeStatement[] trueStms = new CodeStatement [1];
+                               CodeExpression ce = urlSetting;
+                               CodeExpression cond = new CodeBinaryOperatorExpression (urlSetting, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression (null));
+                               
+                               if (ImportInfo.AppSettingBaseUrl != null)
+                                       ce = new CodeMethodInvokeExpression (new CodeTypeReferenceExpression (typeof(string)), "Concat", ce, new CodePrimitiveExpression (ImportInfo.AppSettingBaseUrl));
+                               trueStms [0] = new CodeAssignStatement (thisUrl, ce);
+                               
+                               if (location != null) {
+                                       CodeStatement[] falseStms = new CodeStatement [1];
+                                       falseStms [0] = new CodeAssignStatement (thisUrl, new CodePrimitiveExpression (location));
+                                       stms.Add (new CodeConditionStatement (cond, trueStms, falseStms));
+                               }
+                               else
+                                       stms.Add (new CodeConditionStatement (cond, trueStms));
                        }
                }
-
+               
                void ClasifySchemas (ArrayList importInfo)
                {
                        // I don't like this, but I could not find any other way of clasifying
@@ -388,10 +488,12 @@ namespace System.Web.Services.Description {
                        // Schemas not referenced by any message
                        foreach (XmlSchema sc in Schemas)
                        {
-                               if (ImportsEncodedNamespace (sc))
-                                       soapSchemas.Add (sc);
-                               else if (!soapSchemas.Contains (sc) && !xmlSchemas.Contains (sc))
-                                       defaultList.Add (sc);
+                               if (!soapSchemas.Contains (sc) && !xmlSchemas.Contains (sc)) {
+                                       if (ImportsEncodedNamespace (sc))
+                                               soapSchemas.Add (sc);
+                                       else
+                                               defaultList.Add (sc);
+                               }
                        }
                }
                        
@@ -444,6 +546,196 @@ namespace System.Web.Services.Description {
                        return false;
                }
                
+#if NET_2_0
+
+               void AddAsyncTypes ()
+               {
+                       foreach (CodeTypeDeclaration type in asyncTypes)
+                               codeNamespace.Types.Add (type);
+                       asyncTypes.Clear ();
+               }
+
+               void AddAsyncMembers (string messageName, CodeMemberMethod method)
+               {
+                       CodeThisReferenceExpression ethis = new CodeThisReferenceExpression();
+                       CodePrimitiveExpression enull = new CodePrimitiveExpression (null);
+                       
+                       CodeMemberField codeField = new CodeMemberField (typeof(System.Threading.SendOrPostCallback), messageName + "OperationCompleted");
+                       codeField.Attributes = MemberAttributes.Private;
+                       CodeTypeDeclaration.Members.Add (codeField);
+                       
+                       // Event arguments class
+                       
+                       string argsClassName = classNames.AddUnique (messageName + "CompletedEventArgs", null);
+                       CodeTypeDeclaration argsClass = new CodeTypeDeclaration (argsClassName);
+                       argsClass.BaseTypes.Add (new CodeTypeReference ("System.ComponentModel.AsyncCompletedEventArgs"));
+
+                       CodeMemberField resultsField = new CodeMemberField (typeof(object[]), "results");
+                       resultsField.Attributes = MemberAttributes.Private;
+                       argsClass.Members.Add (resultsField);
+                       
+                       CodeConstructor cc = new CodeConstructor ();
+                       cc.Attributes = MemberAttributes.Assembly;
+                       cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object[]), "results"));
+                       cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(System.Exception), "exception"));
+                       cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(bool), "cancelled"));
+                       cc.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), "userState"));
+                       cc.BaseConstructorArgs.Add (new CodeVariableReferenceExpression ("exception"));
+                       cc.BaseConstructorArgs.Add (new CodeVariableReferenceExpression ("cancelled"));
+                       cc.BaseConstructorArgs.Add (new CodeVariableReferenceExpression ("userState"));
+                       CodeExpression thisResults = new CodeFieldReferenceExpression (ethis, "results");
+                       cc.Statements.Add (new CodeAssignStatement (thisResults, new CodeVariableReferenceExpression ("results")));
+                       argsClass.Members.Add (cc);
+                       
+                       int ind = 0;
+                       
+                       if (method.ReturnType.BaseType != "System.Void")
+                               argsClass.Members.Add (CreateArgsProperty (method.ReturnType, "Result", ind++));
+                       
+                       foreach (CodeParameterDeclarationExpression par in method.Parameters) 
+                       {
+                               if (par.Direction == FieldDirection.Out || par.Direction == FieldDirection.Ref)
+                                       argsClass.Members.Add (CreateArgsProperty (par.Type, par.Name, ind++));
+                       }
+                       
+                       bool needsArgsClass = (ind > 0);
+                       if (needsArgsClass)
+                               asyncTypes.Add (argsClass);
+                       else
+                               argsClassName = "System.ComponentModel.AsyncCompletedEventArgs";
+                       
+                       // Event delegate type
+                       
+                       CodeTypeDelegate delegateType = new CodeTypeDelegate (messageName + "CompletedEventHandler");
+                       delegateType.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), "sender"));
+                       delegateType.Parameters.Add (new CodeParameterDeclarationExpression (argsClassName, "args"));
+                       
+                       // Event member
+                       
+                       CodeMemberEvent codeEvent = new CodeMemberEvent ();
+                       codeEvent.Name = messageName + "Completed";
+                       codeEvent.Type = new CodeTypeReference (delegateType.Name);
+                       CodeTypeDeclaration.Members.Add (codeEvent);
+                       
+                       // Async method (without user state param)
+                       
+                       CodeMemberMethod am = new CodeMemberMethod ();
+                       am.Attributes = MemberAttributes.Public | MemberAttributes.Final;
+                       am.Name = method.Name + "Async";
+                       am.ReturnType = new CodeTypeReference (typeof(void));
+                       CodeMethodInvokeExpression inv;
+                       inv = new CodeMethodInvokeExpression (ethis, am.Name);
+                       am.Statements.Add (inv);
+                       
+                       // On...Completed method
+                       
+                       CodeMemberMethod onCompleted = new CodeMemberMethod ();
+                       onCompleted.Name = "On" + messageName + "Completed";
+                       onCompleted.Attributes = MemberAttributes.Private | MemberAttributes.Final;
+                       onCompleted.ReturnType = new CodeTypeReference (typeof(void));
+                       onCompleted.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), "arg"));
+                       
+                       CodeConditionStatement anIf = new CodeConditionStatement ();
+                       
+                       CodeExpression eventField = new CodeEventReferenceExpression (ethis, codeEvent.Name);
+                       anIf.Condition = new CodeBinaryOperatorExpression (eventField, CodeBinaryOperatorType.IdentityInequality, enull);
+                       CodeExpression castedArg = new CodeCastExpression (typeof(System.Web.Services.Protocols.InvokeCompletedEventArgs), new CodeVariableReferenceExpression ("arg"));
+                       CodeStatement invokeArgs = new CodeVariableDeclarationStatement (typeof(System.Web.Services.Protocols.InvokeCompletedEventArgs), "invokeArgs", castedArg);
+                       anIf.TrueStatements.Add (invokeArgs);
+                       
+                       CodeDelegateInvokeExpression delegateInvoke = new CodeDelegateInvokeExpression ();
+                       delegateInvoke.TargetObject = eventField;
+                       delegateInvoke.Parameters.Add (ethis);
+                       CodeObjectCreateExpression argsInstance = new CodeObjectCreateExpression (argsClassName);
+                       CodeExpression invokeArgsVar = new CodeVariableReferenceExpression ("invokeArgs");
+                       if (needsArgsClass) argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "Results"));
+                       argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "Error"));
+                       argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "Cancelled"));
+                       argsInstance.Parameters.Add (new CodeFieldReferenceExpression (invokeArgsVar, "UserState"));
+                       delegateInvoke.Parameters.Add (argsInstance);
+                       anIf.TrueStatements.Add (delegateInvoke);
+                       
+                       onCompleted.Statements.Add (anIf);
+                       
+                       // Async method
+                       
+                       CodeMemberMethod asyncMethod = new CodeMemberMethod ();
+                       asyncMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
+                       asyncMethod.Name = method.Name + "Async";
+                       asyncMethod.ReturnType = new CodeTypeReference (typeof(void));
+                       
+                       CodeExpression delegateField = new CodeFieldReferenceExpression (ethis, codeField.Name);
+                       anIf = new CodeConditionStatement ();
+                       anIf.Condition = new CodeBinaryOperatorExpression (delegateField, CodeBinaryOperatorType.IdentityEquality, enull);;
+                       CodeExpression delegateRef = new CodeMethodReferenceExpression (ethis, onCompleted.Name);
+                       CodeExpression newDelegate = new CodeObjectCreateExpression (typeof(System.Threading.SendOrPostCallback), delegateRef);
+                       CodeAssignStatement cas = new CodeAssignStatement (delegateField, newDelegate);
+                       anIf.TrueStatements.Add (cas);
+                       asyncMethod.Statements.Add (anIf);
+                       
+                       CodeArrayCreateExpression paramsArray = new CodeArrayCreateExpression (typeof(object));
+                       
+                       // Assign parameters
+                       
+                       CodeIdentifiers paramsIds = new CodeIdentifiers ();
+                       
+                       foreach (CodeParameterDeclarationExpression par in method.Parameters) 
+                       {
+                               paramsIds.Add (par.Name, null);
+                               if (par.Direction == FieldDirection.In || par.Direction == FieldDirection.Ref) {
+                                       CodeParameterDeclarationExpression inpar = new CodeParameterDeclarationExpression (par.Type, par.Name);
+                                       am.Parameters.Add (inpar);
+                                       asyncMethod.Parameters.Add (inpar);
+                                       inv.Parameters.Add (new CodeVariableReferenceExpression (par.Name));
+                                       paramsArray.Initializers.Add (new CodeVariableReferenceExpression (par.Name));
+                               }
+                       }
+
+
+                       inv.Parameters.Add (enull);
+                       
+                       string userStateName = paramsIds.AddUnique ("userState", null);
+                       asyncMethod.Parameters.Add (new CodeParameterDeclarationExpression (typeof(object), userStateName));
+                       
+                       CodeExpression userStateVar = new CodeVariableReferenceExpression (userStateName);
+                       asyncMethod.Statements.Add (BuildInvokeAsync (messageName, paramsArray, delegateField, userStateVar));
+                       
+                       CodeTypeDeclaration.Members.Add (am);
+                       CodeTypeDeclaration.Members.Add (asyncMethod);
+                       CodeTypeDeclaration.Members.Add (onCompleted);
+                       
+                       asyncTypes.Add (delegateType);
+               }
+               
+               CodeMemberProperty CreateArgsProperty (CodeTypeReference type, string name, int ind)
+               {
+                       CodeMemberProperty prop = new CodeMemberProperty ();
+                       prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
+                       prop.HasGet = true;
+                       prop.HasSet = false;
+                       prop.Name = name;
+                       prop.Type = type;
+                       CodeThisReferenceExpression ethis = new CodeThisReferenceExpression();
+                       CodeExpression thisResults = new CodeFieldReferenceExpression (ethis, "results");
+                       prop.GetStatements.Add (new CodeMethodInvokeExpression (ethis, "RaiseExceptionIfNecessary"));
+                       CodeArrayIndexerExpression arrValue = new CodeArrayIndexerExpression (thisResults, new CodePrimitiveExpression (ind));
+                       CodeExpression retval = new CodeCastExpression (type, arrValue);
+                       prop.GetStatements.Add (new CodeMethodReturnStatement (retval));
+                       return prop;
+               }
+               
+               internal virtual CodeExpression BuildInvokeAsync (string messageName, CodeArrayCreateExpression paramsArray, CodeExpression delegateField, CodeExpression userStateVar)
+               {
+                       CodeThisReferenceExpression ethis = new CodeThisReferenceExpression();
+                       CodeMethodInvokeExpression inv2 = new CodeMethodInvokeExpression (ethis, "InvokeAsync");
+                       inv2.Parameters.Add (new CodePrimitiveExpression (messageName));
+                       inv2.Parameters.Add (paramsArray);
+                       inv2.Parameters.Add (delegateField);
+                       inv2.Parameters.Add (userStateVar);
+                       return inv2;
+               }
+#endif
+               
                [MonoTODO]
                public void AddExtensionWarningComments (CodeCommentStatementCollection comments, ServiceDescriptionFormatExtensionCollection extensions) 
                {