#region Copyright (c) Microsoft Corporation
/// <copyright company='Microsoft Corporation'>
///    Copyright (c) Microsoft Corporation. All Rights Reserved.
///    Information Contained Herein is Proprietary and Confidential.
/// </copyright>
#endregion

using System.CodeDom;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.Xml;
using System.Xml.Schema;
using System.Security.Permissions;
using System.Linq;

#if WEB_EXTENSIONS_CODE
using System.Security;
using System.Web.Resources;
#else
using Microsoft.VSDesigner.WCF.Resources;
#endif

///
/// The VSWCFServiceContractGenerator takes a SvcMap file and it's associated metadata,
/// imports the metadata using a WsdlImporter and System.ServiceModel.ServiceContractGenerator
/// that are configured according to the options set in the SvcMap file
/// 
using Debug = System.Diagnostics.Debug;
using System.Diagnostics.CodeAnalysis;

#if WEB_EXTENSIONS_CODE
namespace System.Web.Compilation.WCFModel
#else
namespace Microsoft.VSDesigner.WCFModel
#endif
{
    /// <summary>
    /// Proxy and configuration generator
    /// </summary>
#if WEB_EXTENSIONS_CODE
    [PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust")]    
    [SecurityCritical]
    internal class VSWCFServiceContractGenerator
#else
    // We only check for CLS compliant for the public version of this class since the 
    // compiler will complain about CLS compliance not being checked for non-public classes
    [CLSCompliant(true)]
    [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
    [PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
    public class VSWCFServiceContractGenerator
#endif
    {

        #region Private backing fields
        private const string VB_LANGUAGE_NAME = "vb";

        /// <summary>
        /// Collection to hold all bindings generated by this generator
        /// </summary>
        private IEnumerable<System.ServiceModel.Channels.Binding> bindingCollection;

        /// <summary>
        /// Collection to hold all contracts generated by this generator
        /// </summary>
        private IEnumerable<ContractDescription> contractCollection;

        /// <summary>
        /// Collection to hold all endpoints generated by this generator
        /// </summary>
        private List<ServiceEndpoint> serviceEndpointList;

        /// <summary>
        /// Map from service endpoint to the channel endpoint that was actually imported
        /// </summary>
        private Dictionary<ServiceEndpoint, ChannelEndpointElement> serviceEndpointToChannelEndpointElementMap;

        /// <summary>
        /// List of contract types generate by this generator
        /// </summary>
        private List<GeneratedContractType> proxyGeneratedContractTypes;

        /// <summary>
        /// The target compile unit that contains the proxy and data contracts
        /// </summary>
        private CodeCompileUnit targetCompileUnit;

        /// <summary>
        /// Configuration object that we inserterd bindings and endpoints from the
        /// current service into. May be Null/Nothing.
        /// </summary>
        private System.Configuration.Configuration targetConfiguration;

        /// <summary>
        /// Errors encountered while generating the proxy
        /// </summary>
        private IEnumerable<ProxyGenerationError> proxyGenerationErrors;

        /// <summary>
        /// Errors encountered while importing the metadata..
        /// </summary>
        private IList<ProxyGenerationError> importErrors;

        /// <summary>
        /// Helper property that is added to Out parameters for VB
        /// </summary>
        private static CodeAttributeDeclaration outAttribute;

        /// <summary>
        /// version number for 3.5 framework
        /// </summary>
        private const int FRAMEWORK_VERSION_35 = 0x30005;

        /// <summary>
        /// list of types which are new in the 3.5 framework. 
        /// </summary>
        private static Type[] unsupportedTypesInFramework30 = new Type[] {
            typeof(DateTimeOffset),
        };


        #endregion

        #region Public read-only properties

        /// <summary>
        /// The collection of bindings generated by this generator
        /// </summary>
        /// <value></value>
        /// <remarks>
        /// </remarks>
        public IEnumerable<System.ServiceModel.Channels.Binding> BindingCollection
        {
            get
            {
                System.Diagnostics.Debug.Assert(bindingCollection != null);
                return bindingCollection;
            }
        }

        /// <summary>
        /// The collection of generated contract types
        /// </summary>
        public IEnumerable<GeneratedContractType> ProxyGeneratedContractTypes
        {
            get
            {
                System.Diagnostics.Debug.Assert(proxyGeneratedContractTypes != null);
                return proxyGeneratedContractTypes;
            }
        }

        /// <summary>
        /// The collection of errors encountered while generating the
        /// proxy. For errors related to the metadata import, use the
        /// ImportErrors property
        /// </summary>
        public IEnumerable<ProxyGenerationError> ProxyGenerationErrors
        {
            get
            {
                System.Diagnostics.Debug.Assert(proxyGenerationErrors != null);
                return proxyGenerationErrors;
            }
        }

        /// <summary>
        /// The collection of errors encountered while importing metadata.
        /// For errors related to the proxy and config generation, use the
        /// ProxyGenerationErrors property
        /// </summary>
        public IEnumerable<ProxyGenerationError> ImportErrors
        {
            get
            {
                System.Diagnostics.Debug.Assert(importErrors != null);
                return importErrors;
            }
        }

        /// <summary>
        /// The collection of contracts imported by this generator
        /// </summary>
        /// <value></value>
        /// <remarks></remarks>
        public IEnumerable<ContractDescription> ContractCollection
        {
            get
            {
                System.Diagnostics.Debug.Assert(contractCollection != null);
                return contractCollection;
            }
        }

        /// <summary>
        /// Collection of Endpoints in the service model generated by
        /// this generator
        /// </summary>
        /// <value></value>
        /// <remarks></remarks>
        public IEnumerable<ServiceEndpoint> EndpointCollection
        {
            get
            {
                System.Diagnostics.Debug.Assert(serviceEndpointList != null);
                return serviceEndpointList;
            }
        }

        /// <summary>
        /// Map from service endpoints to its corresponding channel endpoint configuration
        /// element
        /// </summary>
        public Dictionary<ServiceEndpoint, ChannelEndpointElement> EndpointMap
        {
            get
            {
                System.Diagnostics.Debug.Assert(serviceEndpointToChannelEndpointElementMap != null);
                return serviceEndpointToChannelEndpointElementMap;
            }
        }

        /// <summary>
        /// The configuratin into which we inject the bindings and endpoints. May be null/Nothing
        /// if no target configuration was provided.
        /// </summary>
        public System.Configuration.Configuration TargetConfiguration
        {
            get
            {
                // Note: it is valid for this to be NULL. Caller beware!
                return targetConfiguration;
            }
        }

        /// <summary>
        /// CodeCompileUnit containing the generated data contracts, service contracts
        /// and WCF client.
        /// </summary>
        public CodeCompileUnit TargetCompileUnit
        {
            get
            {
                System.Diagnostics.Debug.Assert(targetCompileUnit != null);
                return targetCompileUnit;
            }
        }

        #endregion

        /// <summary>
        /// Cached instance of an Out attribute that we use to patch up
        /// the codegen for VB projects (the VB code generates out parameters
        /// as ByRef)
        /// </summary>
        private static CodeAttributeDeclaration OutAttribute
        {
            get
            {
                if (outAttribute == null)
                {
                    outAttribute = new CodeAttributeDeclaration(typeof(System.Runtime.InteropServices.OutAttribute).FullName);
                }
                return outAttribute;
            }
        }

        /// <summary>
        /// protected constructor to block creating instance directly.
        /// </summary>
        /// <param name="importErrors"></param>
        /// <param name="targetCompileUnit"></param>
        /// <param name="targetConfiguration">May be null</param>
        /// <param name="bindingCollection"></param>
        /// <param name="contractCollection"></param>
        /// <param name="serviceEndpointList"></param>
        /// <param name="serviceEndpointToChannelEndpointElementMap"></param>
        /// <param name="proxyGeneratedContractTypes"></param>
        /// <param name="proxyGenerationErrors"></param>
        protected VSWCFServiceContractGenerator(
                List<ProxyGenerationError> importErrors,
                CodeCompileUnit targetCompileUnit,
                System.Configuration.Configuration targetConfiguration,
                IEnumerable<System.ServiceModel.Channels.Binding> bindingCollection,
                IEnumerable<ContractDescription> contractCollection,
                List<ServiceEndpoint> serviceEndpointList,
                Dictionary<ServiceEndpoint, ChannelEndpointElement> serviceEndpointToChannelEndpointElementMap,
                List<GeneratedContractType> proxyGeneratedContractTypes,
                IEnumerable<ProxyGenerationError> proxyGenerationErrors)
        {
            if (importErrors == null) throw new ArgumentNullException("importErrors");
            if (targetCompileUnit == null) throw new ArgumentNullException("targetCompileUnit");
            // Please note - target configuration may be NULL
            if (bindingCollection == null) throw new ArgumentNullException("bindingCollection");
            if (contractCollection == null) throw new ArgumentNullException("contractCollection");
            if (serviceEndpointList == null) throw new ArgumentNullException("serviceEndpointList");
            if (serviceEndpointToChannelEndpointElementMap == null) throw new ArgumentNullException("serviceEndpointToChannelEndpointElementMap");
            if (proxyGeneratedContractTypes == null) throw new ArgumentNullException("proxyGeneratedContractTypes");
            if (proxyGenerationErrors == null) throw new ArgumentNullException("proxyGenerationErrors");

            this.importErrors = importErrors;
            this.targetCompileUnit = targetCompileUnit;
            this.targetConfiguration = targetConfiguration;
            this.bindingCollection = bindingCollection;
            this.contractCollection = contractCollection;
            this.serviceEndpointList = serviceEndpointList;
            this.serviceEndpointToChannelEndpointElementMap = serviceEndpointToChannelEndpointElementMap;
            this.proxyGeneratedContractTypes = proxyGeneratedContractTypes;
            this.proxyGenerationErrors = proxyGenerationErrors;
        }

        /// <summary>
        /// Factory method: generate code and return the resulting VSWCFServiceContractGenerator. 
        /// </summary>
        /// <param name="svcMapFile">
        /// The SvcMapFile that lists the metadata and generation options for the service reference.
        /// </param>
        /// <param name="toolConfiguration">
        /// Configuration from which we are going to pick up WSDL and policy importer extensions as well
        /// as custom MEX bindings for metadata download. May be Null/Nothing.
        /// </param>
        /// <param name="codeDomProvider">
        /// CodeDom provider that is to be used to generate the client code.
        /// </param>
        /// <param name="proxyNamespace">
        /// CLR namespace in which to generate the client code.
        /// </param>
        /// <param name="targetConfiguration">
        /// The configuration into which we will put bindings/endpoints for this service
        /// reference. May be Null/Nothing.
        /// </param>
        /// <param name="configurationNamespace">
        /// The namespace that is to be used in configuration for this service reference.
        /// </param>
        /// <param name="serviceProviderForImportExtensions">
        /// Service provider that we'll pass on to import extensions that accept our site:ing
        /// mechanism
        /// </param>
        /// <param name="typeLoader">
        /// Type loader that can be used to find reference assemblies and/or resolve shared service and
        /// data contract types.
        /// </param>
        /// <param name="targetFrameworkVersion">
        /// The target framework version number. The higher 16 bits contains the major version number, and low 16 bits contains minor version number.
        /// </param>
        /// <param name="typedDataSetSchemaImporterExtension">
        /// Schema importer extension to be used for typed datasets.
        /// </param>
        /// <returns>
        /// A VSWCFServiceContractGenerator instance that contains the result of the generation. To get
        /// hold of the generated information, you can query it's properties.
        /// </returns>
        public static VSWCFServiceContractGenerator GenerateCodeAndConfiguration(SvcMapFile svcMapFile,
                                             System.Configuration.Configuration toolConfiguration,
                                             System.CodeDom.Compiler.CodeDomProvider codeDomProvider,
                                             string proxyNamespace,
                                             System.Configuration.Configuration targetConfiguration,
                                             string configurationNamespace,
                                             IServiceProvider serviceProviderForImportExtensions,
                                             IContractGeneratorReferenceTypeLoader typeLoader,
                                             int targetFrameworkVersion,
                                             System.Type typedDataSetSchemaImporterExtension)
        {
            if (svcMapFile == null) throw new ArgumentNullException("svcMapFile");
            if (codeDomProvider == null) throw new ArgumentNullException("codeDomProvider");
            if (typedDataSetSchemaImporterExtension == null) throw new ArgumentNullException("typedDataSetSchemaImporterExtension");

            List<ProxyGenerationError> importErrors = new List<ProxyGenerationError>();
            List<ProxyGenerationError> proxyGenerationErrors = new List<ProxyGenerationError>();

            CodeCompileUnit targetCompileUnit = new CodeCompileUnit();

            WsdlImporter wsdlImporter = CreateWsdlImporter(svcMapFile,
                                                           toolConfiguration,
                                                           targetCompileUnit,
                                                           codeDomProvider,
                                                           proxyNamespace,
                                                           serviceProviderForImportExtensions,
                                                           typeLoader,
                                                           targetFrameworkVersion,
                                                           importErrors,
                                                           typedDataSetSchemaImporterExtension);

            ServiceContractGenerator contractGenerator = CreateContractGenerator(svcMapFile.ClientOptions,
                                                                                wsdlImporter,
                                                                                targetCompileUnit,
                                                                                proxyNamespace,
                                                                                targetConfiguration,
                                                                                typeLoader,
                                                                                targetFrameworkVersion,
                                                                                importErrors);

            try
            {
                List<ServiceEndpoint> serviceEndpointList = new List<ServiceEndpoint>();
                IEnumerable<System.ServiceModel.Channels.Binding> bindingCollection;
                IEnumerable<ContractDescription> contractCollection;

                ImportWCFModel(wsdlImporter,
                                 targetCompileUnit,
                                 importErrors,
                                 out serviceEndpointList,
                                 out bindingCollection,
                                 out contractCollection);

                Dictionary<ServiceEndpoint, ChannelEndpointElement> serviceEndpointToChannelEndpointElementMap;
                List<GeneratedContractType> proxyGeneratedContractTypes;

                GenerateProxy(wsdlImporter,
                              contractGenerator,
                              targetCompileUnit,
                              proxyNamespace,
                              configurationNamespace,
                              contractCollection,
                              bindingCollection,
                              serviceEndpointList,
                              proxyGenerationErrors,
                              out serviceEndpointToChannelEndpointElementMap,
                              out proxyGeneratedContractTypes);

                if (IsVBCodeDomProvider(codeDomProvider))
                {
                    PatchOutParametersInVB(targetCompileUnit);
                }

                return new VSWCFServiceContractGenerator(importErrors,
                                                         targetCompileUnit,
                                                         targetConfiguration,
                                                         bindingCollection,
                                                         contractCollection,
                                                         serviceEndpointList,
                                                         serviceEndpointToChannelEndpointElementMap,
                                                         proxyGeneratedContractTypes,
                                                         proxyGenerationErrors);
            }
            catch (Exception ex)
            {
                // fatal error... (workaround for bug #135242)
                // We want to convert fatal error exception to a normal code generator error message,
                // so the user could find information from pervious errors to find KB topic.
                proxyGenerationErrors.Add(new ProxyGenerationError(
                                    ProxyGenerationError.GeneratorState.GenerateCode,
                                    String.Empty,
                                    ex,
                                    false));

                return new VSWCFServiceContractGenerator(importErrors,
                                                        new CodeCompileUnit(),
                                                        targetConfiguration,
                                                        new List<System.ServiceModel.Channels.Binding>(),
                                                        new List<ContractDescription>(),
                                                        new List<ServiceEndpoint>(),
                                                        new Dictionary<ServiceEndpoint, ChannelEndpointElement>(),
                                                        new List<GeneratedContractType>(),
                                                        proxyGenerationErrors);
            }

        }


        /// <summary>
        /// Instantiate and configure a ServiceContractGenerator to be used for code and config
        /// generation.
        /// </summary>
        /// <param name="proxyOptions">
        /// Options set in the SvcMap file to control the code/config generation.
        /// </param>
        /// <param name="wsdlImporter">
        /// The WsdlImporter that is to be used to import the metadata for this service reference.
        /// </param>
        /// <param name="targetCompileUnit">
        /// Compile unit into which we will generate the client code
        /// </param>
        /// <param name="proxyNamespace">
        /// The CLR namespace into which we will generate the client code.
        /// </param>
        /// <param name="targetConfiguration">
        /// Optional configuration into which we will generate the endpoints/bindings corresponding
        /// to this service reference. May be Null/Nothing, in which case we will not generate config.
        /// </param>
        /// <param name="typeLoader">
        /// Type loader that can be used to find reference assemblies and/or resolve shared service and
        /// data contract types.
        /// </param>
        /// <param name="targetFrameworkVersion">
        /// The target framework version number. The higher 16 bits contains the major version number, and low 16 bits contains minor version number.
        /// </param>
        /// <param name="importErrors">
        /// The list into which we will add any errors while importing the metadata.
        /// </param>
        /// <returns></returns>
        protected static ServiceContractGenerator CreateContractGenerator(ClientOptions proxyOptions,
                                            WsdlImporter wsdlImporter,
                                            CodeCompileUnit targetCompileUnit,
                                            string proxyNamespace,
                                            System.Configuration.Configuration targetConfiguration,
                                            IContractGeneratorReferenceTypeLoader typeLoader,
                                            int targetFrameworkVersion,
                                            IList<ProxyGenerationError> importErrors)
        {
            ServiceContractGenerator contractGenerator = new ServiceContractGenerator(targetCompileUnit, targetConfiguration);

            // We want to generate all types into the proxy namespace CLR namespace. We indicate
            // this by adding a namespace mapping from all XML namespaces ("*") to the namespace
            // the caller told us to generate the client code in.
            contractGenerator.NamespaceMappings.Add("*", proxyNamespace);

            if (proxyOptions.GenerateInternalTypes)
            {
                contractGenerator.Options |= ServiceContractGenerationOptions.InternalTypes;
            }
            else
            {
                contractGenerator.Options &= ~ServiceContractGenerationOptions.InternalTypes;
            }

            // Make sure at most one of the async options will be set: AsynchronousMethods | TaskBasedAsynchronousMethod.
            contractGenerator.Options &= ~ServiceContractGenerationOptions.AsynchronousMethods &
                                         ~ServiceContractGenerationOptions.EventBasedAsynchronousMethods &
                                         ~ServiceContractGenerationOptions.TaskBasedAsynchronousMethod;

            if (proxyOptions.GenerateTaskBasedAsynchronousMethod)
            {
                contractGenerator.Options |= ServiceContractGenerationOptions.TaskBasedAsynchronousMethod;
            }
            else if (proxyOptions.GenerateAsynchronousMethods)
            {
                contractGenerator.Options |= ServiceContractGenerationOptions.AsynchronousMethods;
                if (targetFrameworkVersion >= FRAMEWORK_VERSION_35)
                {
                    contractGenerator.Options |= ServiceContractGenerationOptions.EventBasedAsynchronousMethods;
                }
            }

            if (proxyOptions.GenerateMessageContracts)
            {
                contractGenerator.Options |= ServiceContractGenerationOptions.TypedMessages;
            }
            else
            {
                contractGenerator.Options &= ~ServiceContractGenerationOptions.TypedMessages;
            }

            // If we have a type loader, we tell the contract generator and wsdl importer about
            // all shared types and assemblies that we've specified in the proxy options...
            if (typeLoader != null)
            {
                foreach (ContractMapping mapping in proxyOptions.ServiceContractMappingList)
                {
                    try
                    {
                        Type sharedType = typeLoader.LoadType(mapping.TypeName);

                        // verify that the type is shareable - if not, we generate an error...
                        if (!IsTypeShareable(sharedType))
                        {
                            importErrors.Add(
                                    new ProxyGenerationError(
                                        ProxyGenerationError.GeneratorState.GenerateCode,
                                        String.Empty,
                                        new FormatException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_SharedTypeMustBePublic, mapping.TypeName)))
                                );
                            continue;
                        }

                        // Get a contract description corresponding to the type we wanted to share
                        ContractDescription contract = ContractDescription.GetContract(sharedType);

                        if (!String.Equals(mapping.Name, contract.Name, StringComparison.Ordinal) ||
                                !String.Equals(mapping.TargetNamespace, contract.Namespace, StringComparison.Ordinal))
                        {
                            // mismatch
                            importErrors.Add(
                                    new ProxyGenerationError(
                                        ProxyGenerationError.GeneratorState.GenerateCode,
                                        String.Empty,
                                        new FormatException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_ServiceContractMappingMissMatch, mapping.TypeName, contract.Namespace, contract.Name, mapping.TargetNamespace, mapping.Name)))
                                );
                        }

                        XmlQualifiedName qname = new XmlQualifiedName(contract.Name, contract.Namespace);
                        wsdlImporter.KnownContracts.Add(qname, contract);
                        contractGenerator.ReferencedTypes.Add(contract, sharedType);
                    }
                    catch (Exception ex)
                    {
                        importErrors.Add(new ProxyGenerationError(
                                        ProxyGenerationError.GeneratorState.GenerateCode,
                                        String.Empty,
                                        ex));
                    }
                }
            }

            foreach (NamespaceMapping namespaceMapping in proxyOptions.NamespaceMappingList)
            {
                contractGenerator.NamespaceMappings.Add(namespaceMapping.TargetNamespace, namespaceMapping.ClrNamespace);
            }

            return contractGenerator;
        }

        /// <summary>
        /// Generate Proxy Code and (if available) configuration
        /// </summary>
        /// <param name="contractGenerator">The contract generator to use</param>
        /// <param name="targetCompileUnit">Compile unit into which we should generate the client code</param>
        /// <param name="proxyNamespace">CLR namespace into which we should generate the client code</param>
        /// <param name="configurationNamespace">Namespace to use in configuration</param>
        /// <param name="contractCollection">The contracts for which we should generate code and optionally config</param>
        /// <param name="bindingCollection">The bindings we should generate config for</param>
        /// <param name="serviceEndpointList">The endpoints we should generate config for</param>
        /// <param name="proxyGenerationErrors">A list of errors encountered while generating the client</param>
        /// <param name="serviceEndpointToChannelEndpointElementMap">Map from service endpoint to the configuration element for the endpoint</param>
        /// <param name="proxyGeneratedContractTypes">The generated contract types</param>
        protected static void GenerateProxy(WsdlImporter importer,
                                            ServiceContractGenerator contractGenerator,
                                            CodeCompileUnit targetCompileUnit,
                                            string proxyNamespace,
                                            string configurationNamespace,
                                            IEnumerable<ContractDescription> contractCollection,
                                            IEnumerable<System.ServiceModel.Channels.Binding> bindingCollection,
                                            List<ServiceEndpoint> serviceEndpointList,
                                            IList<ProxyGenerationError> proxyGenerationErrors,
                                            out Dictionary<ServiceEndpoint, ChannelEndpointElement> serviceEndpointToChannelEndpointElementMap,
                                            out List<GeneratedContractType> proxyGeneratedContractTypes)
        {
            // Parameter checking
            if (serviceEndpointList == null) throw new ArgumentNullException("serviceEndpointList");
            if (bindingCollection == null) throw new ArgumentNullException("bindingCollection");
            if (contractCollection == null) throw new ArgumentNullException("contractCollection");
            if (proxyGenerationErrors == null) throw new ArgumentNullException("proxyGenerationErrors");

            proxyGeneratedContractTypes = new List<GeneratedContractType>();
            serviceEndpointToChannelEndpointElementMap = new Dictionary<ServiceEndpoint, ChannelEndpointElement>();

            try
            {
                HttpBindingExtension httpBindingEx = importer.WsdlImportExtensions.Find<HttpBindingExtension>();

                foreach (ContractDescription contract in contractCollection)
                {
                    if (httpBindingEx == null || !httpBindingEx.IsHttpBindingContract(contract) || serviceEndpointList.Any(endpoint => endpoint.Contract == contract))
                    {
                        CodeTypeReference typeReference = contractGenerator.GenerateServiceContractType(contract);
                        if (typeReference != null)
                        {
                            // keep the (targetNamespace, portType) -> CLR type map table...

                            string baseType = typeReference.BaseType;

                            GeneratedContractType generatedType = new GeneratedContractType(contract.Namespace, contract.Name, baseType, baseType);
                            proxyGeneratedContractTypes.Add(generatedType);
                        }
                    }
                }

                // We should only import the Binding & Endpoints if there is a configuration storage...
                if (contractGenerator.Configuration != null)
                {
                    foreach (ServiceEndpoint endpoint in serviceEndpointList)
                    {
                        ChannelEndpointElement endpointElement = null;
                        contractGenerator.GenerateServiceEndpoint(endpoint, out endpointElement);
                        serviceEndpointToChannelEndpointElementMap[endpoint] = endpointElement;
                    }

                    foreach (System.ServiceModel.Channels.Binding bindingDescription in bindingCollection)
                    {
                        string bindingSectionName = null;
                        string bindingConfigurationName = null;
                        // Generate binding will change the state of the contractGenerator... 
                        contractGenerator.GenerateBinding(bindingDescription, out bindingSectionName, out bindingConfigurationName);
                    }

                }

                PatchConfigurationName(proxyNamespace,
                                       configurationNamespace,
                                       proxyGeneratedContractTypes,
                                       serviceEndpointToChannelEndpointElementMap.Values,
                                       targetCompileUnit);
            }
            finally
            {
                foreach (MetadataConversionError error in contractGenerator.Errors)
                {
                    proxyGenerationErrors.Add(new ProxyGenerationError(error));
                }
            }
        }

        /// <summary>
        /// Create appropriate XmlSerializerImportOptions for the generator
        /// </summary>
        /// <param name="proxyOptions">Options for the code/config generation</param>
        /// <param name="targetCompileUnit">Compile unit we are going to generate the client code in</param>
        /// <param name="codeDomProvider">CodeDom provider for the language we are using</param>
        /// <param name="proxyNamespace">CLR namespace we'll put the client code in</param>
        /// <returns></returns>
        protected static XmlSerializerImportOptions CreateXmlSerializerImportOptions(
                                ClientOptions proxyOptions,
                                CodeCompileUnit targetCompileUnit,
                                System.CodeDom.Compiler.CodeDomProvider codeDomProvider,
                                string proxyNamespace,
                                System.Type typedDataSetSchemaImporterExtension)
        {
            System.ServiceModel.Channels.XmlSerializerImportOptions xmlSerializerOptions = new XmlSerializerImportOptions(targetCompileUnit);
            System.Web.Services.Description.WebReferenceOptions webReferenceOptions = new System.Web.Services.Description.WebReferenceOptions();

            webReferenceOptions.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties | System.Xml.Serialization.CodeGenerationOptions.GenerateOrder;

            if (proxyOptions.EnableDataBinding)
            {
                webReferenceOptions.CodeGenerationOptions |= System.Xml.Serialization.CodeGenerationOptions.EnableDataBinding;
            }

            webReferenceOptions.SchemaImporterExtensions.Add(typedDataSetSchemaImporterExtension.AssemblyQualifiedName);
            webReferenceOptions.SchemaImporterExtensions.Add(typeof(System.Data.DataSetSchemaImporterExtension).AssemblyQualifiedName);

            /* 

















*/

            xmlSerializerOptions.WebReferenceOptions = webReferenceOptions;
            xmlSerializerOptions.CodeProvider = codeDomProvider;

            xmlSerializerOptions.ClrNamespace = proxyNamespace;

            return xmlSerializerOptions;
        }

        /// <summary>
        /// Create an appropriate XsdDataContractImporter for the generator
        /// </summary>
        /// <param name="proxyOptions">Code/config generation options to use</param>
        /// <param name="targetCompileUnit">CodeCompileUnit into which we will generate the client code</param>
        /// <param name="codeDomProvider">CodeDomProvider for the language we will use to generate the client</param>
        /// <param name="proxyNamespace">CLR namespace in which the client code will be generated</param>
        /// <param name="typeLoader">Service used to resolve type/assembly names (strings) to actual Types and Assemblies</param>
        /// <param name="targetFrameworkVersion">Targetted Framework version number</param>
        /// <param name="importErrors">List of errors encountered. New errors will be added to this list</param>
        /// <returns></returns>
        protected static XsdDataContractImporter CreateDataContractImporter(
                ClientOptions proxyOptions,
                CodeCompileUnit targetCompileUnit,
                System.CodeDom.Compiler.CodeDomProvider codeDomProvider,
                string proxyNamespace,
                IContractGeneratorReferenceTypeLoader typeLoader,
                int targetFrameworkVersion,
                IList<ProxyGenerationError> importErrors)
        {
            System.Runtime.Serialization.XsdDataContractImporter xsdDataContractImporter = new System.Runtime.Serialization.XsdDataContractImporter(targetCompileUnit);
            System.Runtime.Serialization.ImportOptions options = new System.Runtime.Serialization.ImportOptions();

            options.CodeProvider = codeDomProvider;

            // We specify that we want to generate all types from all XML namespaces into
            // our proxy namespace. By default, each XML namespace get's its own CLR namespace
            options.Namespaces.Add("*", proxyNamespace);
            options.GenerateInternal = proxyOptions.GenerateInternalTypes;
            options.GenerateSerializable = proxyOptions.GenerateSerializableTypes;
            options.EnableDataBinding = proxyOptions.EnableDataBinding;
            options.ImportXmlType = proxyOptions.ImportXmlTypes;

            if (typeLoader != null)
            {
                IEnumerable<Type> referencedTypes = LoadSharedDataContractTypes(proxyOptions, typeLoader, targetFrameworkVersion, importErrors);
                if (referencedTypes != null)
                {
                    foreach (Type sharedType in referencedTypes)
                    {
                        options.ReferencedTypes.Add(sharedType);
                    }
                }

                IEnumerable<Type> referencedCollectionTypes = LoadSharedCollectionTypes(proxyOptions, typeLoader, importErrors);
                if (referencedCollectionTypes != null)
                {
                    foreach (Type collectionType in referencedCollectionTypes)
                    {
                        options.ReferencedCollectionTypes.Add(collectionType);
                    }
                }

            }

            foreach (NamespaceMapping namespaceMapping in proxyOptions.NamespaceMappingList)
            {
                options.Namespaces.Add(namespaceMapping.TargetNamespace, namespaceMapping.ClrNamespace);
            }

            xsdDataContractImporter.Options = options;

            return xsdDataContractImporter;
        }

        /// <summary>
        /// Load DataContract types which could be used in the code generator (shared types)
        /// </summary>
        /// <param name="proxyOptions">Options controlling the generation</param>
        /// <param name="typeLoader">Type loader to resolve referenced assemblies and types</param>
        /// <param name="targetFrameworkVersion">Targetted Framework version number</param>
        /// <param name="importErrors">Errors encountered while loading the shared data contracts</param>
        /// <return>
        /// A list of CLR types from referenced assemblies and/or specific types that we want to share
        /// </return>
        /// <remarks></remarks>
        [SuppressMessage("Microsoft.Usage", "CA2301:EmbeddableTypesInContainersRule", MessageId = "sharedTypeTable", Justification = "This is used within VS to get the types from reference assemblies i.e., the reference assembly for a given assembly but not across assemblies - so com interop is not an issue.")]
        protected static IEnumerable<Type> LoadSharedDataContractTypes(
                ClientOptions proxyOptions, IContractGeneratorReferenceTypeLoader typeLoader, int targetFrameworkVersion, IList<ProxyGenerationError> importErrors)
        {
            if (typeLoader == null) throw new ArgumentNullException("typeLoader");

            // the value in sharedTypeTable is why we add this type in the shared type list. 
            // if it is only added because it is in the referenced assembly, the value will be null, otherwise it contains the entry in the type inclusion list
            // if the type is also in the exclusion list, we will report an error if the type is comming from the inclusion list, but no error if it comes from a referenced assembly only.
            Dictionary<Type, ReferencedType> sharedTypeTable = new Dictionary<Type, ReferencedType>();

            // load all types in referencedAssemblies
            IEnumerable<Assembly> referencedAssemblies = LoadReferenedAssemblies(proxyOptions, typeLoader, importErrors);
            if (referencedAssemblies != null)
            {
                foreach (Assembly referencedAssembly in referencedAssemblies)
                {
                    var typeLoader2 = typeLoader as IContractGeneratorReferenceTypeLoader2;
                    if (typeLoader2 != null)
                    {
                        foreach (Type sharedType in typeLoader2.LoadExportedTypes(referencedAssembly))
                        {
                            sharedTypeTable.Add(sharedType, null);
                        }
                    }
                    else
                    {
                        // Fall back to the original approach using IContractGeneratorReferenceTypeLoader.LoadType().
                        foreach (Type typeInAssembly in referencedAssembly.GetExportedTypes())
                        {
                            try
                            {
                                // Do multi-targeting check by calling IContractGeneratorReferenceTypeLoader.LoadType().                            
                                if (typeLoader.LoadType(typeInAssembly.FullName) != null)
                                {
                                    sharedTypeTable.Add(typeInAssembly, null);
                                }
                            }
                            catch (NotSupportedException)
                            {
                                // NotSupportedException is thrown by multi-targeting check. It's normal if some types not existing in the current FX.
                                // So we can safely ---- it.
                            }
                            catch (Exception ex)
                            {
                                // fail to load one type in an assembly: warning message
                                importErrors.Add(
                                    new ProxyGenerationError(
                                        ProxyGenerationError.GeneratorState.GenerateCode,
                                        String.Empty,
                                        ex,
                                        true));
                            }
                        }
                    }
                }
            }

            // load types in DataContractTypeList
            foreach (ReferencedType referencedType in proxyOptions.ReferencedDataContractTypeList)
            {
                try
                {
                    Type sharedType = typeLoader.LoadType(referencedType.TypeName);

                    // verify...
                    if (!IsTypeShareable(sharedType))
                    {
                        importErrors.Add(
                                new ProxyGenerationError(
                                    ProxyGenerationError.GeneratorState.GenerateCode,
                                    String.Empty,
                                    new FormatException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_SharedTypeMustBePublic, referencedType.TypeName)))
                            );
                        continue;
                    }

                    sharedTypeTable[sharedType] = referencedType;
                }
                catch (Exception ex)
                {
                    importErrors.Add(new ProxyGenerationError(
                                    ProxyGenerationError.GeneratorState.GenerateCode,
                                    String.Empty,
                                    ex));
                }
            }

            // remove excluded types
            foreach (ReferencedType excludedType in proxyOptions.ExcludedTypeList)
            {
                try
                {
                    Type sharedType = typeLoader.LoadType(excludedType.TypeName);

                    if (sharedTypeTable.ContainsKey(sharedType))
                    {
                        if (sharedTypeTable[sharedType] != null)
                        {
                            // error message
                            importErrors.Add(new ProxyGenerationError(
                                            ProxyGenerationError.GeneratorState.GenerateCode,
                                            String.Empty,
                                            new Exception(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_DataContractExcludedAndIncluded, excludedType.TypeName))));
                        }
                        sharedTypeTable.Remove(sharedType);
                    }
                }
                catch (Exception ex)
                {
                    // waring message for excludedTypes
                    importErrors.Add(new ProxyGenerationError(
                                    ProxyGenerationError.GeneratorState.GenerateCode,
                                    String.Empty,
                                    ex,
                                    true));
                }
            }

            // remove unsupported types
            foreach (Type unsupportedType in GetUnsupportedTypes(targetFrameworkVersion))
            {
                sharedTypeTable.Remove(unsupportedType);
            }

            return sharedTypeTable.Keys;
        }

        /// <summary>
        /// Get list of types which are not supported in the targetted framework.
        /// </summary>
        /// <param name="targetFrameworkVersion">Targetted Framework version number</param>
        /// <return></return>
        /// <remarks></remarks>
        private static IEnumerable<Type> GetUnsupportedTypes(int targetFrameworkVersion)
        {

            if (targetFrameworkVersion < FRAMEWORK_VERSION_35)
            {
                // NOTE: do we need load those types with typeLoader?
                return unsupportedTypesInFramework30;
            }

            return new Type[0];
        }

        /// <summary>
        /// Ensure that the ConfigurationName attribute on service contracts and the channel endpoint elements all agree on the 
        /// name of the service contract.
        /// We want to avoid having root/default namespace values persisted in config, since that would require us
        /// to update config whenever the default/root namespace changes, so we make sure that we exclude it
        /// from the ConfigurationName attribute and the channel endpoint element we generate.
        /// 
        /// For VB, the root namespace is not actually present in the generated code, so we typically don't have to
        /// do anything (the configuration and proxy namespaces are equal) but for C#, we need to strip out the
        /// default namespace.
        /// </summary>
        /// <param name="proxyNamespace">
        /// CLR namespace into which we will generate the service in the CodeCompileUnit
        /// </param>
        /// <param name="configNamespace">
        /// The namespace that we expect to use in configuration.
        /// </param>
        /// <param name="generatedContracts">
        /// The contracts that we have generated
        /// </param>
        /// <param name="endpoints">
        /// All channel endpoints we have generated
        /// </param>
        /// <param name="targetCompileUnit">
        /// The compile unit into which we generated the client
        /// </param>
        private static void PatchConfigurationName(
                                    string proxyNamespace,
                                    string configNamespace,
                                    IEnumerable<GeneratedContractType> generatedContracts,
                                    IEnumerable<ChannelEndpointElement> endpoints,
                                    CodeCompileUnit targetCompileUnit
            )
        {

            // Since the name has to match between configuration and the name we put in the ConfigurationName 
            // attribute in code, we may have some patching to do - but only if the proxy namespace is not equal
            // to the configuration namespace...
            if (configNamespace != null && !configNamespace.Equals(proxyNamespace, StringComparison.Ordinal))
            {
                string proxyNamespaceHead = MakePeriodTerminatedNamespacePrefix(proxyNamespace);
                string configNamespaceHead = MakePeriodTerminatedNamespacePrefix(configNamespace);

                // We need to fix up the configuration name for all generated contracts...
                foreach (GeneratedContractType contract in generatedContracts)
                {
                    contract.ConfigurationName = ReplaceNamespace(proxyNamespaceHead, configNamespaceHead, contract.ConfigurationName);
                }

                // ..and we need to fix up all elements in config...
                foreach (ChannelEndpointElement endpoint in endpoints)
                {
                    endpoint.Contract = ReplaceNamespace(proxyNamespaceHead, configNamespaceHead, endpoint.Contract);
                }

                // ...and all ConfigurationName values in service contract attributes in the generated code as well...
                PatchConfigurationNameInServiceContractAttribute(targetCompileUnit, proxyNamespace, configNamespace);
            }
        }

        /// <summary>
        /// If the type name begins with the namespace specified in originalNamespace, replace the namespace 
        /// for the given type name with the namespace specified in the replacementNamespace parameter.
        /// </summary>
        /// <param name="originalNamespace">
        /// Original namespace to look for.
        /// Must either be an empty string ("") or end with a period (".")
        /// </param>
        /// <param name="replacementNamespace">
        /// Namespace to replace original namespace with
        /// Muse either be an empty string ("") or end with a period...
        /// </param>
        /// <param name="typeName">Typename on which to do the replacement</param>
        /// <returns>
        /// The new type name (potentially the same as passed in)
        /// </returns>
        private static string ReplaceNamespace(string originalNamespace, string replacementNamespace, string typeName)
        {
            Debug.Assert(originalNamespace.Length == 0 || originalNamespace.EndsWith(".", StringComparison.Ordinal));
            Debug.Assert(replacementNamespace.Length == 0 || replacementNamespace.EndsWith(".", StringComparison.Ordinal));
            Debug.Assert(typeName != null);

            if (typeName.StartsWith(originalNamespace, StringComparison.Ordinal))
            {
                // Strip out the original namespace and replace it with the new namespace
                return replacementNamespace + typeName.Substring(originalNamespace.Length);
            }
            else
            {
                return typeName;
            }
        }

        /// <summary>
        /// Given the namespace, return either an empty string (if the given namespace was NULL or emtpy)
        /// or a period-terminated (".") string.
        /// </summary>
        /// <param name="ns"></param>
        /// <returns>
        /// Either an empty string or a string that ends with a period (".")
        /// Can never return Null...
        /// </returns>
        private static string MakePeriodTerminatedNamespacePrefix(string ns)
        {
            if (String.IsNullOrEmpty(ns))
            {
                return "";
            }
            else if (!ns.EndsWith(".", StringComparison.Ordinal))
            {
                return ns + ".";
            }
            else
            {
                return ns;
            }
        }

        /// <summary>
        /// Determine if a type can be shared.
        /// 
        /// In order for a type to be shareable for service references, it has to be a 
        /// public type...
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        private static bool IsTypeShareable(Type t)
        {
            if (t == null)
            {
                System.Diagnostics.Debug.Fail("Why are you asking if a NULL type is shareable?");
                return false;
            }

            return t.IsPublic || t.IsNestedPublic;
        }

        /// <summary>
        /// Load referenced assemblies
        /// </summary>
        /// <param name="proxyOptions"></param>
        /// <param name="typeLoader"></param>
        /// <param name="importErrors"></param>
        /// <return></return>
        /// <remarks></remarks>
        private static IEnumerable<Assembly> LoadReferenedAssemblies(ClientOptions proxyOptions, IContractGeneratorReferenceTypeLoader typeLoader, IList<ProxyGenerationError> importErrors)
        {
            List<Assembly> referencedAssemblies = new List<Assembly>();
            if (proxyOptions.ReferenceAllAssemblies)
            {
                try
                {
                    IEnumerable<Exception> loadingErrors = null;
                    IEnumerable<Assembly> allAssemblies = null;
                    typeLoader.LoadAllAssemblies(out allAssemblies, out loadingErrors);
                    if (loadingErrors != null)
                    {
                        // treat as warning messages
                        foreach (Exception ex in loadingErrors)
                        {
                            importErrors.Add(new ProxyGenerationError(
                                            ProxyGenerationError.GeneratorState.GenerateCode,
                                            String.Empty,
                                            ex,
                                            true));
                        }
                    }

                    if (allAssemblies != null)
                    {
                        referencedAssemblies.AddRange(allAssemblies);
                    }
                }
                catch (Exception ex)
                {
                    importErrors.Add(new ProxyGenerationError(
                                    ProxyGenerationError.GeneratorState.GenerateCode,
                                    String.Empty,
                                    ex));
                }
            }

            foreach (ReferencedAssembly referencedAssembly in proxyOptions.ReferencedAssemblyList)
            {
                try
                {
                    Assembly refAssembly = typeLoader.LoadAssembly(referencedAssembly.AssemblyName);
                    if (refAssembly != null && !referencedAssemblies.Contains(refAssembly))
                    {
                        referencedAssemblies.Add(refAssembly);
                    }
                }
                catch (Exception ex)
                {
                    importErrors.Add(new ProxyGenerationError(
                                    ProxyGenerationError.GeneratorState.GenerateCode,
                                    String.Empty,
                                    ex));
                }
            }
            return referencedAssemblies;
        }

        /// <summary>
        /// Load the list of types that we have specified as collection types in our client options.
        /// The collection types will be used to generate collections in the data contracts.
        /// </summary>
        /// <param name="proxyOptions">Options specifying the list of collection types</param>
        /// <param name="typeLoader">Type loader that resolves type names to actual CLR types</param>
        /// <param name="importErrors">Errors encountered while loading the collection types</param>
        /// <return></return>
        /// <remarks></remarks>
        protected static IEnumerable<Type> LoadSharedCollectionTypes(ClientOptions proxyOptions, IContractGeneratorReferenceTypeLoader typeLoader, IList<ProxyGenerationError> importErrors)
        {
            List<Type> referencedCollectionTypes = new List<Type>();
            foreach (ReferencedCollectionType referencedCollectionMapping in proxyOptions.CollectionMappingList)
            {
                try
                {
                    Type collectionType = typeLoader.LoadType(referencedCollectionMapping.TypeName);

                    // verify...
                    if (!IsTypeShareable(collectionType))
                    {
                        importErrors.Add(
                                new ProxyGenerationError(
                                    ProxyGenerationError.GeneratorState.GenerateCode,
                                    String.Empty,
                                    new FormatException(String.Format(CultureInfo.CurrentCulture, WCFModelStrings.ReferenceGroup_SharedTypeMustBePublic, referencedCollectionMapping.TypeName)))
                            );
                        continue;
                    }

                    referencedCollectionTypes.Add(collectionType);
                }
                catch (Exception ex)
                {
                    importErrors.Add(new ProxyGenerationError(
                                    ProxyGenerationError.GeneratorState.GenerateCode,
                                    String.Empty,
                                    ex));
                }
            }
            return referencedCollectionTypes;
        }

        /// <summary>
        /// Create an appropriate WsdlImporter for the generator
        /// </summary>
        /// <param name="svcMapFile"></param>
        /// <param name="toolConfiguration"></param>
        /// <param name="targetCompileUnit"></param>
        /// <param name="codeDomProvider"></param>
        /// <param name="targetNamespace"></param>
        /// <param name="typeLoader"></param>
        /// <param name="targetFrameworkVersion">Targetted Framework version number</param>
        /// <param name="importErrors"></param>
        /// <param name="typedDataSetSchemaImporterExtension"></param>
        /// <returns></returns>
        protected static WsdlImporter CreateWsdlImporter(SvcMapFile svcMapFile,
                                                      System.Configuration.Configuration toolConfiguration,
                                                      CodeCompileUnit targetCompileUnit,
                                                      System.CodeDom.Compiler.CodeDomProvider codeDomProvider,
                                                      string targetNamespace,
                                                      IServiceProvider serviceProviderForImportExtensions,
                                                      IContractGeneratorReferenceTypeLoader typeLoader,
                                                      int targetFrameworkVersion,
                                                      IList<ProxyGenerationError> importErrors,
                                                      System.Type typedDataSetSchemaImporterExtension)
        {
            List<MetadataSection> metadataSections = CollectMetadataDocuments(svcMapFile.MetadataList, importErrors);

            WsdlImporter importer = null;

            ClientOptions.ProxySerializerType serializerType = svcMapFile.ClientOptions.Serializer;
            if (serializerType == ClientOptions.ProxySerializerType.Auto && ContainsHttpBindings(metadataSections))
            {
                // NOTE: HTTP Get/Post binding indicates an old web service. We use XmlSerializer to prevent generating dup classes.
                // Please check devdiv bug 94078
                serializerType = ClientOptions.ProxySerializerType.XmlSerializer;
            }

            if (toolConfiguration != null)
            {
                ServiceModelSectionGroup serviceModelSection = ServiceModelSectionGroup.GetSectionGroup(toolConfiguration);

                if (serviceModelSection != null)
                {
                    Collection<IWsdlImportExtension> wsdlImportExtensions = serviceModelSection.Client.Metadata.LoadWsdlImportExtensions();
                    Collection<IPolicyImportExtension> policyImportExtensions = serviceModelSection.Client.Metadata.LoadPolicyImportExtensions();

                    // If we have specified a specific serializer to use, we remove
                    // the other serializer...
                    switch (serializerType)
                    {
                        case ClientOptions.ProxySerializerType.DataContractSerializer:
                            RemoveExtension(typeof(XmlSerializerMessageContractImporter), wsdlImportExtensions);
                            break;
                        case ClientOptions.ProxySerializerType.XmlSerializer:
                            RemoveExtension(typeof(DataContractSerializerMessageContractImporter), wsdlImportExtensions);
                            break;
                        case ClientOptions.ProxySerializerType.Auto:
                            break;
                        default:
                            System.Diagnostics.Debug.Fail("Unknown serializer");
                            break;
                    }

                    ProvideImportExtensionsWithContextInformation(svcMapFile, serviceProviderForImportExtensions, wsdlImportExtensions, policyImportExtensions);

                    wsdlImportExtensions.Add(new HttpBindingExtension());

                    // Create Importer...
                    importer = new WsdlImporter(new MetadataSet(metadataSections), policyImportExtensions, wsdlImportExtensions);
                }
            }

            if (importer == null)
            {
                importer = new WsdlImporter(new MetadataSet(metadataSections));
            }

            // DevDiv 124333 - Always add DataContract importer (even if we are in XmlSerializerMode) to 
            // enable importing Fault contracts...
            importer.State.Add(typeof(System.Runtime.Serialization.XsdDataContractImporter),
                           CreateDataContractImporter(svcMapFile.ClientOptions, targetCompileUnit, codeDomProvider, targetNamespace, typeLoader, targetFrameworkVersion, importErrors));

            if (serializerType != ClientOptions.ProxySerializerType.DataContractSerializer)
            {
                importer.State.Add(typeof(System.ServiceModel.Channels.XmlSerializerImportOptions),
                               CreateXmlSerializerImportOptions(svcMapFile.ClientOptions,
                                                                targetCompileUnit,
                                                                codeDomProvider,
                                                                targetNamespace,
                                                                typedDataSetSchemaImporterExtension));
            }

            // Read the UseSerializerForFaults from Reference.svcmap, create a FaultImportOptions using this information
            // and pass it to WSDL Importer.
            FaultImportOptions faultOptions = new FaultImportOptions();
            faultOptions.UseMessageFormat = svcMapFile.ClientOptions.UseSerializerForFaults;
            importer.State.Add(typeof(System.ServiceModel.FaultImportOptions), faultOptions);

            // Read the WrappedOptions from Reference.svcmap, create a WrappedOptions using this information
            // and pass it to WSDL Importer.
            WrappedOptions wrappedOptions = new WrappedOptions();
            wrappedOptions.WrappedFlag = svcMapFile.ClientOptions.Wrapped;
            importer.State.Add(typeof(System.ServiceModel.Channels.WrappedOptions), wrappedOptions);

            return importer;
        }

        /// <summary>
        /// Look through all the import extensions to see if any of them want access to 
        ///   the service reference's extension files.  They tell us this by implementing
        ///   the interface IWcfReferenceReceiveContextInformation.
        /// </summary>
        /// <param name="svcMapFile"></param>
        /// <param name="serviceProviderForImportExtensions"></param>
        /// <param name="wsdlImportExtensions"></param>
        /// <param name="policyImportExtensions"></param>
        internal static void ProvideImportExtensionsWithContextInformation(SvcMapFile svcMapFile, IServiceProvider serviceProviderForImportExtensions, IEnumerable<IWsdlImportExtension> wsdlImportExtensions, IEnumerable<IPolicyImportExtension> policyImportExtensions)
        {
            // Only make this copy if we need to (not the mainline case)
            Dictionary<string, byte[]> extensionFileContents = null;

            foreach (IWsdlImportExtension wsdlImportExtension in wsdlImportExtensions)
            {
                System.Web.Compilation.IWcfReferenceReceiveContextInformation receiveContext =
                    wsdlImportExtension as System.Web.Compilation.IWcfReferenceReceiveContextInformation;
                if (receiveContext != null)
                {
                    if (extensionFileContents == null)
                    {
                        extensionFileContents = CreateDictionaryOfCopiedExtensionFiles(svcMapFile);
                    }
                    receiveContext.ReceiveImportContextInformation(
                        extensionFileContents,
                        serviceProviderForImportExtensions);
                }
            }
            foreach (IPolicyImportExtension policyImportExtension in policyImportExtensions)
            {
                System.Web.Compilation.IWcfReferenceReceiveContextInformation receiveContext =
                    policyImportExtension as System.Web.Compilation.IWcfReferenceReceiveContextInformation;
                if (receiveContext != null)
                {
                    if (extensionFileContents == null)
                    {
                        extensionFileContents = CreateDictionaryOfCopiedExtensionFiles(svcMapFile);
                    }
                    receiveContext.ReceiveImportContextInformation(
                        extensionFileContents,
                        serviceProviderForImportExtensions);
                }
            }
        }

        /// <summary>
        /// Remove specific wsdl importer extension
        /// </summary>
        /// <param name="extensionType">
        /// The extension to remove
        /// </param>
        /// <param name="wsdlImportExtensions">
        /// The collection to remove the extension from
        /// </param>
        /// <return></return>
        /// <remarks></remarks>
        private static void RemoveExtension(Type extensionType, Collection<IWsdlImportExtension> wsdlImportExtensions)
        {
            Debug.Assert(wsdlImportExtensions != null);

            for (int i = 0; i < wsdlImportExtensions.Count; i++)
            {
                if (wsdlImportExtensions[i].GetType() == extensionType)
                    wsdlImportExtensions.RemoveAt(i);
            }
        }

        /// <summary>
        /// Creates a dictionary containing a copy of the contents of all of the extension files
        /// </summary>
        /// <returns></returns>
        private static Dictionary<string, byte[]> CreateDictionaryOfCopiedExtensionFiles(SvcMapFile svcMapFile)
        {
            Dictionary<string, byte[]> extensionFileContents = new Dictionary<string, byte[]>();
            foreach (ExtensionFile extensionFile in svcMapFile.Extensions)
            {
                // If the extension file was not successfully loaded, do not include it in the
                //   collection.  Users are more likely to expect that the extension file won't
                //   be in the collection on an error than they are to assume they have to check
                //   if the byte array we return is null or not.
                if (extensionFile.ContentBuffer != null && extensionFile.IsBufferValid)
                {
                    extensionFileContents.Add(extensionFile.Name, (byte[])extensionFile.ContentBuffer.Clone());
                }
            }

            return extensionFileContents;
        }


        /// <summary>
        /// Merge metadata files to prepare code generation
        /// </summary>
        /// <returns>metadata collection</returns>
        /// <remarks></remarks>
        protected static List<MetadataSection> CollectMetadataDocuments(IEnumerable<MetadataFile> metadataList, IList<ProxyGenerationError> importErrors)
        {
            List<MetadataSection> metadataCollection = new List<MetadataSection>();

            foreach (MetadataFile metadataItem in metadataList)
            {
                if (!metadataItem.Ignore)
                {
                    try
                    {
                        MetadataSection metadataSection = metadataItem.CreateMetadataSection();
                        if (metadataSection != null)
                        {
                            metadataCollection.Add(metadataSection);
                        }
                    }
                    catch (Exception ex)
                    {
                        importErrors.Add(ConvertMetadataErrorToProxyGenerationError(metadataItem, ex));
                    }
                }
            }

            RemoveDuplicatedSchemaItems(metadataCollection, importErrors);
            CheckDuplicatedWsdlItems(metadataCollection, importErrors);

            return metadataCollection;
        }

        /// <summary>
        /// Convert metadata loading errors into proxy generation error messages
        /// </summary>
        /// <return></return>
        /// <remarks></remarks>
        internal static ProxyGenerationError ConvertMetadataErrorToProxyGenerationError(MetadataFile metadataItem, Exception ex)
        {
            ProxyGenerationError generationError = null;
            if (ex is XmlSchemaException)
            {
                generationError = new ProxyGenerationError(ProxyGenerationError.GeneratorState.LoadMetadata, metadataItem.FileName, (XmlSchemaException)ex);
            }
            else if (ex is XmlException)
            {
                generationError = new ProxyGenerationError(ProxyGenerationError.GeneratorState.LoadMetadata, metadataItem.FileName, (XmlException)ex);
            }
            else if (ex is InvalidOperationException)
            {
                System.Xml.Schema.XmlSchemaException schemaException = ex.InnerException as System.Xml.Schema.XmlSchemaException;
                if (schemaException != null)
                {
                    generationError = new ProxyGenerationError(ProxyGenerationError.GeneratorState.LoadMetadata, metadataItem.FileName, schemaException);
                }
                else
                {
                    System.Xml.XmlException xmlException = ex.InnerException as System.Xml.XmlException;
                    if (xmlException != null)
                    {
                        generationError = new ProxyGenerationError(ProxyGenerationError.GeneratorState.LoadMetadata, metadataItem.FileName, xmlException);
                    }
                    else
                    {
                        generationError = new ProxyGenerationError(ProxyGenerationError.GeneratorState.LoadMetadata, metadataItem.FileName, (InvalidOperationException)ex);
                    }
                }
            }
            else
            {
                generationError = new ProxyGenerationError(ProxyGenerationError.GeneratorState.LoadMetadata, metadataItem.FileName, ex);
            }
            return generationError;
        }

        /// <summary>
        /// Remove duplicated schema items from the metadata collection
        /// </summary>
        /// <remarks></remarks>
        private static void RemoveDuplicatedSchemaItems(List<MetadataSection> metadataCollection, IList<ProxyGenerationError> importErrors)
        {
            Dictionary<XmlSchema, MetadataSection> schemaList = new Dictionary<XmlSchema, MetadataSection>();

            // add independent schema files...
            foreach (MetadataSection metadataSection in metadataCollection)
            {
                if (metadataSection.Dialect == MetadataSection.XmlSchemaDialect)
                {
                    XmlSchema schema = (XmlSchema)metadataSection.Metadata;
                    schemaList.Add(schema, metadataSection);
                }
            }

            // add embedded files...
            foreach (MetadataSection metadataSection in metadataCollection)
            {
                if (metadataSection.Dialect == MetadataSection.ServiceDescriptionDialect)
                {
                    System.Web.Services.Description.ServiceDescription wsdl = (System.Web.Services.Description.ServiceDescription)metadataSection.Metadata;
                    foreach (XmlSchema schema in wsdl.Types.Schemas)
                    {
                        schema.SourceUri = wsdl.RetrievalUrl;
                        schemaList.Add(schema, metadataSection);
                    }
                }
            }

            IEnumerable<XmlSchema> duplicatedSchemas;
            SchemaMerger.MergeSchemas(schemaList.Keys, importErrors, out duplicatedSchemas);

            if (duplicatedSchemas != null)
            {
                foreach (XmlSchema schema in duplicatedSchemas)
                {
                    Debug.Assert(schemaList.ContainsKey(schema), "The schema list should not contain any of the schemas returned in the duplicateSchemas...");

                    MetadataSection metadataSection = schemaList[schema];
                    if (metadataSection.Dialect == MetadataSection.XmlSchemaDialect)
                    {
                        metadataCollection.Remove(metadataSection);
                    }
                    else if (metadataSection.Dialect == MetadataSection.ServiceDescriptionDialect)
                    {
                        System.Web.Services.Description.ServiceDescription wsdl = (System.Web.Services.Description.ServiceDescription)metadataSection.Metadata;
                        wsdl.Types.Schemas.Remove(schema);
                    }
                }
            }
        }

        /// <summary>
        /// check all wsdl files, and generate error messages if one contract have multiple different specifications
        /// </summary>
        /// <remarks></remarks>
        private static void CheckDuplicatedWsdlItems(IList<MetadataSection> metadataCollection, IList<ProxyGenerationError> importErrors)
        {
            List<System.Web.Services.Description.ServiceDescription> wsdlFiles = new List<System.Web.Services.Description.ServiceDescription>();
            foreach (MetadataSection metadataSection in metadataCollection)
            {
                if (metadataSection.Dialect == MetadataSection.ServiceDescriptionDialect)
                {
                    System.Web.Services.Description.ServiceDescription wsdl = (System.Web.Services.Description.ServiceDescription)metadataSection.Metadata;
                    wsdlFiles.Add(wsdl);
                }
            }

            WsdlInspector.CheckDuplicatedWsdlItems(wsdlFiles, importErrors);
        }

        /// <summary>
        /// Given a WSDL importer, a set of metadata files and a compile unit, import the metadata
        /// files.
        /// </summary>
        /// <param name="importer"></param>
        /// <param name="compileUnit"></param>
        /// <param name="generationErrors">
        /// Errors encountered whie importing the model. Any new errors will be appended to this list.
        /// </param>
        /// <param name="serviceEndpointList">List of endpoints imported</param>
        /// <param name="bindingCollection">The collection of bindings imported</param>
        /// <param name="contractCollection">The collection of contracts imported</param>
        protected static void ImportWCFModel(WsdlImporter importer,
                                          System.CodeDom.CodeCompileUnit compileUnit,
                                          IList<ProxyGenerationError> generationErrors,
                                          out List<ServiceEndpoint> serviceEndpointList,
                                          out IEnumerable<System.ServiceModel.Channels.Binding> bindingCollection,
                                          out IEnumerable<ContractDescription> contractCollection)
        {
            // We want to remove soap1.2 endpoints for ASMX references, but we can't use the "normal" way 
            // of using a IWsdlImportExtension to do so since BeforeImport is called too late (DevDiv 7857)
            // If DevDiv 7857 is fixed, we can remove the following two lines and instead add the 
            // AsmxEndpointPickerExtension to the importer's wsdl import extensions...
            IWsdlImportExtension asmxFixerUpper = new AsmxEndpointPickerExtension();
            asmxFixerUpper.BeforeImport(importer.WsdlDocuments, null, null);

            // NOTE: we should import Endpoint before Contracts, otherwise some information (related to binding) will be lost in the model (devdiv: 22396)
            serviceEndpointList = new List<ServiceEndpoint>();

            //
            // First we import all the endpoints (ports). This is required so that any WsdlImportExtension's BeforeImport
            // gets called before we actually try to import anything from the WSDL object model. 
            // If we don't do this, we run into problems if any wsdl import extensions want to delete a specific port
            // and this port happens to be the first port we try to import (you can't interrupt the import)
            importer.ImportAllEndpoints();

            //
            // We need to go through each endpoint element and "re-import" it in order to get the mapping
            // between the wsdlPort and the ServiceEndpoint... Importing the same endpoint twice is a no-op
            // as far as the endpoint collection is concerned - it is simply a hashtable lookup to retreive 
            // the already generated information...
            //
            foreach (System.Web.Services.Description.ServiceDescription wsdlServiceDescription in importer.WsdlDocuments)
            {
                foreach (System.Web.Services.Description.Service wsdlService in wsdlServiceDescription.Services)
                {
                    foreach (System.Web.Services.Description.Port servicePort in wsdlService.Ports)
                    {
                        try
                        {
                            ServiceEndpoint newEndpoint = importer.ImportEndpoint(servicePort);
                            serviceEndpointList.Add(newEndpoint);
                        }
                        catch (InvalidOperationException)
                        {
                            // Invalid operation exceptions should already be in the errors collection for the importer, so we don't
                            // need to add another generationError. The most probable cause for this is that the we failed to import 
                            // the endpoint...
                        }
                        catch (Exception ex)
                        { // It is bad, because WsdlImporter.WsdlImportException is a private class
                            generationErrors.Add(new ProxyGenerationError(ProxyGenerationError.GeneratorState.GenerateCode, wsdlServiceDescription.RetrievalUrl, ex));
                        }
                    }
                }
            }


            bindingCollection = importer.ImportAllBindings();
            System.Diagnostics.Debug.Assert(bindingCollection != null, "The importer should never return a NULL binding collection!");

            contractCollection = importer.ImportAllContracts();
            System.Diagnostics.Debug.Assert(contractCollection != null, "The importer should never return a NULL contract collection!");

            foreach (MetadataConversionError error in importer.Errors)
            {
                generationErrors.Add(new ProxyGenerationError(error));
            }
        }



        /// <summary>
        /// This function patches ServiceContractAttribute in the generated proxy code. It replaces all proxyNamespace in the attribute
        ///   to configNamespace.  The configNamespace does not depend on defaultNamespace of the project.
        /// </summary>
        /// <param name="proxyNamespace"></param>
        /// <param name="configNamespace"></param>
        /// <return></return>
        /// <remarks></remarks>
        private static void PatchConfigurationNameInServiceContractAttribute(CodeCompileUnit proxyCodeUnit, string proxyNamespace, string configNamespace)
        {
            if (proxyNamespace == null)
            {
                proxyNamespace = String.Empty;
            }

            string proxyNamespaceHead = MakePeriodTerminatedNamespacePrefix(proxyNamespace);
            string configNamespaceHead = MakePeriodTerminatedNamespacePrefix(configNamespace);

            if (proxyCodeUnit != null)
            {
                foreach (CodeNamespace proxyCodeNamespace in proxyCodeUnit.Namespaces)
                {
                    // Find the namespace we are patching...
                    if (String.Equals(proxyNamespace, proxyCodeNamespace.Name, StringComparison.Ordinal))
                    {
                        // ...and all types in each namespace...
                        foreach (CodeTypeDeclaration typeDeclaration in proxyCodeNamespace.Types)
                        {
                            if (typeDeclaration.IsInterface)
                            {
                                // ...and each attribute on each interface...
                                foreach (CodeAttributeDeclaration codeAttribute in typeDeclaration.CustomAttributes)
                                {
                                    // find System.ServiceModel.ServiceContractAttribute attribute.
                                    if (String.Equals(codeAttribute.AttributeType.BaseType, typeof(System.ServiceModel.ServiceContractAttribute).FullName, StringComparison.Ordinal))
                                    {
                                        foreach (CodeAttributeArgument argument in codeAttribute.Arguments)
                                        {
                                            if (String.Equals(argument.Name, "ConfigurationName", StringComparison.Ordinal))
                                            {
                                                // we only fix the string here
                                                CodePrimitiveExpression valueExpression = argument.Value as CodePrimitiveExpression;
                                                if (valueExpression != null && valueExpression.Value is string)
                                                {
                                                    valueExpression.Value = ReplaceNamespace(proxyNamespaceHead, configNamespaceHead, (string)valueExpression.Value);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }


        /// <summary>
        /// Patch VB code for output parameters.
        /// 
        /// Visual Basic doesn't support Out parameters - they are all generated as ByRef.
        /// Unfortunately, the CodeDom provider doesn't add an Out attribute to the ByRef
        /// parameters, so we have to do that ourselves...
        /// </summary>
        /// <param name="codeCompileUnit"></param>
        /// <remarks></remarks>
        private static void PatchOutParametersInVB(CodeCompileUnit codeCompileUnit)
        {
            foreach (CodeNamespace codeNamespace in codeCompileUnit.Namespaces)
            {
                foreach (CodeTypeDeclaration codeClass in codeNamespace.Types)
                {
                    PatchTypeDeclaration(codeClass);
                }
            }
        }

        /// <summary>
        /// Patch TypeDeclaration in VB code for output parameters
        /// </summary>
        /// <param name="codeClass"></param>
        /// <remarks></remarks>
        private static void PatchTypeDeclaration(CodeTypeDeclaration codeClass)
        {
            foreach (CodeTypeMember member in codeClass.Members)
            {
                if (member is CodeTypeDeclaration)
                {
                    // Recurse down in nested types...
                    PatchTypeDeclaration((CodeTypeDeclaration)member);
                }
                else if (member is CodeMemberMethod)
                {
                    CodeMemberMethod method = member as CodeMemberMethod;
                    foreach (CodeParameterDeclarationExpression parameter in method.Parameters)
                    {
                        if (parameter.Direction == FieldDirection.Out)
                        {
                            // Make sure that all Out parameters have an <Out> attribute
                            //
                            // First check for explicit <OutAttribute> declaration to avoid adding duplicate attributes.
                            if (!IsDefinedInCodeAttributeCollection(typeof(System.Runtime.InteropServices.OutAttribute), parameter.CustomAttributes))
                            {
                                parameter.CustomAttributes.Add(OutAttribute);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// check whether code attribuate has already been declared.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="metadata"></param>
        /// <return></return>
        /// <remarks></remarks>
        private static bool IsDefinedInCodeAttributeCollection(Type type, CodeAttributeDeclarationCollection metadata)
        {
            foreach (CodeAttributeDeclaration attribute in metadata)
            {
                if (String.Equals(attribute.Name, type.FullName, StringComparison.Ordinal) || String.Equals(attribute.Name, type.Name, StringComparison.Ordinal))
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Check whether it is VB language
        /// </summary>
        /// <param name="codeDomProvider"></param>
        /// <return></return>
        /// <remarks></remarks>
        private static bool IsVBCodeDomProvider(System.CodeDom.Compiler.CodeDomProvider codeDomProvider)
        {
            string fileExtension = codeDomProvider.FileExtension;
            try
            {
                string language = System.CodeDom.Compiler.CodeDomProvider.GetLanguageFromExtension(fileExtension);
                return String.Equals(language, VB_LANGUAGE_NAME, StringComparison.OrdinalIgnoreCase);
            }
            catch (System.Configuration.ConfigurationException)
            {
                // not defined extension
                return false;
            }
        }

        /// <summary>
        /// check whether HTTP Binding is used in those metadata files
        /// </summary>
        /// <param name="metadataCollection">metadata files</param>
        /// <return></return>
        /// <remarks></remarks>
        private static bool ContainsHttpBindings(IEnumerable<MetadataSection> metadataCollection)
        {
            foreach (MetadataSection metadataSection in metadataCollection)
            {
                if (metadataSection.Dialect == MetadataSection.ServiceDescriptionDialect)
                {
                    System.Web.Services.Description.ServiceDescription wsdlFile = (System.Web.Services.Description.ServiceDescription)metadataSection.Metadata;
                    if (ContainsHttpBindings(wsdlFile))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// check whether HTTP Binding is used in one wsdl file
        /// </summary>
        /// <param name="wsdlFile">one wsdl</param>
        /// <return></return>
        /// <remarks></remarks>
        internal static bool ContainsHttpBindings(System.Web.Services.Description.ServiceDescription wsdlFile)
        {
            foreach (System.Web.Services.Description.Binding binding in wsdlFile.Bindings)
            {
                foreach (object extension in binding.Extensions)
                {
                    System.Web.Services.Description.HttpBinding httpBinding = extension as System.Web.Services.Description.HttpBinding;
                    if (httpBinding != null)
                    {
                        return true;
                    }
                }
            }
            return false;
        }
    }
}
