File: FrameworkFork\System.ServiceModel\System\ServiceModel\Description\OperationGenerator.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
namespace System.ServiceModel.Description
{
    using Microsoft.CodeDom;
    using System.Collections.Generic;
    using System.Net.Security;
    using System.Reflection;
    using System.Runtime;
    using System.Runtime.Serialization;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Security;
    using System.ComponentModel;
    using System.Threading.Tasks;
 
    internal enum MessageContractType { None, WrappedMessageContract, BareMessageContract }
    internal interface IWrappedBodyTypeGenerator
    {
        void ValidateForParameterMode(OperationDescription operationDescription);
        void AddMemberAttributes(XmlName messageName, MessagePartDescription part, CodeAttributeDeclarationCollection attributesImported, CodeAttributeDeclarationCollection typeAttributes, CodeAttributeDeclarationCollection fieldAttributes);
        void AddTypeAttributes(string messageName, string typeNS, CodeAttributeDeclarationCollection typeAttributes, bool isEncoded);
    }
 
    internal class OperationGenerator //: IOperationBehavior, IOperationContractGenerationExtension
    {
        private Dictionary<MessagePartDescription, CodeTypeReference> _parameterTypes;
        private Dictionary<MessagePartDescription, CodeAttributeDeclarationCollection> _parameterAttributes;
        private Dictionary<MessagePartDescription, string> _specialPartName;
 
        internal OperationGenerator()
        {
        }
 
        internal Dictionary<MessagePartDescription, CodeAttributeDeclarationCollection> ParameterAttributes
        {
            get
            {
                if (_parameterAttributes == null)
                    _parameterAttributes = new Dictionary<MessagePartDescription, CodeAttributeDeclarationCollection>();
                return _parameterAttributes;
            }
        }
 
        internal Dictionary<MessagePartDescription, CodeTypeReference> ParameterTypes
        {
            get
            {
                if (_parameterTypes == null)
                    _parameterTypes = new Dictionary<MessagePartDescription, CodeTypeReference>();
                return _parameterTypes;
            }
        }
 
        internal Dictionary<MessagePartDescription, string> SpecialPartName
        {
            get
            {
                if (_specialPartName == null)
                    _specialPartName = new Dictionary<MessagePartDescription, string>();
                return _specialPartName;
            }
        }
 
        internal void GenerateOperation(OperationContractGenerationContext context, ref OperationFormatStyle style, bool isEncoded, IWrappedBodyTypeGenerator wrappedBodyTypeGenerator, Dictionary<MessagePartDescription, ICollection<CodeTypeReference>> knownTypes)
        {
            if (context == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("context"));
            if (context.Operation == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRServiceModel.OperationPropertyIsRequiredForAttributeGeneration));
 
            MethodSignatureGenerator methodSignatureGenerator = new MethodSignatureGenerator(this, context, style, isEncoded, wrappedBodyTypeGenerator, knownTypes);
            methodSignatureGenerator.GenerateSyncSignature(ref style);
 
            if (context.IsTask)
            {
                methodSignatureGenerator.GenerateTaskSignature(ref style);
            }
 
            if (context.IsAsync)
            {
                methodSignatureGenerator.GenerateAsyncSignature(ref style);
            }
        }
 
        internal static CodeAttributeDeclaration GenerateAttributeDeclaration(ServiceContractGenerator generator, Attribute attribute)
        {
            return CustomAttributeHelper.GenerateAttributeDeclaration(generator, attribute);
        }
 
        private class MethodSignatureGenerator
        {
            private readonly OperationGenerator _parent;
            private readonly OperationContractGenerationContext _context;
            private readonly OperationFormatStyle _style;
            private readonly bool _isEncoded;
            private readonly IWrappedBodyTypeGenerator _wrappedBodyTypeGenerator;
            private readonly Dictionary<MessagePartDescription, ICollection<CodeTypeReference>> _knownTypes;
 
            private CodeMemberMethod _method;
            private CodeMemberMethod _endMethod;
 
            private readonly string _contractName;
            private string _defaultName;
            private readonly string _contractNS;
            private readonly string _defaultNS;
 
            private readonly bool _oneway;
 
            private readonly MessageDescription _request;
            private readonly MessageDescription _response;
 
            private bool _isNewRequest;
            private bool _isNewResponse;
            private bool _isTaskWithOutputParameters;
            private MessageContractType _messageContractType;
 
            private IPartCodeGenerator _beginPartCodeGenerator;
            private IPartCodeGenerator _endPartCodeGenerator;
 
            internal MethodSignatureGenerator(OperationGenerator parent, OperationContractGenerationContext context, OperationFormatStyle style, bool isEncoded, IWrappedBodyTypeGenerator wrappedBodyTypeGenerator, Dictionary<MessagePartDescription, ICollection<CodeTypeReference>> knownTypes)
            {
                _parent = parent;
                _context = context;
                _style = style;
                _isEncoded = isEncoded;
                _wrappedBodyTypeGenerator = wrappedBodyTypeGenerator;
                _knownTypes = knownTypes;
                _messageContractType = context.ServiceContractGenerator.OptionsInternal.IsSet(ServiceContractGenerationOptions.TypedMessages) ? MessageContractType.WrappedMessageContract : MessageContractType.None;
 
                _contractName = context.Contract.Contract.CodeName;
                _contractNS = context.Operation.DeclaringContract.Namespace;
                _defaultNS = (style == OperationFormatStyle.Rpc) ? string.Empty : _contractNS;
                _oneway = (context.Operation.IsOneWay);
                _request = context.Operation.Messages[0];
                _response = _oneway ? null : context.Operation.Messages[1];
 
                _isNewRequest = true;
                _isNewResponse = true;
                _beginPartCodeGenerator = null;
                _endPartCodeGenerator = null;
                _isTaskWithOutputParameters = context.IsTask && context.Operation.HasOutputParameters;
 
                Fx.Assert(_oneway == (_response == null), "OperationContractGenerationContext.Operation cannot contain a null response message when the operation is not one-way");
            }
 
