//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ namespace System.Web.Services.Description { using System.Web.Services; using System.Web.Services.Protocols; using System.Xml; using System.Xml.Serialization; using System.Xml.Schema; using System.Collections; using System; using System.Reflection; using System.CodeDom; using System.CodeDom.Compiler; using System.Web.Services.Configuration; using System.Diagnostics; using System.ComponentModel; using System.Threading; using System.EnterpriseServices; // internal class HttpMethodInfo { internal MimeParameterCollection UrlParameters; internal MimeParameterCollection MimeParameters; internal MimeReturn MimeReturn; internal string Name; internal string Href; } internal abstract class HttpProtocolImporter : ProtocolImporter { MimeImporter[] importers; ArrayList[] importedParameters; ArrayList[] importedReturns; bool hasInputPayload; ArrayList codeClasses = new ArrayList(); protected HttpProtocolImporter(bool hasInputPayload) { Type[] importerTypes = WebServicesSection.Current.MimeImporterTypes; importers = new MimeImporter[importerTypes.Length]; importedParameters = new ArrayList[importerTypes.Length]; importedReturns = new ArrayList[importerTypes.Length]; for (int i = 0; i < importers.Length; i++) { MimeImporter importer = (MimeImporter)Activator.CreateInstance(importerTypes[i]); importer.ImportContext = this; importedParameters[i] = new ArrayList(); importedReturns[i] = new ArrayList(); importers[i] = importer; } this.hasInputPayload = hasInputPayload; } // MimeParameterCollection ImportMimeParameters() { for (int i = 0; i < importers.Length; i++) { MimeParameterCollection importedParameters = importers[i].ImportParameters(); if (importedParameters != null) { this.importedParameters[i].Add(importedParameters); return importedParameters; } } return null; } MimeReturn ImportMimeReturn() { MimeReturn importedReturn; if (OperationBinding.Output.Extensions.Count == 0) { importedReturn = new MimeReturn(); importedReturn.TypeName = typeof(void).FullName; return importedReturn; } for (int i = 0; i < importers.Length; i++) { importedReturn = importers[i].ImportReturn(); if (importedReturn != null) { this.importedReturns[i].Add(importedReturn); return importedReturn; } } return null; } MimeParameterCollection ImportUrlParameters() { // HttpUrlEncodedBinding httpUrlEncodedBinding = (HttpUrlEncodedBinding)OperationBinding.Input.Extensions.Find(typeof(HttpUrlEncodedBinding)); if (httpUrlEncodedBinding == null) return new MimeParameterCollection(); return ImportStringParametersMessage(); } internal MimeParameterCollection ImportStringParametersMessage() { MimeParameterCollection parameters = new MimeParameterCollection(); foreach (MessagePart part in InputMessage.Parts) { MimeParameter parameter = ImportUrlParameter(part); if (parameter == null) return null; parameters.Add(parameter); } return parameters; } MimeParameter ImportUrlParameter(MessagePart part) { // MimeParameter parameter = new MimeParameter(); parameter.Name = CodeIdentifier.MakeValid(XmlConvert.DecodeName(part.Name)); parameter.TypeName = IsRepeatingParameter(part) ? typeof(string[]).FullName : typeof(string).FullName; return parameter; } bool IsRepeatingParameter(MessagePart part) { XmlSchemaComplexType type = (XmlSchemaComplexType)Schemas.Find(part.Type, typeof(XmlSchemaComplexType)); if (type == null) return false; if (type.ContentModel == null) return false; if (type.ContentModel.Content == null) throw new ArgumentException(Res.GetString(Res.Missing2, type.Name, type.ContentModel.GetType().Name), "part"); if (type.ContentModel.Content is XmlSchemaComplexContentExtension) { return ((XmlSchemaComplexContentExtension)type.ContentModel.Content).BaseTypeName == new XmlQualifiedName(Soap.ArrayType, Soap.Encoding); } else if (type.ContentModel.Content is XmlSchemaComplexContentRestriction) { return ((XmlSchemaComplexContentRestriction)type.ContentModel.Content).BaseTypeName == new XmlQualifiedName(Soap.ArrayType, Soap.Encoding); } return false; } static void AppendMetadata(CodeAttributeDeclarationCollection from, CodeAttributeDeclarationCollection to) { foreach (CodeAttributeDeclaration attr in from) to.Add(attr); } CodeMemberMethod GenerateMethod(HttpMethodInfo method) { MimeParameterCollection parameters = method.MimeParameters != null ? method.MimeParameters : method.UrlParameters; string[] parameterTypeNames = new string[parameters.Count]; string[] parameterNames = new string[parameters.Count]; for (int i = 0; i < parameters.Count; i++) { MimeParameter param = parameters[i]; parameterNames[i] = param.Name; parameterTypeNames[i] = param.TypeName; } CodeAttributeDeclarationCollection metadata = new CodeAttributeDeclarationCollection(); CodeExpression[] formatterTypes = new CodeExpression[2]; if (method.MimeReturn.ReaderType == null) { formatterTypes[0] = new CodeTypeOfExpression(typeof(NopReturnReader).FullName); } else { formatterTypes[0] = new CodeTypeOfExpression(method.MimeReturn.ReaderType.FullName); } if (method.MimeParameters != null) formatterTypes[1] = new CodeTypeOfExpression(method.MimeParameters.WriterType.FullName); else formatterTypes[1] = new CodeTypeOfExpression(typeof(UrlParameterWriter).FullName); WebCodeGenerator.AddCustomAttribute(metadata, typeof(HttpMethodAttribute), formatterTypes, new string[0], new CodeExpression[0]); CodeMemberMethod mainCodeMethod = WebCodeGenerator.AddMethod(this.CodeTypeDeclaration, method.Name, new CodeFlags[parameterTypeNames.Length], parameterTypeNames, parameterNames, method.MimeReturn.TypeName, metadata, CodeFlags.IsPublic | (Style == ServiceDescriptionImportStyle.Client ? 0 : CodeFlags.IsAbstract)); AppendMetadata(method.MimeReturn.Attributes, mainCodeMethod.ReturnTypeCustomAttributes); mainCodeMethod.Comments.Add(new CodeCommentStatement(Res.GetString(Res.CodeRemarks), true)); for (int i = 0; i < parameters.Count; i++) { AppendMetadata(parameters[i].Attributes, mainCodeMethod.Parameters[i].CustomAttributes); } if (Style == ServiceDescriptionImportStyle.Client) { bool oldAsync = (ServiceImporter.CodeGenerationOptions & CodeGenerationOptions.GenerateOldAsync) != 0; bool newAsync = (ServiceImporter.CodeGenerationOptions & CodeGenerationOptions.GenerateNewAsync) != 0 && ServiceImporter.CodeGenerator.Supports(GeneratorSupport.DeclareEvents) && ServiceImporter.CodeGenerator.Supports(GeneratorSupport.DeclareDelegates); CodeExpression[] invokeParams = new CodeExpression[3]; CreateInvokeParams(invokeParams, method, parameterNames); CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "Invoke", invokeParams); if (method.MimeReturn.ReaderType != null) { mainCodeMethod.Statements.Add(new CodeMethodReturnStatement(new CodeCastExpression(method.MimeReturn.TypeName, invoke))); } else { mainCodeMethod.Statements.Add(new CodeExpressionStatement(invoke)); } metadata = new CodeAttributeDeclarationCollection(); string[] asyncParameterTypeNames = new string[parameterTypeNames.Length + 2]; parameterTypeNames.CopyTo(asyncParameterTypeNames, 0); asyncParameterTypeNames[parameterTypeNames.Length] = typeof(AsyncCallback).FullName; asyncParameterTypeNames[parameterTypeNames.Length + 1] = typeof(object).FullName; string[] asyncParameterNames = new string[parameterNames.Length + 2]; parameterNames.CopyTo(asyncParameterNames, 0); asyncParameterNames[parameterNames.Length] = "callback"; asyncParameterNames[parameterNames.Length + 1] = "asyncState"; if (oldAsync) { CodeMemberMethod beginCodeMethod = WebCodeGenerator.AddMethod(this.CodeTypeDeclaration, "Begin" + method.Name, new CodeFlags[asyncParameterTypeNames.Length], asyncParameterTypeNames, asyncParameterNames, typeof(IAsyncResult).FullName, metadata, CodeFlags.IsPublic); beginCodeMethod.Comments.Add(new CodeCommentStatement(Res.GetString(Res.CodeRemarks), true)); invokeParams = new CodeExpression[5]; CreateInvokeParams(invokeParams, method, parameterNames); invokeParams[3] = new CodeArgumentReferenceExpression( "callback"); invokeParams[4] = new CodeArgumentReferenceExpression( "asyncState"); invoke = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "BeginInvoke", invokeParams); beginCodeMethod.Statements.Add(new CodeMethodReturnStatement(invoke)); CodeMemberMethod endCodeMethod = WebCodeGenerator.AddMethod(this.CodeTypeDeclaration, "End" + method.Name, new CodeFlags[1], new string[] { typeof(IAsyncResult).FullName }, new string[] { "asyncResult" }, method.MimeReturn.TypeName, metadata, CodeFlags.IsPublic); endCodeMethod.Comments.Add(new CodeCommentStatement(Res.GetString(Res.CodeRemarks), true)); CodeExpression expr = new CodeArgumentReferenceExpression( "asyncResult"); invoke = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "EndInvoke", new CodeExpression[] { expr }); if (method.MimeReturn.ReaderType != null) { endCodeMethod.Statements.Add(new CodeMethodReturnStatement(new CodeCastExpression(method.MimeReturn.TypeName, invoke))); } else { endCodeMethod.Statements.Add(new CodeExpressionStatement(invoke)); } } if (newAsync) { metadata = new CodeAttributeDeclarationCollection(); string uniqueMethodName = method.Name; string methodKey = MethodSignature(uniqueMethodName, method.MimeReturn.TypeName, new CodeFlags[parameterTypeNames.Length], parameterTypeNames); DelegateInfo delegateInfo = (DelegateInfo)ExportContext[methodKey]; if (delegateInfo == null) { string handlerType = ClassNames.AddUnique(uniqueMethodName + "CompletedEventHandler", uniqueMethodName); string handlerArgs = ClassNames.AddUnique(uniqueMethodName + "CompletedEventArgs", uniqueMethodName); delegateInfo = new DelegateInfo(handlerType, handlerArgs); } string handlerName = MethodNames.AddUnique(uniqueMethodName + "Completed", uniqueMethodName); string asyncName = MethodNames.AddUnique(uniqueMethodName + "Async", uniqueMethodName); string callbackMember = MethodNames.AddUnique(uniqueMethodName + "OperationCompleted", uniqueMethodName); string callbackName = MethodNames.AddUnique("On" + uniqueMethodName + "OperationCompleted", uniqueMethodName); // public event xxxCompletedEventHandler xxxCompleted; WebCodeGenerator.AddEvent(this.CodeTypeDeclaration.Members, delegateInfo.handlerType, handlerName); // private SendOrPostCallback xxxOperationCompleted; WebCodeGenerator.AddCallbackDeclaration(this.CodeTypeDeclaration.Members, callbackMember); // create the pair of xxxAsync methods string userState = UniqueName("userState", parameterNames); CodeMemberMethod asyncCodeMethod = WebCodeGenerator.AddAsyncMethod(this.CodeTypeDeclaration, asyncName, parameterTypeNames, parameterNames, callbackMember, callbackName, userState); // Generate InvokeAsync call invokeParams = new CodeExpression[5]; CreateInvokeParams(invokeParams, method, parameterNames); invokeParams[3] = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), callbackMember); invokeParams[4] = new CodeArgumentReferenceExpression(userState); invoke = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "InvokeAsync", invokeParams); asyncCodeMethod.Statements.Add(invoke); // private void On_xxx_OperationCompleted(object arg) {..} bool methodHasReturn = method.MimeReturn.ReaderType != null; WebCodeGenerator.AddCallbackImplementation(this.CodeTypeDeclaration, callbackName, handlerName, delegateInfo.handlerArgs, methodHasReturn); if (ExportContext[methodKey] == null) { // public delegate void xxxCompletedEventHandler(object sender, System.ComponentModel.AsyncCompletedEventArgs args); WebCodeGenerator.AddDelegate(ExtraCodeClasses, delegateInfo.handlerType, methodHasReturn ? delegateInfo.handlerArgs : typeof(AsyncCompletedEventArgs).FullName); if (methodHasReturn) { ExtraCodeClasses.Add(WebCodeGenerator.CreateArgsClass(delegateInfo.handlerArgs, new string[] { method.MimeReturn.TypeName }, new string[] { "Result" }, ServiceImporter.CodeGenerator.Supports(GeneratorSupport.PartialTypes))); } ExportContext[methodKey] = delegateInfo; } } } return mainCodeMethod; } void CreateInvokeParams(CodeExpression[] invokeParams, HttpMethodInfo method, string[] parameterNames) { invokeParams[0] = new CodePrimitiveExpression(method.Name); CodeExpression left = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "Url"); CodeExpression right = new CodePrimitiveExpression(method.Href); invokeParams[1] = new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.Add, right); CodeExpression[] values = new CodeExpression[parameterNames.Length]; for (int i = 0; i < parameterNames.Length; i++) { values[i] = new CodeArgumentReferenceExpression( parameterNames[i]); } invokeParams[2] = new CodeArrayCreateExpression(typeof(object).FullName, values); } protected override bool IsOperationFlowSupported(OperationFlow flow) { return flow == OperationFlow.RequestResponse; } // protected override CodeMemberMethod GenerateMethod() { HttpOperationBinding httpOperationBinding = (HttpOperationBinding)OperationBinding.Extensions.Find(typeof(HttpOperationBinding)); if (httpOperationBinding == null) throw OperationBindingSyntaxException(Res.GetString(Res.MissingHttpOperationElement0)); HttpMethodInfo method = new HttpMethodInfo(); if (hasInputPayload) { method.MimeParameters = ImportMimeParameters(); if (method.MimeParameters == null) { UnsupportedOperationWarning(Res.GetString(Res.NoInputMIMEFormatsWereRecognized0)); return null; } } else { method.UrlParameters = ImportUrlParameters(); if (method.UrlParameters == null) { UnsupportedOperationWarning(Res.GetString(Res.NoInputHTTPFormatsWereRecognized0)); return null; } } method.MimeReturn = ImportMimeReturn(); if (method.MimeReturn == null) { UnsupportedOperationWarning(Res.GetString(Res.NoOutputMIMEFormatsWereRecognized0)); return null; } method.Name = MethodNames.AddUnique(MethodName, method); method.Href = httpOperationBinding.Location; return GenerateMethod(method); } protected override CodeTypeDeclaration BeginClass() { MethodNames.Clear(); ExtraCodeClasses.Clear(); CodeAttributeDeclarationCollection metadata = new CodeAttributeDeclarationCollection(); if (Style == ServiceDescriptionImportStyle.Client) { WebCodeGenerator.AddCustomAttribute(metadata, typeof(DebuggerStepThroughAttribute), new CodeExpression[0]); WebCodeGenerator.AddCustomAttribute(metadata, typeof(DesignerCategoryAttribute), new CodeExpression[] { new CodePrimitiveExpression("code") }); } Type[] requiredTypes = new Type[] { typeof(SoapDocumentMethodAttribute), typeof(XmlAttributeAttribute), typeof(WebService), typeof(Object), typeof(DebuggerStepThroughAttribute), typeof(DesignerCategoryAttribute), typeof(TransactionOption), }; WebCodeGenerator.AddImports(this.CodeNamespace, WebCodeGenerator.GetNamespacesForTypes(requiredTypes)); CodeFlags flags = 0; if (Style == ServiceDescriptionImportStyle.Server) flags = CodeFlags.IsAbstract; else if (Style == ServiceDescriptionImportStyle.ServerInterface) flags = CodeFlags.IsInterface; CodeTypeDeclaration codeClass = WebCodeGenerator.CreateClass(this.ClassName, BaseClass.FullName, new string[0], metadata, CodeFlags.IsPublic | flags, ServiceImporter.CodeGenerator.Supports(GeneratorSupport.PartialTypes)); codeClass.Comments.Add(new CodeCommentStatement(Res.GetString(Res.CodeRemarks), true)); CodeConstructor ctor = WebCodeGenerator.AddConstructor(codeClass, new string[0], new string[0], null, CodeFlags.IsPublic); ctor.Comments.Add(new CodeCommentStatement(Res.GetString(Res.CodeRemarks), true)); HttpAddressBinding httpAddressBinding = Port == null ? null : (HttpAddressBinding)Port.Extensions.Find(typeof(HttpAddressBinding)); string url = (httpAddressBinding != null) ? httpAddressBinding.Location : null; ServiceDescription serviceDescription = Binding.ServiceDescription; ProtocolImporterUtil.GenerateConstructorStatements(ctor, url, serviceDescription.AppSettingUrlKey, serviceDescription.AppSettingBaseUrl, false); codeClasses.Add(codeClass); return codeClass; } protected override void EndNamespace() { for (int i = 0; i < importers.Length; i++) { importers[i].GenerateCode((MimeReturn[])importedReturns[i].ToArray(typeof(MimeReturn)), (MimeParameterCollection[])importedParameters[i].ToArray(typeof(MimeParameterCollection))); } foreach (CodeTypeDeclaration codeClass in codeClasses) { if (codeClass.CustomAttributes == null) codeClass.CustomAttributes = new CodeAttributeDeclarationCollection(); for (int i = 0; i < importers.Length; i++) { importers[i].AddClassMetadata(codeClass); } } foreach (CodeTypeDeclaration declaration in ExtraCodeClasses) { this.CodeNamespace.Types.Add(declaration); } CodeGenerator.ValidateIdentifiers(CodeNamespace); } internal abstract Type BaseClass { get; } } }