|
// 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;
}
}
}
|