            internal void GenerateSyncSignature(ref OperationFormatStyle style)
            {
                _method = _context.SyncMethod;
                _endMethod = _context.SyncMethod;
                _defaultName = _method.Name;
                GenerateOperationSignatures(ref style);
            }
 
            internal void GenerateAsyncSignature(ref OperationFormatStyle style)
            {
                _method = _context.BeginMethod;
                _endMethod = _context.EndMethod;
                _defaultName = _method.Name.Substring(5);
                GenerateOperationSignatures(ref style);
            }
 
            private void GenerateOperationSignatures(ref OperationFormatStyle style)
            {
                if (_messageContractType != MessageContractType.None || this.GenerateTypedMessageForTaskWithOutputParameters())
                {
                    CheckAndSetMessageContractTypeToBare();
                    this.GenerateTypedMessageOperation(false /*hideFromEditor*/, ref style);
                }
                else if (!this.TryGenerateParameterizedOperation())
                {
                    this.GenerateTypedMessageOperation(true /*hideFromEditor*/, ref style);
                }
            }
 
            private bool GenerateTypedMessageForTaskWithOutputParameters()
            {
                if (_isTaskWithOutputParameters)
                {
                    if (_method == _context.TaskMethod)
                    {
                        _method.Comments.Add(new CodeCommentStatement(string.Format(SRServiceModel.SFxCodeGenWarning, SRServiceModel.SFxCannotImportAsParameters_OutputParameterAndTask)));
                    }
 
                    return true;
                }
 
                return false;
            }
 
            private void CheckAndSetMessageContractTypeToBare()
            {
                if (_messageContractType == MessageContractType.BareMessageContract)
                    return;
                try
                {
                    _wrappedBodyTypeGenerator.ValidateForParameterMode(_context.Operation);
                }
                catch (ParameterModeException ex)
                {
                    _messageContractType = ex.MessageContractType;
                }
            }
 
            private bool TryGenerateParameterizedOperation()
            {
                CodeParameterDeclarationExpressionCollection methodParameters, endMethodParameters = null;
                methodParameters = new CodeParameterDeclarationExpressionCollection(_method.Parameters);
                if (_endMethod != null)
                    endMethodParameters = new CodeParameterDeclarationExpressionCollection(_endMethod.Parameters);
 
                try
                {
                    GenerateParameterizedOperation();
                }
                catch (ParameterModeException ex)
                {
                    _messageContractType = ex.MessageContractType;
                    CodeMemberMethod method = _method;
                    method.Comments.Add(new CodeCommentStatement(string.Format(SRServiceModel.SFxCodeGenWarning, ex.Message)));
                    method.Parameters.Clear();
                    method.Parameters.AddRange(methodParameters);
                    if (_context.IsAsync)
                    {
                        CodeMemberMethod endMethod = _endMethod;
                        endMethod.Parameters.Clear();
                        endMethod.Parameters.AddRange(endMethodParameters);
                    }
                    return false;
                }
                return true;
            }
 
            private void GenerateParameterizedOperation()
            {
                ParameterizedMessageHelper.ValidateProtectionLevel(this);
                CreateOrOverrideActionProperties();
 
                if (this.HasUntypedMessages)
                {
                    if (!this.IsCompletelyUntyped)
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ParameterModeException(string.Format(SRServiceModel.SFxCannotImportAsParameters_Message, _context.Operation.CodeName)));
 
                    CreateUntypedMessages();
                }
                else
                {
                    ParameterizedMessageHelper.ValidateWrapperSettings(this);
                    ParameterizedMessageHelper.ValidateNoHeaders(this);
                    _wrappedBodyTypeGenerator.ValidateForParameterMode(_context.Operation);
 
                    ParameterizedMethodGenerator generator = new ParameterizedMethodGenerator(_method, _endMethod);
                    _beginPartCodeGenerator = generator.InputGenerator;
                    _endPartCodeGenerator = generator.OutputGenerator;
 
                    if (!_oneway && _response.Body.ReturnValue != null)
                    {
                        _endMethod.ReturnType = GetParameterType(_response.Body.ReturnValue);
                        ParameterizedMessageHelper.GenerateMessageParameterAttribute(_response.Body.ReturnValue, _endMethod.ReturnTypeCustomAttributes, TypeLoader.GetReturnValueName(_defaultName), _defaultNS);
                        AddAdditionalAttributes(_response.Body.ReturnValue, _endMethod.ReturnTypeCustomAttributes, _isEncoded);
                    }
 
                    GenerateMessageBodyParts(false /*generateTypedMessages*/);
                }
            }
 
            private void GenerateTypedMessageOperation(bool hideFromEditor, ref OperationFormatStyle style)
            {
                CreateOrOverrideActionProperties();
 
                if (this.HasUntypedMessages)
                {
                    CreateUntypedMessages();
                    if (this.IsCompletelyUntyped)
                        return;
                }
 
                CodeNamespace ns = _context.ServiceContractGenerator.NamespaceManager.EnsureNamespace(_contractNS);
 
                if (!_request.IsUntypedMessage)
                {
                    CodeTypeReference typedReqMessageRef = GenerateTypedMessageHeaderAndReturnValueParts(ns, _defaultName + "Request", _request, false /*isReply*/, hideFromEditor, ref _isNewRequest, out _beginPartCodeGenerator);
                    _method.Parameters.Insert(0, new CodeParameterDeclarationExpression(typedReqMessageRef, "request"));
                }
 
                if (!_oneway && !_response.IsUntypedMessage)
                {
                    CodeTypeReference typedRespMessageRef = GenerateTypedMessageHeaderAndReturnValueParts(ns, _defaultName + "Response", _response, true /*isReply*/, hideFromEditor, ref _isNewResponse, out _endPartCodeGenerator);
                    _endMethod.ReturnType = typedRespMessageRef;
                }
 
                GenerateMessageBodyParts(true /*generateTypedMessages*/);
 
                if (!_isEncoded)
                    style = OperationFormatStyle.Document;
            }
 
