1 // ***********************************************************************
2 // Copyright (c) 2008-2015 Charlie Poole
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // ***********************************************************************
25 #define NUNIT_FRAMEWORK
33 using System.Reflection;
35 using NUnit.Compatibility;
36 using NUnit.Framework.Interfaces;
38 namespace NUnit.Framework.Internal
41 /// TypeHelper provides static methods that operate on Types.
43 public class TypeHelper
45 private const int STRING_MAX = 40;
46 private const int STRING_LIMIT = STRING_MAX - 3;
47 private const string THREE_DOTS = "...";
49 internal sealed class NonmatchingTypeClass
54 /// A special value, which is used to indicate that BestCommonType() method
55 /// was unable to find a common type for the specified arguments.
57 public static readonly Type NonmatchingType = typeof( NonmatchingTypeClass );
60 /// Gets the display name for a Type as used by NUnit.
62 /// <param name="type">The Type for which a display name is needed.</param>
63 /// <returns>The display name for the Type</returns>
64 public static string GetDisplayName(Type type)
66 if (type.IsGenericParameter)
69 if (type.GetTypeInfo().IsGenericType)
71 string name = type.FullName;
72 int index = name.IndexOf('[');
73 if (index >= 0) name = name.Substring(0, index);
75 index = name.LastIndexOf('.');
76 if (index >= 0) name = name.Substring(index+1);
78 var genericArguments = type.GetGenericArguments();
79 var currentArgument = 0;
81 StringBuilder sb = new StringBuilder();
83 bool firstClassSeen = false;
84 foreach (string nestedClass in name.Split('+'))
89 firstClassSeen = true;
91 index = nestedClass.IndexOf('`');
94 var nestedClassName = nestedClass.Substring(0, index);
95 sb.Append(nestedClassName);
98 var argumentCount = Int32.Parse(nestedClass.Substring(index + 1));
99 for (int i = 0; i < argumentCount; i++)
104 sb.Append(GetDisplayName(genericArguments[currentArgument++]));
109 sb.Append(nestedClass);
112 return sb.ToString();
115 int lastdot = type.FullName.LastIndexOf('.');
117 ? type.FullName.Substring(lastdot+1)
122 /// Gets the display name for a Type as used by NUnit.
124 /// <param name="type">The Type for which a display name is needed.</param>
125 /// <param name="arglist">The arglist provided.</param>
126 /// <returns>The display name for the Type</returns>
127 public static string GetDisplayName(Type type, object[] arglist)
129 string baseName = GetDisplayName(type);
130 if (arglist == null || arglist.Length == 0)
133 StringBuilder sb = new StringBuilder( baseName );
136 for (int i = 0; i < arglist.Length; i++)
138 if (i > 0) sb.Append(",");
140 object arg = arglist[i];
141 string display = arg == null ? "null" : arg.ToString();
143 if (arg is double || arg is float)
145 if (display.IndexOf('.') == -1)
147 display += arg is double ? "d" : "f";
149 else if (arg is decimal) display += "m";
150 else if (arg is long) display += "L";
151 else if (arg is ulong) display += "UL";
152 else if (arg is string)
154 if (display.Length > STRING_MAX)
155 display = display.Substring(0, STRING_LIMIT) + THREE_DOTS;
156 display = "\"" + display + "\"";
163 return sb.ToString();
167 /// Returns the best fit for a common type to be used in
168 /// matching actual arguments to a methods Type parameters.
170 /// <param name="type1">The first type.</param>
171 /// <param name="type2">The second type.</param>
172 /// <returns>Either type1 or type2, depending on which is more general.</returns>
173 public static Type BestCommonType(Type type1, Type type2)
175 if ( type1 == TypeHelper.NonmatchingType ) return TypeHelper.NonmatchingType;
176 if ( type2 == TypeHelper.NonmatchingType ) return TypeHelper.NonmatchingType;
178 if (type1 == type2) return type1;
179 if (type1 == null) return type2;
180 if (type2 == null) return type1;
182 if (TypeHelper.IsNumeric(type1) && TypeHelper.IsNumeric(type2))
184 if (type1 == typeof(double)) return type1;
185 if (type2 == typeof(double)) return type2;
187 if (type1 == typeof(float)) return type1;
188 if (type2 == typeof(float)) return type2;
190 if (type1 == typeof(decimal)) return type1;
191 if (type2 == typeof(decimal)) return type2;
193 if (type1 == typeof(UInt64)) return type1;
194 if (type2 == typeof(UInt64)) return type2;
196 if (type1 == typeof(Int64)) return type1;
197 if (type2 == typeof(Int64)) return type2;
199 if (type1 == typeof(UInt32)) return type1;
200 if (type2 == typeof(UInt32)) return type2;
202 if (type1 == typeof(Int32)) return type1;
203 if (type2 == typeof(Int32)) return type2;
205 if (type1 == typeof(UInt16)) return type1;
206 if (type2 == typeof(UInt16)) return type2;
208 if (type1 == typeof(Int16)) return type1;
209 if (type2 == typeof(Int16)) return type2;
211 if (type1 == typeof(byte)) return type1;
212 if (type2 == typeof(byte)) return type2;
214 if (type1 == typeof(sbyte)) return type1;
215 if (type2 == typeof(sbyte)) return type2;
218 if ( type1.IsAssignableFrom( type2 ) ) return type1;
219 if ( type2.IsAssignableFrom( type1 ) ) return type2;
221 return TypeHelper.NonmatchingType;
225 /// Determines whether the specified type is numeric.
227 /// <param name="type">The type to be examined.</param>
229 /// <c>true</c> if the specified type is numeric; otherwise, <c>false</c>.
231 public static bool IsNumeric(Type type)
233 return type == typeof(double) ||
234 type == typeof(float) ||
235 type == typeof(decimal) ||
236 type == typeof(Int64) ||
237 type == typeof(Int32) ||
238 type == typeof(Int16) ||
239 type == typeof(UInt64) ||
240 type == typeof(UInt32) ||
241 type == typeof(UInt16) ||
242 type == typeof(byte) ||
243 type == typeof(sbyte);
247 /// Convert an argument list to the required parameter types.
248 /// Currently, only widening numeric conversions are performed.
250 /// <param name="arglist">An array of args to be converted</param>
251 /// <param name="parameters">A ParameterInfo[] whose types will be used as targets</param>
252 public static void ConvertArgumentList(object[] arglist, IParameterInfo[] parameters)
254 System.Diagnostics.Debug.Assert(arglist.Length <= parameters.Length);
256 for (int i = 0; i < arglist.Length; i++)
258 object arg = arglist[i];
263 if (arg != null && arg is IConvertible)
266 Type argType = arg.GetType();
267 Type targetType = parameters[i].ParameterType;
268 bool convert = false;
270 if (argType != targetType && !argType.IsAssignableFrom(targetType))
272 if (IsNumeric(argType) && IsNumeric(targetType))
274 if (targetType == typeof(double) || targetType == typeof(float))
275 convert = arg is int || arg is long || arg is short || arg is byte || arg is sbyte;
277 if (targetType == typeof(long))
278 convert = arg is int || arg is short || arg is byte || arg is sbyte;
280 if (targetType == typeof(short))
281 convert = arg is byte || arg is sbyte;
286 arglist[i] = Convert.ChangeType(arg, targetType,
287 System.Globalization.CultureInfo.InvariantCulture);
293 /// Determines whether this instance can deduce type args for a generic type from the supplied arguments.
295 /// <param name="type">The type to be examined.</param>
296 /// <param name="arglist">The arglist.</param>
297 /// <param name="typeArgsOut">The type args to be used.</param>
299 /// <c>true</c> if this the provided args give sufficient information to determine the type args to be used; otherwise, <c>false</c>.
301 public static bool CanDeduceTypeArgsFromArgs(Type type, object[] arglist, ref Type[] typeArgsOut)
303 Type[] typeParameters = type.GetGenericArguments();
305 #if NETCF || PORTABLE
306 Type[] argTypes = arglist.Select(a => a == null ? typeof(object) : a.GetType()).ToArray();
307 if (argTypes.Length != typeParameters.Length || argTypes.Any(at => at.GetTypeInfo().IsGenericType))
311 type = type.MakeGenericType(argTypes);
319 foreach (ConstructorInfo ctor in type.GetConstructors())
321 ParameterInfo[] parameters = ctor.GetParameters();
322 if (parameters.Length != arglist.Length)
325 Type[] typeArgs = new Type[typeParameters.Length];
326 for (int i = 0; i < typeArgs.Length; i++)
328 for (int j = 0; j < arglist.Length; j++)
330 if (typeParameters[i].IsGenericParameter || parameters[j].ParameterType.Equals(typeParameters[i]))
331 typeArgs[i] = TypeHelper.BestCommonType(
333 arglist[j].GetType());
336 if (typeArgs[i] == null)
343 if (typeArgs != null)
345 typeArgsOut = typeArgs;
354 /// Gets the _values for an enumeration, using Enum.GetTypes
355 /// where available, otherwise through reflection.
357 /// <param name="enumType"></param>
358 /// <returns></returns>
359 public static Array GetEnumValues(Type enumType)
361 #if NETCF || SILVERLIGHT
362 FieldInfo[] fields = enumType.GetFields(BindingFlags.Public | BindingFlags.Static);
364 Array enumValues = Array.CreateInstance(enumType, fields.Length);
366 for (int index = 0; index < fields.Length; index++)
367 enumValues.SetValue(fields[index].GetValue(enumType), index);
371 return Enum.GetValues(enumType);
376 /// Gets the ids of the _values for an enumeration,
377 /// using Enum.GetNames where available, otherwise
378 /// through reflection.
380 /// <param name="enumType"></param>
381 /// <returns></returns>
382 public static string[] GetEnumNames(Type enumType)
384 #if NETCF || SILVERLIGHT
385 FieldInfo[] fields = enumType.GetFields(BindingFlags.Public | BindingFlags.Static);
387 string[] names = new string[fields.Length];
389 for (int index = 0; index < fields.Length; index++)
390 names[index] = fields[index].Name;
394 return Enum.GetNames(enumType);