JD2022-TU1/main/tools/framework/JD.Reflection/MefImplementations/Model/Internal/ContractNameServices.cs

246 lines
9.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace JD.Reflection.MefImplementations.Model.Internal
{
internal static class ContractNameServices
{
#region Constants
private const char NamespaceSeparator = '.';
private const char ArrayOpeningBracket = '[';
private const char ArrayClosingBracket = ']';
private const char ArraySeparator = ',';
private const char PointerSymbol = '*';
private const char ReferenceSymbol = '&';
private const char GenericArityBackQuote = '`';
private const char NestedClassSeparator = '+';
private const char ContractNameGenericOpeningBracket = '<';
private const char ContractNameGenericClosingBracket = '>';
private const char ContractNameGenericArgumentSeparator = ',';
#endregion
#region Properties
[ThreadStatic]
private static Dictionary<Type, string> ms_typeIdentityCache;
private static Dictionary<Type, string> TypeIdentityCache
{
get
{
return ms_typeIdentityCache = ms_typeIdentityCache ?? new Dictionary<Type, string>();
}
}
#endregion
#region Methods
internal static string GetTypeIdentity(Type type)
{
string typeIdentity;
if (!TypeIdentityCache.TryGetValue(type, out typeIdentity))
{
StringBuilder typeIdentityStringBuilder = new StringBuilder();
WriteTypeWithNamespace(typeIdentityStringBuilder, type);
typeIdentity = typeIdentityStringBuilder.ToString();
TypeIdentityCache.Add(type, typeIdentity);
}
return typeIdentity;
}
private static void WriteTypeWithNamespace(StringBuilder typeName, Type type)
{
// Writes type with namesapce
if (!string.IsNullOrEmpty(type.Namespace))
{
typeName.Append(type.Namespace);
typeName.Append(NamespaceSeparator);
}
WriteType(typeName, type);
}
private static void WriteType(StringBuilder typeName, Type type)
{
if (type.IsGenericType)
{
// Reflection format stores all the generic arguments (including the ones for parent types) on the leaf type.
// These arguments are placed in a queue and are written out based on generic arity (`X) of each type
Queue<Type> genericTypeArguments = new Queue<Type>(type.GetGenericArguments());
WriteGenericType(typeName, type, type.IsGenericTypeDefinition, genericTypeArguments);
Debug.Assert(genericTypeArguments.Count == 0);
}
else
{
WriteNonGenericType(typeName, type);
}
}
private static void WriteNonGenericType(StringBuilder typeName, Type type)
{
if (type.DeclaringType != null)
{
WriteType(typeName, type.DeclaringType);
typeName.Append(NestedClassSeparator);
}
if (type.IsArray)
{
WriteArrayType(typeName, type);
}
else if (type.IsPointer)
{
WritePointerType(typeName, type);
}
else if (type.IsByRef)
{
WriteByRefType(typeName, type);
}
else
{
typeName.Append(type.Name);
}
}
private static void WriteArrayType(StringBuilder typeName, Type type)
{
// Writes array type e.g <TypeName>[]
// Note that jagged arrays are stored in reverse order
// e.g. C#: Int32[][,] Reflection: Int32[,][]
// we are following C# order for arrays
Type rootElementType = FindArrayElementType(type);
WriteType(typeName, rootElementType);
Type elementType = type;
do
{
WriteArrayTypeDimensions(typeName, elementType);
}
while ((elementType = elementType.GetElementType()) != null && elementType.IsArray);
}
private static void WritePointerType(StringBuilder typeName, Type type)
{
// Writes pointer type e.g <TypeName>*
WriteType(typeName, type.GetElementType());
typeName.Append(PointerSymbol);
}
private static void WriteByRefType(StringBuilder typeName, Type type)
{
// Writes by ref type e.g <TypeName>&
WriteType(typeName, type.GetElementType());
typeName.Append(ReferenceSymbol);
}
private static void WriteArrayTypeDimensions(StringBuilder typeName, Type type)
{
// Writes array type dimensions e.g. [,,]
typeName.Append(ArrayOpeningBracket);
int rank = type.GetArrayRank();
for (int i = 1; i < rank; i++)
{
typeName.Append(ArraySeparator);
}
typeName.Append(ArrayClosingBracket);
}
private static void WriteGenericType(StringBuilder typeName, Type type, bool isDefinition, Queue<Type> genericTypeArguments)
{
// Writes generic type including parent generic types
// genericTypeArguments contains type arguments obtained from the most nested type
// isDefinition parameter indicates if we are dealing with generic type definition
if (type.DeclaringType != null)
{
if (type.DeclaringType.IsGenericType)
{
WriteGenericType(typeName, type.DeclaringType, isDefinition, genericTypeArguments);
}
else
{
WriteNonGenericType(typeName, type.DeclaringType);
}
typeName.Append(NestedClassSeparator);
}
WriteGenericTypeName(typeName, type, isDefinition, genericTypeArguments);
}
private static void WriteGenericTypeName(StringBuilder typeName, Type type, bool isDefinition, Queue<Type> genericTypeArguments)
{
// Writes generic type name, e.g. generic name and generic arguments
Debug.Assert(type.IsGenericType, "Expecting type to be a generic type");
int genericArity = GetGenericArity(type);
string genericTypeName = FindGenericTypeName(type.GetGenericTypeDefinition().Name);
typeName.Append(genericTypeName);
WriteTypeArgumentsString(typeName, genericArity, isDefinition, genericTypeArguments);
}
private static void WriteTypeArgumentsString(StringBuilder typeName, int argumentsCount, bool isDefinition, Queue<Type> genericTypeArguments)
{
// Writes type arguments in brackets, e.g. (<contract_name1>, <contract_name2>, ...)
if (argumentsCount == 0)
{
return;
}
typeName.Append(ContractNameGenericOpeningBracket);
for (int i = 0; i < argumentsCount; i++)
{
Debug.Assert(genericTypeArguments.Count > 0, "Expecting genericTypeArguments to contain at least one Type");
Type genericTypeArgument = genericTypeArguments.Dequeue();
if (!isDefinition)
{
WriteTypeWithNamespace(typeName, genericTypeArgument);
}
typeName.Append(ContractNameGenericArgumentSeparator);
}
typeName.Remove(typeName.Length - 1, 1);
typeName.Append(ContractNameGenericClosingBracket);
}
private static Type FindArrayElementType(Type type)
{
// Gets array element type by calling GetElementType() until the element is not an array
Type elementType = type;
while ((elementType = elementType.GetElementType()) != null && elementType.IsArray) { }
return elementType;
}
private static string FindGenericTypeName(string genericName)
{
// Gets generic type name omitting the backquote and arity indicator
// List`1 -> List
// Arity indicator is returned as output parameter
int indexOfBackQuote = genericName.IndexOf(GenericArityBackQuote);
if (indexOfBackQuote > -1)
{
genericName = genericName.Substring(0, indexOfBackQuote);
}
return genericName;
}
private static int GetGenericArity(Type type)
{
if (type.DeclaringType == null)
{
return type.GetGenericArguments().Length;
}
// The generic arity is equal to the difference in the number of generic arguments
// from the type and the declaring type.
int delclaringTypeGenericArguments = type.DeclaringType.GetGenericArguments().Length;
int typeGenericArguments = type.GetGenericArguments().Length;
Debug.Assert(typeGenericArguments >= delclaringTypeGenericArguments);
return typeGenericArguments - delclaringTypeGenericArguments;
}
#endregion
}
}