            private CodeTypeReference GenerateTypedMessageHeaderAndReturnValueParts(CodeNamespace ns, string defaultName, MessageDescription message, bool isReply, bool hideFromEditor, ref bool isNewMessage, out IPartCodeGenerator partCodeGenerator)
            {
                CodeTypeReference typedMessageRef;
                if (TypedMessageHelper.FindGeneratedTypedMessage(_context.Contract, message, out typedMessageRef))
                {
                    partCodeGenerator = null;
                    isNewMessage = false;
                }
                else
                {
                    UniqueCodeNamespaceScope namespaceScope = new UniqueCodeNamespaceScope(ns);
 
                    CodeTypeDeclaration typedMessageDecl = _context.Contract.TypeFactory.CreateClassType();
                    string messageName = XmlName.IsNullOrEmpty(message.MessageName) ? null : message.MessageName.DecodedName;
                    typedMessageRef = namespaceScope.AddUnique(typedMessageDecl, messageName, defaultName);
 
                    TypedMessageHelper.AddGeneratedTypedMessage(_context.Contract, message, typedMessageRef);
 
                    if (_messageContractType == MessageContractType.BareMessageContract && message.Body.WrapperName != null)
                        WrapTypedMessage(ns, typedMessageDecl.Name, message, isReply, _context.IsInherited, hideFromEditor);
 
                    partCodeGenerator = new TypedMessagePartCodeGenerator(typedMessageDecl);
 
                    if (hideFromEditor)
                    {
                        TypedMessageHelper.AddEditorBrowsableAttribute(typedMessageDecl.CustomAttributes);
                    }
                    TypedMessageHelper.GenerateWrapperAttribute(message, partCodeGenerator);
                    TypedMessageHelper.GenerateProtectionLevelAttribute(message, partCodeGenerator);
 
                    foreach (MessageHeaderDescription setting in message.Headers)
                        GenerateHeaderPart(setting, partCodeGenerator);
 
                    if (isReply && message.Body.ReturnValue != null)
                    {
                        GenerateBodyPart(0, message.Body.ReturnValue, partCodeGenerator, true, _isEncoded, _defaultNS);
                    }
                }
                return typedMessageRef;
            }
 
 
            private bool IsCompletelyUntyped
            {
                get
                {
                    bool isRequestMessage = _request != null && _request.IsUntypedMessage;
                    bool isResponseMessage = _response != null && _response.IsUntypedMessage;
 
                    if (isRequestMessage && isResponseMessage)
                        return true;
                    else if (isResponseMessage && _request == null || IsEmpty(_request))
                        return true;
                    else if (isRequestMessage && _response == null || IsEmpty(_response))
                        return true;
                    else
                        return false;
                }
            }
 
            private bool IsEmpty(MessageDescription message)
            {
                return (message.Body.Parts.Count == 0 && message.Headers.Count == 0);
            }
 
            private bool HasUntypedMessages
            {
                get
                {
                    bool isRequestMessage = _request != null && _request.IsUntypedMessage;
                    bool isResponseMessage = _response != null && _response.IsUntypedMessage;
                    return (isRequestMessage || isResponseMessage);
                }
            }
 
            private void CreateUntypedMessages()
            {
                bool isRequestMessage = _request != null && _request.IsUntypedMessage;
                bool isResponseMessage = _response != null && _response.IsUntypedMessage;
 
                if (isRequestMessage)
                    _method.Parameters.Insert(0, new CodeParameterDeclarationExpression(_context.ServiceContractGenerator.GetCodeTypeReference((typeof(Message))), "request"));
                if (isResponseMessage)
                    _endMethod.ReturnType = _context.ServiceContractGenerator.GetCodeTypeReference(typeof(Message));
            }
 
            private void CreateOrOverrideActionProperties()
            {
                if (_request != null)
                {
                    CustomAttributeHelper.CreateOrOverridePropertyDeclaration(
                        CustomAttributeHelper.FindOrCreateAttributeDeclaration<OperationContractAttribute>(_method.CustomAttributes), OperationContractAttribute.ActionPropertyName, _request.Action);
                }
                if (_response != null)
                {
                    CustomAttributeHelper.CreateOrOverridePropertyDeclaration(
                        CustomAttributeHelper.FindOrCreateAttributeDeclaration<OperationContractAttribute>(_method.CustomAttributes), OperationContractAttribute.ReplyActionPropertyName, _response.Action);
                }
            }
 
            private interface IPartCodeGenerator
            {
                CodeAttributeDeclarationCollection AddPart(CodeTypeReference type, ref string name);
                CodeAttributeDeclarationCollection MessageLevelAttributes { get; }
                void EndCodeGeneration();
            }
 
            private class ParameterizedMethodGenerator
            {
                private ParametersPartCodeGenerator _ins;
                private ParametersPartCodeGenerator _outs;
                private bool _isSync;
 
                internal ParameterizedMethodGenerator(CodeMemberMethod beginMethod, CodeMemberMethod endMethod)
                {
                    _ins = new ParametersPartCodeGenerator(this, beginMethod.Name, beginMethod.Parameters, beginMethod.CustomAttributes, FieldDirection.In);
                    _outs = new ParametersPartCodeGenerator(this, beginMethod.Name, endMethod.Parameters, beginMethod.CustomAttributes, FieldDirection.Out);
                    _isSync = (beginMethod == endMethod);
                }
 
