1 // -----------------------------------------------------------------------
\r
2 // Copyright (c) Microsoft Corporation. All rights reserved.
\r
3 // -----------------------------------------------------------------------
\r
5 using System.Collections.Generic;
\r
6 using System.Globalization;
\r
7 using System.Reflection;
\r
9 using Microsoft.Internal;
\r
11 namespace System.ComponentModel.Composition
\r
13 internal static class ContractNameServices
\r
15 const char NamespaceSeparator = '.';
\r
16 const char ArrayOpeningBracket = '[';
\r
17 const char ArrayClosingBracket = ']';
\r
18 const char ArraySeparator = ',';
\r
19 const char PointerSymbol = '*';
\r
20 const char ReferenceSymbol = '&';
\r
21 const char GenericArityBackQuote = '`';
\r
22 const char NestedClassSeparator = '+';
\r
23 const char ContractNameGenericOpeningBracket = '(';
\r
24 const char ContractNameGenericClosingBracket = ')';
\r
25 const char ContractNameGenericArgumentSeparator = ',';
\r
26 const char CustomModifiersSeparator = ' ';
\r
29 private static Dictionary<Type, string> typeIdentityCache;
\r
31 private static Dictionary<Type, string> TypeIdentityCache
\r
35 return typeIdentityCache = typeIdentityCache ?? new Dictionary<Type, string>();
\r
39 internal static string GetTypeIdentity(Type type)
\r
41 Assumes.NotNull(type);
\r
42 string typeIdentity = null;
\r
44 if (!TypeIdentityCache.TryGetValue(type, out typeIdentity))
\r
46 if (!type.IsAbstract && type.IsSubclassOf(typeof(Delegate)))
\r
48 MethodInfo method = type.GetMethod("Invoke");
\r
49 typeIdentity = ContractNameServices.GetTypeIdentityFromMethod(method);
\r
53 StringBuilder typeIdentityStringBuilder = new StringBuilder();
\r
54 WriteTypeWithNamespace(typeIdentityStringBuilder, type);
\r
55 typeIdentity = typeIdentityStringBuilder.ToString();
\r
58 TypeIdentityCache.Add(type, typeIdentity);
\r
61 return typeIdentity;
\r
64 internal static string GetTypeIdentityFromMethod(MethodInfo method)
\r
66 StringBuilder methodNameStringBuilder = new StringBuilder();
\r
68 WriteTypeWithNamespace(methodNameStringBuilder, method.ReturnType);
\r
70 methodNameStringBuilder.Append("(");
\r
72 ParameterInfo[] parameters = method.GetParameters();
\r
74 for (int i = 0; i < parameters.Length; i++)
\r
78 methodNameStringBuilder.Append(",");
\r
81 WriteTypeWithNamespace(methodNameStringBuilder, parameters[i].ParameterType);
\r
83 methodNameStringBuilder.Append(")");
\r
85 return methodNameStringBuilder.ToString();
\r
88 private static void WriteTypeWithNamespace(StringBuilder typeName, Type type)
\r
90 // Writes type with namesapce
\r
91 if (!string.IsNullOrEmpty(type.Namespace))
\r
93 typeName.Append(type.Namespace);
\r
94 typeName.Append(NamespaceSeparator);
\r
96 WriteType(typeName, type);
\r
99 private static void WriteType(StringBuilder typeName, Type type)
\r
101 // Writes type name
\r
102 if (type.IsGenericType)
\r
105 // Reflection format stores all the generic arguments (including the ones for parent types) on the leaf type.
\r
106 // These arguments are placed in a queue and are written out based on generic arity (`X) of each type
\r
108 Queue<Type> genericTypeArguments = new Queue<Type>(type.GetGenericArguments());
\r
109 WriteGenericType(typeName, type, type.IsGenericTypeDefinition, genericTypeArguments);
\r
110 Assumes.IsTrue(genericTypeArguments.Count == 0, "Expecting genericTypeArguments queue to be empty.");
\r
114 WriteNonGenericType(typeName, type);
\r
118 private static void WriteNonGenericType(StringBuilder typeName, Type type)
\r
121 // Writes non-generic type
\r
123 if (type.DeclaringType != null)
\r
125 WriteType(typeName, type.DeclaringType);
\r
126 typeName.Append(NestedClassSeparator);
\r
130 WriteArrayType(typeName, type);
\r
132 else if (type.IsPointer)
\r
134 WritePointerType(typeName, type);
\r
136 else if (type.IsByRef)
\r
138 WriteByRefType(typeName, type);
\r
142 typeName.Append(type.Name);
\r
146 private static void WriteArrayType(StringBuilder typeName, Type type)
\r
149 // Writes array type e.g <TypeName>[]
\r
150 // Note that jagged arrays are stored in reverse order
\r
151 // e.g. C#: Int32[][,] Reflection: Int32[,][]
\r
152 // we are following C# order for arrays
\r
154 Type rootElementType = FindArrayElementType(type);
\r
155 WriteType(typeName, rootElementType);
\r
156 Type elementType = type;
\r
159 WriteArrayTypeDimensions(typeName, elementType);
\r
161 while ((elementType = elementType.GetElementType()) != null && elementType.IsArray);
\r
164 private static void WritePointerType(StringBuilder typeName, Type type)
\r
167 // Writes pointer type e.g <TypeName>*
\r
169 WriteType(typeName, type.GetElementType());
\r
170 typeName.Append(PointerSymbol);
\r
173 private static void WriteByRefType(StringBuilder typeName, Type type)
\r
176 // Writes by ref type e.g <TypeName>&
\r
178 WriteType(typeName, type.GetElementType());
\r
179 typeName.Append(ReferenceSymbol);
\r
182 private static void WriteArrayTypeDimensions(StringBuilder typeName, Type type)
\r
185 // Writes array type dimensions e.g. [,,]
\r
187 typeName.Append(ArrayOpeningBracket);
\r
188 int rank = type.GetArrayRank();
\r
189 for (int i = 1; i < rank; i++)
\r
191 typeName.Append(ArraySeparator);
\r
193 typeName.Append(ArrayClosingBracket);
\r
196 private static void WriteGenericType(StringBuilder typeName, Type type, bool isDefinition, Queue<Type> genericTypeArguments)
\r
199 // Writes generic type including parent generic types
\r
200 // genericTypeArguments contains type arguments obtained from the most nested type
\r
201 // isDefinition parameter indicates if we are dealing with generic type definition
\r
203 if (type.DeclaringType != null)
\r
205 if (type.DeclaringType.IsGenericType)
\r
207 WriteGenericType(typeName, type.DeclaringType, isDefinition, genericTypeArguments);
\r
211 WriteNonGenericType(typeName, type.DeclaringType);
\r
213 typeName.Append(NestedClassSeparator);
\r
215 WriteGenericTypeName(typeName, type, isDefinition, genericTypeArguments);
\r
218 private static void WriteGenericTypeName(StringBuilder typeName, Type type, bool isDefinition, Queue<Type> genericTypeArguments)
\r
221 // Writes generic type name, e.g. generic name and generic arguments
\r
223 Assumes.IsTrue(type.IsGenericType, "Expecting type to be a generic type");
\r
224 int genericArity = GetGenericArity(type);
\r
225 string genericTypeName = FindGenericTypeName(type.GetGenericTypeDefinition().Name);
\r
226 typeName.Append(genericTypeName);
\r
227 WriteTypeArgumentsString(typeName, genericArity, isDefinition, genericTypeArguments);
\r
230 private static void WriteTypeArgumentsString(StringBuilder typeName, int argumentsCount, bool isDefinition, Queue<Type> genericTypeArguments)
\r
233 // Writes type arguments in brackets, e.g. (<contract_name1>, <contract_name2>, ...)
\r
235 if (argumentsCount == 0)
\r
239 typeName.Append(ContractNameGenericOpeningBracket);
\r
240 for (int i = 0; i < argumentsCount; i++)
\r
242 Assumes.IsTrue(genericTypeArguments.Count > 0, "Expecting genericTypeArguments to contain at least one Type");
\r
243 Type genericTypeArgument = genericTypeArguments.Dequeue();
\r
246 WriteTypeWithNamespace(typeName, genericTypeArgument);
\r
248 typeName.Append(ContractNameGenericArgumentSeparator);
\r
250 typeName.Remove(typeName.Length - 1, 1);
\r
251 typeName.Append(ContractNameGenericClosingBracket);
\r
254 //internal for testability
\r
255 internal static void WriteCustomModifiers(StringBuilder typeName, string customKeyword, Type[] types)
\r
258 // Writes custom modifiers in the format: customKeyword(<contract_name>,<contract_name>,...)
\r
260 typeName.Append(CustomModifiersSeparator);
\r
261 typeName.Append(customKeyword);
\r
262 Queue<Type> typeArguments = new Queue<Type>(types);
\r
263 WriteTypeArgumentsString(typeName, types.Length, false, typeArguments);
\r
264 Assumes.IsTrue(typeArguments.Count == 0, "Expecting genericTypeArguments queue to be empty.");
\r
267 private static Type FindArrayElementType(Type type)
\r
270 // Gets array element type by calling GetElementType() until the element is not an array
\r
272 Type elementType = type;
\r
273 while ((elementType = elementType.GetElementType()) != null && elementType.IsArray) { }
\r
274 return elementType;
\r
277 private static string FindGenericTypeName(string genericName)
\r
280 // Gets generic type name omitting the backquote and arity indicator
\r
282 // Arity indicator is returned as output parameter
\r
284 int indexOfBackQuote = genericName.IndexOf(GenericArityBackQuote);
\r
285 if (indexOfBackQuote > -1)
\r
287 genericName = genericName.Substring(0, indexOfBackQuote);
\r
289 return genericName;
\r
292 private static int GetGenericArity(Type type)
\r
294 if (type.DeclaringType == null)
\r
296 return type.GetGenericArguments().Length;
\r
299 // The generic arity is equal to the difference in the number of generic arguments
\r
300 // from the type and the declaring type.
\r
302 int delclaringTypeGenericArguments = type.DeclaringType.GetGenericArguments().Length;
\r
303 int typeGenericArguments = type.GetGenericArguments().Length;
\r
305 Assumes.IsTrue(typeGenericArguments >= delclaringTypeGenericArguments);
\r
307 return typeGenericArguments - delclaringTypeGenericArguments;
\r