File: Microsoft\Tools\ServiceModel\SvcUtil\XmlSerializerGenerator.cs
Web Access
Project: src\src\svcutilcore\src\dotnet-svcutil.xmlserializer.csproj (dotnet-svcutil.xmlserializer)
// 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 Microsoft.Tools.ServiceModel.SvcUtil.XmlSerializer
{
    using System;
    using System.Reflection;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.IO;
    using System.Globalization;
    using System.Text;
    using System.Xml.Serialization;
    using System.CodeDom.Compiler;
 
    internal class XmlSerializerGenerator : OutputModule
    {
        private const string sourceExtension = ".cs";
        private readonly ExportModule.IsTypeExcludedDelegate _isTypeExcluded;
 
        private string _outFile;
 
        internal XmlSerializerGenerator(Options options)
            : base(options)
        {
            _isTypeExcluded = options.IsTypeExcluded;
            _outFile = options.OutputFileArg;
        }
 
        internal void GenerateCode(List<Assembly> assemblies)
        {
            if (!string.IsNullOrEmpty(_outFile) && assemblies.Count > 1)
            {
                ToolConsole.WriteWarning(SR.Format(SR.WrnOptionConflictsWithInput, Options.Cmd.Out));
                _outFile = null;
            }
 
            foreach (Assembly assembly in assemblies)
            {
                GenerateCode(assembly);
            }
        }
 
        private void GenerateCode(Assembly assembly)
        {
            List<XmlMapping> mappings = new List<XmlMapping>();
            List<Type> types = CollectXmlSerializerTypes(assembly, mappings);
 
#pragma warning disable SYSLIB0044
            if (types.Count == 0)
            {
                ToolConsole.WriteWarning(SR.Format(SR.WrnNoServiceContractTypes, assembly.GetName().CodeBase));
                return;
            }
            if (mappings.Count == 0)
            {
                ToolConsole.WriteWarning(SR.Format(SR.WrnNoXmlSerializerOperationBehavior, assembly.GetName().CodeBase));
                return;
            }
#pragma warning restore SYSLIB0044
 
            bool success = false;
            bool toDeleteFile = true;
 
            string codePath = Path.GetTempFileName();
 
            try
            {
                if (File.Exists(codePath))
                {
                    File.Delete(codePath);
                }
 
                using (FileStream fs = File.Create(codePath))
                {
                    MethodInfo method = typeof(System.Xml.Serialization.XmlSerializer).GetMethod("GenerateSerializer", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
                    if (method == null)
                    {
                        throw new ToolRuntimeException(SR.GenerateSerializerNotFound);
                    }
                    else
                    {
                        success = (bool)method.Invoke(null, new object[] { types.ToArray(), mappings.ToArray(), fs });
                    }
                }
            }
            finally
            {
                if (!success && toDeleteFile && File.Exists(codePath))
                {
                    File.Delete(codePath);
                }
            }
 
            string sgenSource = XmlSerializer.GetXmlSerializerAssemblyName(types[0]);
 
            // delete all temp files generated by CodeDom except source file 
            sgenSource = BuildFilePath(sgenSource, sourceExtension, null);
            if (File.Exists(sgenSource))
                File.Delete(sgenSource);
 
            string sourceName;
            if (_outFile != null)
                sourceName = FilenameHelper.UniquifyFileName(_outFile, sourceExtension);
            else
                sourceName = FilenameHelper.UniquifyFileName(sgenSource, sourceExtension);
 
            string sourceFilePath = BuildFilePath(sourceName, sourceExtension, null);
            CreateDirectoryIfNeeded(sourceFilePath);
            File.Copy(codePath, sourceFilePath, true);
            ToolConsole.WriteLine(sourceFilePath);
 
            return;
        }
 
        private List<Type> CollectXmlSerializerTypes(Assembly assembly, List<XmlMapping> mappings)
        {
            List<Type> types = new List<Type>();
 
            ExportModule.ContractLoader contractLoader = new ExportModule.ContractLoader(new Assembly[] { assembly }, _isTypeExcluded);
            contractLoader.ContractLoadErrorCallback = delegate (Type contractType, string errorMessage)
                    {
                        ToolConsole.WriteWarning(SR.Format(SR.WrnUnableToLoadContractForSGen, contractType, errorMessage));
                    };
 
            Type contractDescriptionType = Tool.SMAssembly.GetType("System.ServiceModel.Description.ContractDescription");
            if (contractDescriptionType == null)
            {
                ToolConsole.WriteError($"Not found type System.ServiceModel.Description.ContractDescription in {Tool.SMAssembly.FullName}");
                throw new ToolRuntimeException();
            }
 
            PropertyInfo contractTypeProperty = contractDescriptionType.GetProperty("ContractType");
            if (contractTypeProperty == null)
            {
                ToolConsole.WriteError($"Not found property ContractType in type {contractDescriptionType}");
                throw new ToolRuntimeException();
            }
 
            Type xmlSerializerOperationBehaviorType = Tool.SMAssembly.GetType("System.ServiceModel.Description.XmlSerializerOperationBehavior");
            if (xmlSerializerOperationBehaviorType == null)
            {
                ToolConsole.WriteError($"Not found type System.ServiceModel.Description.XmlSerializerOperationBehaviorType in {Tool.SMAssembly.FullName}");
                throw new ToolRuntimeException();
            }
 
            Type operationType = Tool.SMAssembly.GetType("System.ServiceModel.Description.OperationDescription");
            if (operationType == null)
            {
                ToolConsole.WriteError($"Not found type System.ServiceModel.Description.OperationDescription in {Tool.SMAssembly.FullName}");
                throw new ToolRuntimeException();
            }
 
            PropertyInfo getBehaviorsProperty = operationType.GetProperty("Behaviors");
            if (getBehaviorsProperty == null)
            {
                ToolConsole.WriteError($"Not found method get_Behaviors in type {operationType}");
                throw new ToolRuntimeException();
            }
 
            Type keyedByTypeCollectionType = Tool.SMAssembly.GetType("System.Collections.Generic.KeyedByTypeCollection`1");
            if (keyedByTypeCollectionType == null)
            {
                ToolConsole.WriteError($"Not found type System.Collections.Generic.KeyedByTypeCollection`1 in {Tool.SMAssembly.FullName}");
                throw new ToolRuntimeException();
            }
 
            Type iOperationBehaviorType = Tool.SMAssembly.GetType("System.ServiceModel.Description.IOperationBehavior");
            if (iOperationBehaviorType == null)
            {
                ToolConsole.WriteError($"Not found type System.ServiceModel.Description.IOperationBehavior in {Tool.SMAssembly.FullName}");
                throw new ToolRuntimeException();
            }
 
            keyedByTypeCollectionType = keyedByTypeCollectionType.MakeGenericType(new Type[] { iOperationBehaviorType });
            if (keyedByTypeCollectionType == null)
            {
                ToolConsole.WriteError($"Cannot make Generic Type System.Collections.Generic.KeyedByTypeCollection<IOperationBehavior> in {Tool.SMAssembly.FullName}");
                throw new ToolRuntimeException();
            }
 
            MethodInfo findMethod = keyedByTypeCollectionType.GetMethod("Find");
            if(findMethod == null)
            {
                ToolConsole.WriteError($"Not found method find in type {keyedByTypeCollectionType}");
                throw new ToolRuntimeException();
            }
 
            findMethod = findMethod.MakeGenericMethod(new Type[] { xmlSerializerOperationBehaviorType });
            if (findMethod == null)
            {
                ToolConsole.WriteError($"Not found method Find<XmlSerializerOperationBehavior> in operation.Behaviors");
                throw new ToolRuntimeException();
            }
 
            MethodInfo getXmlMappingsMethod = xmlSerializerOperationBehaviorType.GetMethod("GetXmlMappings");
            if (getXmlMappingsMethod == null)
            {
                ToolConsole.WriteError($"Not found method getXmlMappings in XmlSerializerOperationBehavior");
                throw new ToolRuntimeException();
            }
 
            PropertyInfo operationsProperty = contractDescriptionType.GetProperty("Operations");
            if (operationsProperty == null)
            {
                ToolConsole.WriteError($"Not found property Operations in type {contractDescriptionType}");
                throw new ToolRuntimeException();
            }
 
            //Use reflection to replace the following code
            //foreach (ContractDescription contract in contractLoader.GetContracts())
            //{
            //    types.Add(contract.ContractType);
            //    foreach (OperationDescription operation in contract.Operations)
            //    {
            //        XmlSerializerOperationBehavior behavior = operation.Behaviors.Find<XmlSerializerOperationBehavior>();
            //        if (behavior != null)
            //        {
            //            foreach (XmlMapping map in behavior.GetXmlMappings())
            //            {
            //                mappings.Add(map);
            //            }
            //        }
            //    }
            //}
 
            foreach (object contract in contractLoader.GetContracts())
            {
                types.Add((Type)contractTypeProperty.GetValue(contract));
 
                System.Collections.IEnumerable operations = (System.Collections.IEnumerable)operationsProperty.GetValue(contract);
                foreach(var operation in operations)
                {
                    if(operation == null)
                    {
                        throw new ToolRuntimeException("operation is null");
                    }
 
                    var behaviors = getBehaviorsProperty.GetValue(operation);
                    var behavior = findMethod.Invoke(behaviors, new object[] { });
                    if (behavior != null)
                    {
                        var xmlMappings = (Collection<XmlMapping>)getXmlMappingsMethod.Invoke(behavior, new object[] { });
                        if (xmlMappings != null)
                        {
                            foreach (XmlMapping map in xmlMappings)
                            {
                                mappings.Add(map);
                            }
                        }
                    }
                }
            }
 
            return types;
        }
    }
}