//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ namespace System.Web.Services.Description { using System.Web.Services; using System.Web.Services.Protocols; 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.Text; using System.Xml; using System.Web.Services.Configuration; using System.Configuration; using System.Security.Permissions; using System.Threading; using System.Diagnostics; /// /// /// [To be supplied.] /// [PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")] [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] public abstract class ProtocolImporter { ServiceDescriptionImporter importer; CodeNamespace codeNamespace; CodeIdentifiers methodNames; CodeTypeDeclaration codeClass; CodeTypeDeclarationCollection classes; ServiceDescriptionImportWarnings warnings; Port port; PortType portType; Binding binding; Operation operation; OperationBinding operationBinding; bool encodedBinding; ImportContext importContext; Hashtable exportContext; Service service; Message inputMessage; Message outputMessage; string className; int bindingCount; bool anyPorts; internal void Initialize(ServiceDescriptionImporter importer) { this.importer = importer; } /// /// /// [To be supplied.] /// public ServiceDescriptionCollection ServiceDescriptions { get { return importer.ServiceDescriptions; } } /// /// /// [To be supplied.] /// public XmlSchemas Schemas { get { return importer.AllSchemas; } } /// /// /// [To be supplied.] /// public XmlSchemas AbstractSchemas { get { return importer.AbstractSchemas; } } /// /// /// [To be supplied.] /// public XmlSchemas ConcreteSchemas { get { return importer.ConcreteSchemas; } } /// /// /// [To be supplied.] /// public CodeNamespace CodeNamespace { get { return codeNamespace; } } /// /// /// [To be supplied.] /// public CodeTypeDeclaration CodeTypeDeclaration { get { return codeClass; } } internal CodeTypeDeclarationCollection ExtraCodeClasses { get { if (classes == null) classes = new CodeTypeDeclarationCollection(); return classes; } } /// /// /// [To be supplied.] /// public ServiceDescriptionImportStyle Style { get { return importer.Style; } } /// /// /// [To be supplied.] /// public ServiceDescriptionImportWarnings Warnings { get { return warnings; } set { warnings = value; } } /// /// /// [To be supplied.] /// public CodeIdentifiers ClassNames { get { return importContext.TypeIdentifiers; } } /// /// /// [To be supplied.] /// public string MethodName { get { // We don't attempt to make this unique because of method overloading return CodeIdentifier.MakeValid(XmlConvert.DecodeName(Operation.Name)); } } /// /// /// [To be supplied.] /// public string ClassName { get { return className; } } /// /// /// [To be supplied.] /// public Port Port { get { return port; } } /// /// /// [To be supplied.] /// public PortType PortType { get { return portType; } } /// /// /// [To be supplied.] /// public Binding Binding { get { return binding; } } /// /// /// [To be supplied.] /// public Service Service { get { return service; } } internal ServiceDescriptionImporter ServiceImporter { get { return importer; } } /// /// /// [To be supplied.] /// public Operation Operation { get { return operation; } } /// /// /// [To be supplied.] /// public OperationBinding OperationBinding { get { return operationBinding; } } /// /// /// [To be supplied.] /// public Message InputMessage { get { return inputMessage; } } /// /// /// [To be supplied.] /// public Message OutputMessage { get { return outputMessage; } } internal ImportContext ImportContext { get { return importContext; } } internal bool IsEncodedBinding { get { return encodedBinding; } set { encodedBinding = value; } } internal Hashtable ExportContext { get { if (exportContext == null) exportContext = new Hashtable(); return exportContext; } } internal CodeIdentifiers MethodNames { get { if (methodNames == null) methodNames = new CodeIdentifiers(); return methodNames; } } internal bool GenerateCode(CodeNamespace codeNamespace, ImportContext importContext, Hashtable exportContext) { bindingCount = 0; anyPorts = false; this.codeNamespace = codeNamespace; Hashtable supportedBindings = new Hashtable(); Hashtable unsupportedBindings = new Hashtable(); // look for ports with bindings foreach (ServiceDescription serviceDescription in ServiceDescriptions) { foreach (Service service in serviceDescription.Services) { foreach (Port port in service.Ports) { Binding binding = ServiceDescriptions.GetBinding(port.Binding); if (supportedBindings.Contains(binding)) continue; PortType portType = ServiceDescriptions.GetPortType(binding.Type); MoveToBinding(service, port, binding, portType); if (IsBindingSupported()) { bindingCount++; anyPorts = true; supportedBindings.Add(binding, binding); } else if (binding != null) unsupportedBindings[binding] = binding; } } } // no ports, look for bindings if (bindingCount == 0) { foreach (ServiceDescription serviceDescription in ServiceDescriptions) { foreach (Binding binding in serviceDescription.Bindings) { if (unsupportedBindings.Contains(binding)) continue; PortType portType = ServiceDescriptions.GetPortType(binding.Type); MoveToBinding(binding, portType); if (IsBindingSupported()) { bindingCount++; } } } } // give up if no bindings if (bindingCount == 0) { // if we generated comments return true so that the comments get written return codeNamespace.Comments.Count > 0; } this.importContext = importContext; this.exportContext = exportContext; BeginNamespace(); supportedBindings.Clear(); foreach (ServiceDescription serviceDescription in ServiceDescriptions) { if (anyPorts) { foreach (Service service in serviceDescription.Services) { foreach (Port port in service.Ports) { Binding binding = ServiceDescriptions.GetBinding(port.Binding); PortType portType = ServiceDescriptions.GetPortType(binding.Type); MoveToBinding(service, port, binding, portType); if (IsBindingSupported() && !supportedBindings.Contains(binding)) { GenerateClassForBinding(); supportedBindings.Add(binding, binding); } } } } else { foreach (Binding binding in serviceDescription.Bindings) { PortType portType = ServiceDescriptions.GetPortType(binding.Type); MoveToBinding(binding, portType); if (IsBindingSupported()) { GenerateClassForBinding(); } } } } EndNamespace(); return true; } void MoveToBinding(Binding binding, PortType portType) { MoveToBinding(null, null, binding, portType); } void MoveToBinding(Service service, Port port, Binding binding, PortType portType) { this.service = service; this.port = port; this.portType = portType; this.binding = binding; this.encodedBinding = false; } void MoveToOperation(Operation operation) { this.operation = operation; operationBinding = null; foreach (OperationBinding b in binding.Operations) { if (operation.IsBoundBy(b)) { if (operationBinding != null) throw OperationSyntaxException(Res.GetString(Res.DuplicateInputOutputNames0)); operationBinding = b; } } if (operationBinding == null) { throw OperationSyntaxException(Res.GetString(Res.MissingBinding0)); } //NOTE: The following two excepions would never happen since IsBoundBy checks these conditions already. if (operation.Messages.Input != null && operationBinding.Input == null) { throw OperationSyntaxException(Res.GetString(Res.MissingInputBinding0)); } if (operation.Messages.Output != null && operationBinding.Output == null) { throw OperationSyntaxException(Res.GetString(Res.MissingOutputBinding0)); } this.inputMessage = operation.Messages.Input == null ? null : ServiceDescriptions.GetMessage(operation.Messages.Input.Message); this.outputMessage = operation.Messages.Output == null ? null : ServiceDescriptions.GetMessage(operation.Messages.Output.Message); } void GenerateClassForBinding() { try { if (bindingCount == 1 && service != null && Style != ServiceDescriptionImportStyle.ServerInterface) { // If there is only one binding, then use the name of the service className = XmlConvert.DecodeName(service.Name); } else { // If multiple bindings, then use the name of the binding className = binding.Name; if (Style == ServiceDescriptionImportStyle.ServerInterface) { // append "I" if we are generating interfaces className = "I" + CodeIdentifier.MakePascal(className); } } className = XmlConvert.DecodeName(className); className = ClassNames.AddUnique(CodeIdentifier.MakeValid(className), null); this.codeClass = BeginClass(); int methodCount = 0; for (int i = 0; i < portType.Operations.Count; i++) { MoveToOperation(portType.Operations[i]); if (!IsOperationFlowSupported(operation.Messages.Flow)) { // switch (operation.Messages.Flow) { case OperationFlow.SolicitResponse: UnsupportedOperationWarning(Res.GetString(Res.SolicitResponseIsNotSupported0)); continue; case OperationFlow.RequestResponse: UnsupportedOperationWarning(Res.GetString(Res.RequestResponseIsNotSupported0)); continue; case OperationFlow.OneWay: UnsupportedOperationWarning(Res.GetString(Res.OneWayIsNotSupported0)); continue; case OperationFlow.Notification: UnsupportedOperationWarning(Res.GetString(Res.NotificationIsNotSupported0)); continue; } } CodeMemberMethod method; try { method = GenerateMethod(); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } throw new InvalidOperationException(Res.GetString(Res.UnableToImportOperation1, operation.Name), e); } if (method != null) { AddExtensionWarningComments(codeClass.Comments, operationBinding.Extensions); if (operationBinding.Input != null) AddExtensionWarningComments(codeClass.Comments, operationBinding.Input.Extensions); if (operationBinding.Output != null) AddExtensionWarningComments(codeClass.Comments, operationBinding.Output.Extensions); methodCount++; } } bool newAsync = (ServiceImporter.CodeGenerationOptions & CodeGenerationOptions.GenerateNewAsync) != 0 && ServiceImporter.CodeGenerator.Supports(GeneratorSupport.DeclareEvents) && ServiceImporter.CodeGenerator.Supports(GeneratorSupport.DeclareDelegates); if (newAsync && methodCount > 0 && Style == ServiceDescriptionImportStyle.Client) { CodeAttributeDeclarationCollection metadata = new CodeAttributeDeclarationCollection(); string cancelAsync = "CancelAsync"; string cancelMethodName = MethodNames.AddUnique(cancelAsync, cancelAsync); CodeMemberMethod asyncCancelMethod = WebCodeGenerator.AddMethod(this.CodeTypeDeclaration, cancelMethodName, new CodeFlags[1], new string[] { typeof(object).FullName }, new string[] { "userState" }, typeof(void).FullName, metadata, CodeFlags.IsPublic | (cancelAsync != cancelMethodName ? 0 : CodeFlags.IsNew)); asyncCancelMethod.Comments.Add(new CodeCommentStatement(Res.GetString(Res.CodeRemarks), true)); CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression(new CodeBaseReferenceExpression(), cancelAsync); invoke.Parameters.Add(new CodeArgumentReferenceExpression("userState")); asyncCancelMethod.Statements.Add(invoke); } EndClass(); if (portType.Operations.Count == 0) NoMethodsGeneratedWarning(); AddExtensionWarningComments(codeClass.Comments, binding.Extensions); if (port != null) AddExtensionWarningComments(codeClass.Comments, port.Extensions); codeNamespace.Types.Add(codeClass); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } throw new InvalidOperationException(Res.GetString(Res.UnableToImportBindingFromNamespace2, binding.Name, binding.ServiceDescription.TargetNamespace), e); } } /// /// /// [To be supplied.] /// public void AddExtensionWarningComments(CodeCommentStatementCollection comments, ServiceDescriptionFormatExtensionCollection extensions) { foreach (object item in extensions) { if (!extensions.IsHandled(item)) { string name = null; string ns = null; if (item is XmlElement) { XmlElement element = (XmlElement)item; name = element.LocalName; ns = element.NamespaceURI; } else if (item is ServiceDescriptionFormatExtension) { XmlFormatExtensionAttribute[] attrs = (XmlFormatExtensionAttribute[])item.GetType().GetCustomAttributes(typeof(XmlFormatExtensionAttribute), false); if (attrs.Length > 0) { name = attrs[0].ElementName; ns = attrs[0].Namespace; } } if (name != null) { if (extensions.IsRequired(item)) { warnings |= ServiceDescriptionImportWarnings.RequiredExtensionsIgnored; AddWarningComment(comments, Res.GetString(Res.WebServiceDescriptionIgnoredRequired, name, ns)); } else { warnings |= ServiceDescriptionImportWarnings.OptionalExtensionsIgnored; AddWarningComment(comments, Res.GetString(Res.WebServiceDescriptionIgnoredOptional, name, ns)); } } } } } /// /// /// [To be supplied.] /// public void UnsupportedBindingWarning(string text) { AddWarningComment(codeClass == null ? codeNamespace.Comments : codeClass.Comments, Res.GetString(Res.TheBinding0FromNamespace1WasIgnored2, Binding.Name, Binding.ServiceDescription.TargetNamespace, text)); warnings |= ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored; } /// /// /// [To be supplied.] /// public void UnsupportedOperationWarning(string text) { AddWarningComment(codeClass == null ? codeNamespace.Comments : codeClass.Comments, Res.GetString(Res.TheOperation0FromNamespace1WasIgnored2, operation.Name, operation.PortType.ServiceDescription.TargetNamespace, text)); warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored; } /// /// /// [To be supplied.] /// public void UnsupportedOperationBindingWarning(string text) { AddWarningComment(codeClass == null ? codeNamespace.Comments : codeClass.Comments, Res.GetString(Res.TheOperationBinding0FromNamespace1WasIgnored, operationBinding.Name, operationBinding.Binding.ServiceDescription.TargetNamespace, text)); warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored; } void NoMethodsGeneratedWarning() { AddWarningComment(codeClass.Comments, Res.GetString(Res.NoMethodsWereFoundInTheWSDLForThisProtocol)); warnings |= ServiceDescriptionImportWarnings.NoMethodsGenerated; } internal static void AddWarningComment(CodeCommentStatementCollection comments, string text) { comments.Add(new CodeCommentStatement(Res.GetString(Res.CodegenWarningDetails, text))); } /// /// /// [To be supplied.] /// public Exception OperationSyntaxException(string text) { return new Exception(Res.GetString(Res.TheOperationFromNamespaceHadInvalidSyntax3, operation.Name, operation.PortType.Name, operation.PortType.ServiceDescription.TargetNamespace, text)); } /// /// /// [To be supplied.] /// public Exception OperationBindingSyntaxException(string text) { return new Exception(Res.GetString(Res.TheOperationBindingFromNamespaceHadInvalid3, operationBinding.Name, operationBinding.Binding.ServiceDescription.TargetNamespace, text)); } /// /// /// [To be supplied.] /// public abstract string ProtocolName { get; } // These overridable methods have no parameters. The subclass uses properties on this // base object to obtain the information. This allows us to grow the set of // information passed to the methods over time w/o breaking anyone. They are protected // instead of public because this object is passed to extensions and we don't want // those calling these methods. /// /// /// [To be supplied.] /// protected virtual void BeginNamespace() { MethodNames.Clear(); } /// /// /// [To be supplied.] /// protected abstract bool IsBindingSupported(); /// /// /// [To be supplied.] /// protected abstract bool IsOperationFlowSupported(OperationFlow flow); /// /// /// [To be supplied.] /// protected abstract CodeTypeDeclaration BeginClass(); /// /// /// [To be supplied.] /// protected abstract CodeMemberMethod GenerateMethod(); /// /// /// [To be supplied.] /// protected virtual void EndClass() { } /// /// /// [To be supplied.] /// protected virtual void EndNamespace() { if (classes != null) { foreach (CodeTypeDeclaration declaration in classes) { codeNamespace.Types.Add(declaration); } } CodeGenerator.ValidateIdentifiers(codeNamespace); } internal static string UniqueName(string baseName, string[] scope) { CodeIdentifiers identifiers = new CodeIdentifiers(); for (int i = 0; i < scope.Length; i++) { identifiers.AddUnique(scope[i], scope[i]); } return identifiers.AddUnique(baseName, baseName); } internal static string MethodSignature(string methodName, string returnType, CodeFlags[] parameterFlags, string[] parameterTypes) { Debug.Assert(parameterFlags.Length == parameterTypes.Length, "parameterFlags.Length != parameterTypes.Length"); StringBuilder sb = new StringBuilder(); sb.Append(returnType); sb.Append(" "); sb.Append(methodName); sb.Append(" ("); for (int i = 0; i < parameterTypes.Length; i++) { if ((parameterFlags[i] & CodeFlags.IsByRef) != 0) sb.Append("ref "); else if ((parameterFlags[i] & CodeFlags.IsOut) != 0) sb.Append("out "); sb.Append(parameterTypes[i]); if (i > 0) sb.Append(","); } sb.Append(")"); return sb.ToString(); } } internal class ProtocolImporterUtil { private ProtocolImporterUtil() { } internal static void GenerateConstructorStatements(CodeConstructor ctor, string url, string appSettingUrlKey, string appSettingBaseUrl, bool soap11) { CodeExpression value; bool generateFixedUrlAssignment = (url != null && url.Length > 0); bool generateConfigUrlAssignment = appSettingUrlKey != null && appSettingUrlKey.Length > 0; CodeAssignStatement assignUrlStatement = null; if (!generateFixedUrlAssignment && !generateConfigUrlAssignment) return; // this.Url property CodePropertyReferenceExpression urlPropertyReference = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "Url"); if (generateFixedUrlAssignment) { value = new CodePrimitiveExpression(url); assignUrlStatement = new CodeAssignStatement(urlPropertyReference, value); } if (generateFixedUrlAssignment && !generateConfigUrlAssignment) { ctor.Statements.Add(assignUrlStatement); } else if (generateConfigUrlAssignment) { // urlSetting local variable CodeVariableReferenceExpression urlSettingReference = new CodeVariableReferenceExpression("urlSetting"); // Generate: string urlSetting = System.Configuration.ConfigurationManager.AppSettings[""]; CodeTypeReferenceExpression codeTypeReference = new CodeTypeReferenceExpression(typeof(ConfigurationManager)); CodePropertyReferenceExpression propertyReference = new CodePropertyReferenceExpression(codeTypeReference, "AppSettings"); value = new CodeIndexerExpression(propertyReference, new CodeExpression[] { new CodePrimitiveExpression(appSettingUrlKey) }); ctor.Statements.Add(new CodeVariableDeclarationStatement(typeof(string), "urlSetting", value)); if (appSettingBaseUrl == null || appSettingBaseUrl.Length == 0) { // Generate: this.Url = urlSetting; value = urlSettingReference; } else { // Generate: this.Url = "http://localhost/mywebapplication/simple.asmx"; if (url == null || url.Length == 0) throw new ArgumentException(Res.GetString(Res.IfAppSettingBaseUrlArgumentIsSpecifiedThen0)); string relativeUrl = new Uri(appSettingBaseUrl).MakeRelative(new Uri(url)); CodeExpression[] parameters = new CodeExpression[] { urlSettingReference, new CodePrimitiveExpression(relativeUrl) }; value = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(typeof(System.String)), "Concat", parameters); } CodeStatement[] trueStatements = new CodeStatement[] { new CodeAssignStatement(urlPropertyReference, value) }; // Generate: if (urlSetting != null) { } else { } CodeBinaryOperatorExpression checkIfNull = new CodeBinaryOperatorExpression(urlSettingReference, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null)); if (generateFixedUrlAssignment) ctor.Statements.Add(new CodeConditionStatement(checkIfNull, trueStatements, new CodeStatement[] { assignUrlStatement })); else ctor.Statements.Add(new CodeConditionStatement(checkIfNull, trueStatements)); } } } internal class DelegateInfo { internal string handlerType; internal string handlerArgs; internal DelegateInfo(string handlerType, string handlerArgs) { this.handlerType = handlerType; this.handlerArgs = handlerArgs; } } }