                internal CodeParameterDeclarationExpression GetOrCreateParameter(CodeTypeReference type, string name, FieldDirection direction, ref int index, out bool createdNew)
                {
                    Fx.Assert(Microsoft.CodeDom.Compiler.CodeGenerator.IsValidLanguageIndependentIdentifier(name), String.Format(System.Globalization.CultureInfo.InvariantCulture, "Type name '{0}' is not ValidLanguageIndependentIdentifier!", name));
                    ParametersPartCodeGenerator existingParams = direction != FieldDirection.In ? _ins : _outs;
                    int i = index;
                    CodeParameterDeclarationExpression existing = existingParams.GetParameter(name, ref i);
                    bool isRef = existing != null && existing.Type.BaseType == type.BaseType;
                    if (isRef)
                    {
                        existing.Direction = FieldDirection.Ref;
                        if (_isSync)
                        {
                            index = i + 1;
                            createdNew = false;
                            return existing;
                        }
                    }
 
                    CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression();
                    paramDecl.Name = name;
                    paramDecl.Type = type;
                    paramDecl.Direction = direction;
                    if (isRef)
                        paramDecl.Direction = FieldDirection.Ref;
 
                    createdNew = true;
 
                    return paramDecl;
                }
 
                internal IPartCodeGenerator InputGenerator
                {
                    get
                    {
                        return _ins;
                    }
                }
 
                internal IPartCodeGenerator OutputGenerator
                {
                    get
                    {
                        return _outs;
                    }
                }
 
                private class ParametersPartCodeGenerator : IPartCodeGenerator
                {
                    private ParameterizedMethodGenerator _parent;
                    private FieldDirection _direction;
                    private CodeParameterDeclarationExpressionCollection _parameters;
                    private CodeAttributeDeclarationCollection _messageAttrs;
                    private string _methodName;
                    private int _index;
 
                    internal ParametersPartCodeGenerator(ParameterizedMethodGenerator parent, string methodName, CodeParameterDeclarationExpressionCollection parameters, CodeAttributeDeclarationCollection messageAttrs, FieldDirection direction)
                    {
                        _parent = parent;
                        _methodName = methodName;
                        _parameters = parameters;
                        _messageAttrs = messageAttrs;
                        _direction = direction;
                        _index = 0;
                    }
 
                    public bool NameExists(string name)
                    {
                        if (String.Compare(name, _methodName, StringComparison.OrdinalIgnoreCase) == 0)
                            return true;
                        int index = 0;
                        return GetParameter(name, ref index) != null;
                    }
 
                    CodeAttributeDeclarationCollection IPartCodeGenerator.AddPart(CodeTypeReference type, ref string name)
                    {
                        bool createdNew;
                        name = UniqueCodeIdentifierScope.MakeValid(name, "param");
                        CodeParameterDeclarationExpression paramDecl = _parent.GetOrCreateParameter(type, name, _direction, ref _index, out createdNew);
                        if (createdNew)
                        {
                            paramDecl.Name = GetUniqueParameterName(paramDecl.Name, this);
                            _parameters.Insert(_index++, paramDecl);
                        }
 
                        name = paramDecl.Name;
                        if (!createdNew)
                            return null;
                        return paramDecl.CustomAttributes;
                    }
 
 
                    internal CodeParameterDeclarationExpression GetParameter(string name, ref int index)
                    {
                        for (int i = index; i < _parameters.Count; i++)
                        {
                            CodeParameterDeclarationExpression parameter = _parameters[i];
                            if (String.Compare(parameter.Name, name, StringComparison.OrdinalIgnoreCase) == 0)
                            {
                                index = i;
                                return parameter;
                            }
                        }
 
                        return null;
                    }
 
                    CodeAttributeDeclarationCollection IPartCodeGenerator.MessageLevelAttributes
                    {
                        get
                        {
                            return _messageAttrs;
                        }
                    }
 
                    void IPartCodeGenerator.EndCodeGeneration()
                    {
                    }
 
                    private static string GetUniqueParameterName(string name, ParametersPartCodeGenerator parameters)
                    {
                        return NamingHelper.GetUniqueName(name, DoesParameterNameExist, parameters);
                    }
                    private static bool DoesParameterNameExist(string name, object parametersObject)
                    {
                        return ((ParametersPartCodeGenerator)parametersObject).NameExists(name);
                    }
                }
            }
 
 
            private class TypedMessagePartCodeGenerator : IPartCodeGenerator
            {
                private CodeTypeDeclaration _typeDecl;
                private UniqueCodeIdentifierScope _memberScope;
 
                internal TypedMessagePartCodeGenerator(CodeTypeDeclaration typeDecl)
                {
                    _typeDecl = typeDecl;
                    _memberScope = new UniqueCodeIdentifierScope();
                    _memberScope.AddReserved(typeDecl.Name);
                }
 
                CodeAttributeDeclarationCollection IPartCodeGenerator.AddPart(CodeTypeReference type, ref string name)
                {
                    CodeMemberField memberDecl = new CodeMemberField();
                    memberDecl.Name = name = _memberScope.AddUnique(name, "member");
                    memberDecl.Type = type;
                    memberDecl.Attributes = MemberAttributes.Public;
                    _typeDecl.Members.Add(memberDecl);
                    return memberDecl.CustomAttributes;
                }
 
                CodeAttributeDeclarationCollection IPartCodeGenerator.MessageLevelAttributes
                {
                    get
                    {
                        return _typeDecl.CustomAttributes;
                    }
                }
 
                void IPartCodeGenerator.EndCodeGeneration()
                {
                    TypedMessageHelper.GenerateConstructors(_typeDecl);
                }
            }
 
            private void WrapTypedMessage(CodeNamespace ns, string typeName, MessageDescription messageDescription, bool isReply, bool isInherited, bool hideFromEditor)
            {
                Fx.Assert(String.IsNullOrEmpty(typeName) || Microsoft.CodeDom.Compiler.CodeGenerator.IsValidLanguageIndependentIdentifier(typeName), String.Format(System.Globalization.CultureInfo.InvariantCulture, "Type name '{0}' is not ValidLanguageIndependentIdentifier!", typeName));
                UniqueCodeNamespaceScope namespaceScope = new UniqueCodeNamespaceScope(ns);
                CodeTypeDeclaration wrapperTypeDecl = _context.Contract.TypeFactory.CreateClassType();
                CodeTypeReference wrapperTypeRef = namespaceScope.AddUnique(wrapperTypeDecl, typeName + "Body", "Body");
 
                if (hideFromEditor)
                {
                    TypedMessageHelper.AddEditorBrowsableAttribute(wrapperTypeDecl.CustomAttributes);
                }
 
                string defaultNS = GetWrapperNamespace(messageDescription);
                string messageName = XmlName.IsNullOrEmpty(messageDescription.MessageName) ? null : messageDescription.MessageName.DecodedName;
                _wrappedBodyTypeGenerator.AddTypeAttributes(messageName, defaultNS, wrapperTypeDecl.CustomAttributes, _isEncoded);
 
                IPartCodeGenerator partGenerator = new TypedMessagePartCodeGenerator(wrapperTypeDecl);
                System.Net.Security.ProtectionLevel protectionLevel = System.Net.Security.ProtectionLevel.None;
                bool isProtectionLevelSetExplicitly = false;
 
                if (messageDescription.Body.ReturnValue != null)
                {
                    AddWrapperPart(messageDescription.MessageName, _wrappedBodyTypeGenerator, partGenerator, messageDescription.Body.ReturnValue, wrapperTypeDecl.CustomAttributes);
                    protectionLevel = ProtectionLevelHelper.Max(protectionLevel, messageDescription.Body.ReturnValue.ProtectionLevel);
                    if (messageDescription.Body.ReturnValue.HasProtectionLevel)
                        isProtectionLevelSetExplicitly = true;
                }
 
                List<CodeTypeReference> wrapperKnownTypes = new List<CodeTypeReference>();
                foreach (MessagePartDescription part in messageDescription.Body.Parts)
                {
                    AddWrapperPart(messageDescription.MessageName, _wrappedBodyTypeGenerator, partGenerator, part, wrapperTypeDecl.CustomAttributes);
                    protectionLevel = ProtectionLevelHelper.Max(protectionLevel, part.ProtectionLevel);
                    if (part.HasProtectionLevel)
                        isProtectionLevelSetExplicitly = true;
 
                    ICollection<CodeTypeReference> knownTypesForPart = null;
                    if (_knownTypes != null && _knownTypes.TryGetValue(part, out knownTypesForPart))
                    {
                        foreach (CodeTypeReference typeReference in knownTypesForPart)
                            wrapperKnownTypes.Add(typeReference);
                    }
                }
                messageDescription.Body.Parts.Clear();
 
                MessagePartDescription wrapperPart = new MessagePartDescription(messageDescription.Body.WrapperName, messageDescription.Body.WrapperNamespace);
                if (_knownTypes != null)
                    _knownTypes.Add(wrapperPart, wrapperKnownTypes);
                if (isProtectionLevelSetExplicitly)
                    wrapperPart.ProtectionLevel = protectionLevel;
                messageDescription.Body.WrapperName = null;
                messageDescription.Body.WrapperNamespace = null;
                if (isReply)
                    messageDescription.Body.ReturnValue = wrapperPart;
                else
                    messageDescription.Body.Parts.Add(wrapperPart);
                TypedMessageHelper.GenerateConstructors(wrapperTypeDecl);
                _parent.ParameterTypes.Add(wrapperPart, wrapperTypeRef);
                _parent.SpecialPartName.Add(wrapperPart, "Body");
            }
 
            private string GetWrapperNamespace(MessageDescription messageDescription)
            {
                string defaultNS = _defaultNS;
                if (messageDescription.Body.ReturnValue != null)
                    defaultNS = messageDescription.Body.ReturnValue.Namespace;
                else if (messageDescription.Body.Parts.Count > 0)
                    defaultNS = messageDescription.Body.Parts[0].Namespace;
                return defaultNS;
            }
 
            private void GenerateMessageBodyParts(bool generateTypedMessages)
            {
                int order = 0;
                if (_isNewRequest)
                {
                    foreach (MessagePartDescription setting in _request.Body.Parts)
                        GenerateBodyPart(order++, setting, _beginPartCodeGenerator, generateTypedMessages, _isEncoded, _defaultNS);
                }
 
                if (!_oneway && _isNewResponse)
                {
                    order = _response.Body.ReturnValue != null ? 1 : 0;
                    foreach (MessagePartDescription setting in _response.Body.Parts)
                        GenerateBodyPart(order++, setting, _endPartCodeGenerator, generateTypedMessages, _isEncoded, _defaultNS);
                }
                if (_isNewRequest)
                {
                    if (_beginPartCodeGenerator != null)
                        _beginPartCodeGenerator.EndCodeGeneration();
                }
                if (_isNewResponse)
                {
                    if (_endPartCodeGenerator != null)
                        _endPartCodeGenerator.EndCodeGeneration();
                }
            }
 
            private void AddWrapperPart(XmlName messageName, IWrappedBodyTypeGenerator wrappedBodyTypeGenerator, IPartCodeGenerator partGenerator, MessagePartDescription part, CodeAttributeDeclarationCollection typeAttributes)
            {
                string fieldName = part.CodeName;
                CodeTypeReference type;
                if (part.Type == typeof(System.IO.Stream))
                    type = _context.ServiceContractGenerator.GetCodeTypeReference(typeof(byte[]));
                else
                    type = GetParameterType(part);
                CodeAttributeDeclarationCollection fieldAttributes = partGenerator.AddPart(type, ref fieldName);
 
                CodeAttributeDeclarationCollection importedAttributes = null;
 
                bool hasAttributes = _parent.ParameterAttributes.TryGetValue(part, out importedAttributes);
 
                wrappedBodyTypeGenerator.AddMemberAttributes(messageName, part, importedAttributes, typeAttributes, fieldAttributes);
                _parent.ParameterTypes.Remove(part);
                if (hasAttributes)
                    _parent.ParameterAttributes.Remove(part);
            }
 
            private void GenerateBodyPart(int order, MessagePartDescription messagePart, IPartCodeGenerator partCodeGenerator, bool generateTypedMessage, bool isEncoded, string defaultNS)
            {
                if (!generateTypedMessage) order = -1;
 
                string partName;
                if (!_parent.SpecialPartName.TryGetValue(messagePart, out partName))
                    partName = messagePart.CodeName;
 
                CodeTypeReference partType = GetParameterType(messagePart);
                CodeAttributeDeclarationCollection partAttributes = partCodeGenerator.AddPart(partType, ref partName);
 
                if (partAttributes == null)
                    return;
 
                XmlName xmlPartName = new XmlName(partName);
                if (generateTypedMessage)
                    TypedMessageHelper.GenerateMessageBodyMemberAttribute(order, messagePart, partAttributes, xmlPartName);
                else
                    ParameterizedMessageHelper.GenerateMessageParameterAttribute(messagePart, partAttributes, xmlPartName, defaultNS);
 
                AddAdditionalAttributes(messagePart, partAttributes, generateTypedMessage || isEncoded);
            }
 
            private void GenerateHeaderPart(MessageHeaderDescription setting, IPartCodeGenerator parts)
            {
                string partName;
                if (!_parent.SpecialPartName.TryGetValue(setting, out partName))
                    partName = setting.CodeName;
                CodeTypeReference partType = GetParameterType(setting);
                CodeAttributeDeclarationCollection partAttributes = parts.AddPart(partType, ref partName);
                TypedMessageHelper.GenerateMessageHeaderAttribute(setting, partAttributes, new XmlName(partName));
                AddAdditionalAttributes(setting, partAttributes, true /*isAdditionalAttributesAllowed*/);
            }
 
            private CodeTypeReference GetParameterType(MessagePartDescription setting)
            {
                if (setting.Type != null)
                    return _context.ServiceContractGenerator.GetCodeTypeReference(setting.Type);
                else if (_parent._parameterTypes.ContainsKey(setting))
                    return _parent._parameterTypes[setting];
                else
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(string.Format(SRServiceModel.SfxNoTypeSpecifiedForParameter, setting.Name)));
            }
 
            private void AddAdditionalAttributes(MessagePartDescription setting, CodeAttributeDeclarationCollection attributes, bool isAdditionalAttributesAllowed)
            {
                if (_parent._parameterAttributes != null && _parent._parameterAttributes.ContainsKey(setting))
                {
                    CodeAttributeDeclarationCollection localAttributes = _parent._parameterAttributes[setting];
                    if (localAttributes != null && localAttributes.Count > 0)
                    {
                        if (isAdditionalAttributesAllowed)
                            attributes.AddRange(localAttributes);
                        else
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ParameterModeException(string.Format(SRServiceModel.SfxUseTypedMessageForCustomAttributes, setting.Name, localAttributes[0].AttributeType.BaseType)));
                    }
                }
            }
 
 
            private static class TypedMessageHelper
            {
                internal static void GenerateProtectionLevelAttribute(MessageDescription message, IPartCodeGenerator partCodeGenerator)
                {
                    CodeAttributeDeclaration messageContractAttr = CustomAttributeHelper.FindOrCreateAttributeDeclaration<MessageContractAttribute>(partCodeGenerator.MessageLevelAttributes);
                    if (message.HasProtectionLevel)
                    {
                        messageContractAttr.Arguments.Add(new CodeAttributeArgument("ProtectionLevel",
                            new CodeFieldReferenceExpression(
                                new CodeTypeReferenceExpression(typeof(ProtectionLevel)), message.ProtectionLevel.ToString())));
                    }
                }
 
                internal static void GenerateWrapperAttribute(MessageDescription message, IPartCodeGenerator partCodeGenerator)
                {
                    CodeAttributeDeclaration messageContractAttr = CustomAttributeHelper.FindOrCreateAttributeDeclaration<MessageContractAttribute>(partCodeGenerator.MessageLevelAttributes);
                    if (message.Body.WrapperName != null)
                    {
                        // use encoded name to specify exactly what goes on the wire.
                        messageContractAttr.Arguments.Add(new CodeAttributeArgument("WrapperName",
                            new CodePrimitiveExpression(NamingHelper.CodeName(message.Body.WrapperName))));
                        messageContractAttr.Arguments.Add(new CodeAttributeArgument("WrapperNamespace",
                            new CodePrimitiveExpression(message.Body.WrapperNamespace)));
                        messageContractAttr.Arguments.Add(new CodeAttributeArgument("IsWrapped",
                            new CodePrimitiveExpression(true)));
                    }
                    else
                        messageContractAttr.Arguments.Add(new CodeAttributeArgument("IsWrapped",
                            new CodePrimitiveExpression(false)));
                }
 
                internal static void AddEditorBrowsableAttribute(CodeAttributeDeclarationCollection attributes)
                {
                    attributes.Add(ClientClassGenerator.CreateEditorBrowsableAttribute(EditorBrowsableState.Advanced));
                }
 
                internal static void AddGeneratedTypedMessage(ServiceContractGenerationContext contract, MessageDescription message, CodeTypeReference codeTypeReference)
                {
                    if (message.XsdTypeName != null && !message.XsdTypeName.IsEmpty)
                    {
                        contract.ServiceContractGenerator.GeneratedTypedMessages.Add(message, codeTypeReference);
                    }
                }
 
                internal static bool FindGeneratedTypedMessage(ServiceContractGenerationContext contract, MessageDescription message, out CodeTypeReference codeTypeReference)
                {
                    if (message.XsdTypeName == null || message.XsdTypeName.IsEmpty)
                    {
                        codeTypeReference = null;
                        return false;
                    }
                    return contract.ServiceContractGenerator.GeneratedTypedMessages.TryGetValue(message, out codeTypeReference);
                }
 
                internal static void GenerateConstructors(CodeTypeDeclaration typeDecl)
                {
                    CodeConstructor defaultCtor = new CodeConstructor();
                    defaultCtor.Attributes = MemberAttributes.Public;
                    typeDecl.Members.Add(defaultCtor);
                    CodeConstructor otherCtor = new CodeConstructor();
                    otherCtor.Attributes = MemberAttributes.Public;
                    foreach (CodeTypeMember member in typeDecl.Members)
                    {
                        CodeMemberField field = member as CodeMemberField;
                        if (field == null)
                            continue;
                        CodeParameterDeclarationExpression param = new CodeParameterDeclarationExpression(field.Type, field.Name);
                        otherCtor.Parameters.Add(param);
                        otherCtor.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name), new CodeArgumentReferenceExpression(param.Name)));
                    }
                    if (otherCtor.Parameters.Count > 0)
                        typeDecl.Members.Add(otherCtor);
                }
 
                internal static void GenerateMessageBodyMemberAttribute(int order, MessagePartDescription setting, CodeAttributeDeclarationCollection attributes, XmlName defaultName)
                {
                    GenerateMessageContractMemberAttribute<MessageBodyMemberAttribute>(order, setting, attributes, defaultName);
                }
 
                internal static void GenerateMessageHeaderAttribute(MessageHeaderDescription setting, CodeAttributeDeclarationCollection attributes, XmlName defaultName)
                {
                    if (setting.Multiple)
                        GenerateMessageContractMemberAttribute<MessageHeaderArrayAttribute>(-1, setting, attributes, defaultName);
                    else
                        GenerateMessageContractMemberAttribute<MessageHeaderAttribute>(-1, setting, attributes, defaultName);
                }
 
                private static void GenerateMessageContractMemberAttribute<T>(int order, MessagePartDescription setting, CodeAttributeDeclarationCollection attrs, XmlName defaultName)
                    where T : Attribute
                {
                    CodeAttributeDeclaration decl = CustomAttributeHelper.FindOrCreateAttributeDeclaration<T>(attrs);
 
                    if (setting.Name != defaultName.EncodedName)
                        // override name with encoded value specified in wsdl; this only works beacuse
                        // our Encoding algorithm will leave alredy encoded names untouched
                        CustomAttributeHelper.CreateOrOverridePropertyDeclaration(decl, MessageContractMemberAttribute.NamePropertyName, setting.Name);
 
                    CustomAttributeHelper.CreateOrOverridePropertyDeclaration(decl, MessageContractMemberAttribute.NamespacePropertyName, setting.Namespace);
 
                    if (setting.HasProtectionLevel)
                        CustomAttributeHelper.CreateOrOverridePropertyDeclaration(decl, MessageContractMemberAttribute.ProtectionLevelPropertyName, setting.ProtectionLevel);
 
                    if (order >= 0)
                        CustomAttributeHelper.CreateOrOverridePropertyDeclaration(decl, MessageBodyMemberAttribute.OrderPropertyName, order);
                }
            }
 
            private static class ParameterizedMessageHelper
            {
                internal static void GenerateMessageParameterAttribute(MessagePartDescription setting, CodeAttributeDeclarationCollection attributes, XmlName defaultName, string defaultNS)
                {
                    if (setting.Name != defaultName.EncodedName)
                    {
                        // override name with encoded value specified in wsdl; this only works beacuse
                        // our Encoding algorithm will leave alredy encoded names untouched
                        CustomAttributeHelper.CreateOrOverridePropertyDeclaration(
                            CustomAttributeHelper.FindOrCreateAttributeDeclaration<MessageParameterAttribute>(attributes), MessageParameterAttribute.NamePropertyName, setting.Name);
                    }
                    if (setting.Namespace != defaultNS)
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ParameterModeException(string.Format(SRServiceModel.SFxCannotImportAsParameters_NamespaceMismatch, setting.Namespace, defaultNS)));
                }
 
                internal static void ValidateProtectionLevel(MethodSignatureGenerator parent)
                {
                    if (parent._request != null && parent._request.HasProtectionLevel)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ParameterModeException(string.Format(SRServiceModel.SFxCannotImportAsParameters_MessageHasProtectionLevel, parent._request.Action == null ? "" : parent._request.Action)));
                    }
                    if (parent._response != null && parent._response.HasProtectionLevel)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ParameterModeException(string.Format(SRServiceModel.SFxCannotImportAsParameters_MessageHasProtectionLevel, parent._response.Action == null ? "" : parent._response.Action)));
                    }
                }
 
                internal static void ValidateWrapperSettings(MethodSignatureGenerator parent)
                {
                    if (parent._request.Body.WrapperName == null || (parent._response != null && parent._response.Body.WrapperName == null))
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ParameterModeException(string.Format(SRServiceModel.SFxCannotImportAsParameters_Bare, parent._context.Operation.CodeName)));
 
                    if (!StringEqualOrNull(parent._request.Body.WrapperNamespace, parent._contractNS))
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ParameterModeException(string.Format(SRServiceModel.SFxCannotImportAsParameters_DifferentWrapperNs, parent._request.MessageName, parent._request.Body.WrapperNamespace, parent._contractNS)));
 
                    XmlName defaultName = new XmlName(parent._defaultName);
                    if (!String.Equals(parent._request.Body.WrapperName, defaultName.EncodedName, StringComparison.Ordinal))
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ParameterModeException(string.Format(SRServiceModel.SFxCannotImportAsParameters_DifferentWrapperName, parent._request.MessageName, parent._request.Body.WrapperName, defaultName.EncodedName)));
 
                    if (parent._response != null)
                    {
                        if (!StringEqualOrNull(parent._response.Body.WrapperNamespace, parent._contractNS))
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ParameterModeException(string.Format(SRServiceModel.SFxCannotImportAsParameters_DifferentWrapperNs, parent._response.MessageName, parent._response.Body.WrapperNamespace, parent._contractNS)));
 
                        if (!String.Equals(parent._response.Body.WrapperName, TypeLoader.GetBodyWrapperResponseName(defaultName).EncodedName, StringComparison.Ordinal))
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ParameterModeException(string.Format(SRServiceModel.SFxCannotImportAsParameters_DifferentWrapperName, parent._response.MessageName, parent._response.Body.WrapperName, defaultName.EncodedName)));
                    }
                }
 
                internal static void ValidateNoHeaders(MethodSignatureGenerator parent)
                {
                    if (parent._request.Headers.Count > 0)
                    {
                        if (parent._isEncoded)
                        {
                            parent._context.Contract.ServiceContractGenerator.Errors.Add(new MetadataConversionError(string.Format(SRServiceModel.SFxCannotImportAsParameters_HeadersAreIgnoredInEncoded, parent._request.MessageName), true/*isWarning*/));
                        }
                        else
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ParameterModeException(string.Format(SRServiceModel.SFxCannotImportAsParameters_HeadersAreUnsupported, parent._request.MessageName)));
                    }
 
                    if (!parent._oneway && parent._response.Headers.Count > 0)
                    {
                        if (parent._isEncoded)
                            parent._context.Contract.ServiceContractGenerator.Errors.Add(new MetadataConversionError(string.Format(SRServiceModel.SFxCannotImportAsParameters_HeadersAreIgnoredInEncoded, parent._response.MessageName), true/*isWarning*/));
                        else
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ParameterModeException(string.Format(SRServiceModel.SFxCannotImportAsParameters_HeadersAreUnsupported, parent._response.MessageName)));
                    }
                }
 
                private static bool StringEqualOrNull(string overrideValue, string defaultValue)
                {
                    return overrideValue == null || String.Equals(overrideValue, defaultValue, StringComparison.Ordinal);
                }
            }
 
            internal void GenerateTaskSignature(ref OperationFormatStyle style)
            {
                _method = _context.TaskMethod;
                _endMethod = _context.TaskMethod;
                _defaultName = _context.SyncMethod.Name;
                GenerateOperationSignatures(ref style);
 
                CodeTypeReference resultType = _method.ReturnType;
                CodeTypeReference taskReturnType;
                if (resultType.BaseType == ServiceReflector.VoidType.FullName)
                {
                    taskReturnType = new CodeTypeReference(ServiceReflector.taskType);
                }
                else
                {
                    taskReturnType = new CodeTypeReference(_context.ServiceContractGenerator.GetCodeTypeReference(ServiceReflector.taskTResultType).BaseType, resultType);
                }
 
                _method.ReturnType = taskReturnType;
            }
        }
 
        private static class CustomAttributeHelper
        {
            internal static void CreateOrOverridePropertyDeclaration<V>(CodeAttributeDeclaration attribute, string propertyName, V value)
            {
                SecurityAttributeGenerationHelper.CreateOrOverridePropertyDeclaration<V>(attribute, propertyName, value);
            }
 
            internal static CodeAttributeDeclaration FindOrCreateAttributeDeclaration<T>(CodeAttributeDeclarationCollection attributes)
                where T : Attribute
            {
                return SecurityAttributeGenerationHelper.FindOrCreateAttributeDeclaration<T>(attributes);
            }
 
            internal static CodeAttributeDeclaration GenerateAttributeDeclaration(ServiceContractGenerator generator, Attribute attribute)
            {
                Type attributeType = attribute.GetType();
                Attribute defaultAttribute = (Attribute)Activator.CreateInstance(attributeType);
                MemberInfo[] publicMembers = attributeType.GetMembers(BindingFlags.Instance | BindingFlags.Public);
                Array.Sort<MemberInfo>(publicMembers,
                                       delegate (MemberInfo a, MemberInfo b)
                                       {
                                           return String.Compare(a.Name, b.Name, StringComparison.Ordinal);
                                       }
                );
                // we should create this reference through ServiceContractGenerator, which tracks referenced assemblies
                CodeAttributeDeclaration attr = new CodeAttributeDeclaration(generator.GetCodeTypeReference(attributeType));
                foreach (MemberInfo member in publicMembers)
                {
                    if (member.DeclaringType == typeof(Attribute))
                        continue;
                    FieldInfo field = member as FieldInfo;
                    if (field != null)
                    {
                        object fieldValue = field.GetValue(attribute);
                        object defaultValue = field.GetValue(defaultAttribute);
 
                        if (!object.Equals(fieldValue, defaultValue))
                            attr.Arguments.Add(new CodeAttributeArgument(field.Name, GetArgValue(fieldValue)));
                        continue;
                    }
                    PropertyInfo property = member as PropertyInfo;
                    if (property != null)
                    {
                        object propertyValue = property.GetValue(attribute, null);
                        object defaultValue = property.GetValue(defaultAttribute, null);
                        if (!object.Equals(propertyValue, defaultValue))
                            attr.Arguments.Add(new CodeAttributeArgument(property.Name, GetArgValue(propertyValue)));
                        continue;
                    }
                }
                return attr;
            }
 
            private static CodeExpression GetArgValue(object val)
            {
                Type type = val.GetType();
                TypeInfo info = type.GetTypeInfo();
                if (info.IsPrimitive || type == typeof(string))
                    return new CodePrimitiveExpression(val);
                if (info.IsEnum)
                    return new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(type), Enum.Format(type, val, "G"));
 
                Fx.Assert(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Attribute generation is not supported for argument type {0}", type));
                return null;
            }
        }
    }
 
    internal class ParameterModeException : Exception
    {
        private MessageContractType _messageContractType = MessageContractType.WrappedMessageContract;
        public ParameterModeException() { }
        public ParameterModeException(string message) : base(message) { }
        public MessageContractType MessageContractType
        {
            get { return _messageContractType; }
            set { _messageContractType = value; }
        }
    }
}