PageRenderTime 40ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/Gradebook/Areas/HelpPage/SampleGeneration/ObjectGenerator.cs

https://bitbucket.org/academium/gradebook
C# | 456 lines | 384 code | 50 blank | 22 comment | 73 complexity | 88d15c5ccdf8c70296a7d8912e93a597 MD5 | raw file
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.Globalization;
  6. using System.Linq;
  7. using System.Reflection;
  8. namespace Gradebook.Areas.HelpPage
  9. {
  10. /// <summary>
  11. /// This class will create an object of a given type and populate it with sample data.
  12. /// </summary>
  13. public class ObjectGenerator
  14. {
  15. private const int DefaultCollectionSize = 3;
  16. private readonly SimpleTypeObjectGenerator SimpleObjectGenerator = new SimpleTypeObjectGenerator();
  17. /// <summary>
  18. /// Generates an object for a given type. The type needs to be public, have a public default constructor and settable public properties/fields. Currently it supports the following types:
  19. /// Simple types: <see cref="int"/>, <see cref="string"/>, <see cref="Enum"/>, <see cref="DateTime"/>, <see cref="Uri"/>, etc.
  20. /// Complex types: POCO types.
  21. /// Nullables: <see cref="Nullable{T}"/>.
  22. /// Arrays: arrays of simple types or complex types.
  23. /// Key value pairs: <see cref="KeyValuePair{TKey,TValue}"/>
  24. /// Tuples: <see cref="Tuple{T1}"/>, <see cref="Tuple{T1,T2}"/>, etc
  25. /// Dictionaries: <see cref="IDictionary{TKey,TValue}"/> or anything deriving from <see cref="IDictionary{TKey,TValue}"/>.
  26. /// Collections: <see cref="IList{T}"/>, <see cref="IEnumerable{T}"/>, <see cref="ICollection{T}"/>, <see cref="IList"/>, <see cref="IEnumerable"/>, <see cref="ICollection"/> or anything deriving from <see cref="ICollection{T}"/> or <see cref="IList"/>.
  27. /// Queryables: <see cref="IQueryable"/>, <see cref="IQueryable{T}"/>.
  28. /// </summary>
  29. /// <param name="type">The type.</param>
  30. /// <returns>An object of the given type.</returns>
  31. public object GenerateObject(Type type)
  32. {
  33. return GenerateObject(type, new Dictionary<Type, object>());
  34. }
  35. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Here we just want to return null if anything goes wrong.")]
  36. private object GenerateObject(Type type, Dictionary<Type, object> createdObjectReferences)
  37. {
  38. try
  39. {
  40. if (SimpleTypeObjectGenerator.CanGenerateObject(type))
  41. {
  42. return SimpleObjectGenerator.GenerateObject(type);
  43. }
  44. if (type.IsArray)
  45. {
  46. return GenerateArray(type, DefaultCollectionSize, createdObjectReferences);
  47. }
  48. if (type.IsGenericType)
  49. {
  50. return GenerateGenericType(type, DefaultCollectionSize, createdObjectReferences);
  51. }
  52. if (type == typeof(IDictionary))
  53. {
  54. return GenerateDictionary(typeof(Hashtable), DefaultCollectionSize, createdObjectReferences);
  55. }
  56. if (typeof(IDictionary).IsAssignableFrom(type))
  57. {
  58. return GenerateDictionary(type, DefaultCollectionSize, createdObjectReferences);
  59. }
  60. if (type == typeof(IList) ||
  61. type == typeof(IEnumerable) ||
  62. type == typeof(ICollection))
  63. {
  64. return GenerateCollection(typeof(ArrayList), DefaultCollectionSize, createdObjectReferences);
  65. }
  66. if (typeof(IList).IsAssignableFrom(type))
  67. {
  68. return GenerateCollection(type, DefaultCollectionSize, createdObjectReferences);
  69. }
  70. if (type == typeof(IQueryable))
  71. {
  72. return GenerateQueryable(type, DefaultCollectionSize, createdObjectReferences);
  73. }
  74. if (type.IsEnum)
  75. {
  76. return GenerateEnum(type);
  77. }
  78. if (type.IsPublic || type.IsNestedPublic)
  79. {
  80. return GenerateComplexObject(type, createdObjectReferences);
  81. }
  82. }
  83. catch
  84. {
  85. // Returns null if anything fails
  86. return null;
  87. }
  88. return null;
  89. }
  90. private static object GenerateGenericType(Type type, int collectionSize, Dictionary<Type, object> createdObjectReferences)
  91. {
  92. Type genericTypeDefinition = type.GetGenericTypeDefinition();
  93. if (genericTypeDefinition == typeof(Nullable<>))
  94. {
  95. return GenerateNullable(type, createdObjectReferences);
  96. }
  97. if (genericTypeDefinition == typeof(KeyValuePair<,>))
  98. {
  99. return GenerateKeyValuePair(type, createdObjectReferences);
  100. }
  101. if (IsTuple(genericTypeDefinition))
  102. {
  103. return GenerateTuple(type, createdObjectReferences);
  104. }
  105. Type[] genericArguments = type.GetGenericArguments();
  106. if (genericArguments.Length == 1)
  107. {
  108. if (genericTypeDefinition == typeof(IList<>) ||
  109. genericTypeDefinition == typeof(IEnumerable<>) ||
  110. genericTypeDefinition == typeof(ICollection<>))
  111. {
  112. Type collectionType = typeof(List<>).MakeGenericType(genericArguments);
  113. return GenerateCollection(collectionType, collectionSize, createdObjectReferences);
  114. }
  115. if (genericTypeDefinition == typeof(IQueryable<>))
  116. {
  117. return GenerateQueryable(type, collectionSize, createdObjectReferences);
  118. }
  119. Type closedCollectionType = typeof(ICollection<>).MakeGenericType(genericArguments[0]);
  120. if (closedCollectionType.IsAssignableFrom(type))
  121. {
  122. return GenerateCollection(type, collectionSize, createdObjectReferences);
  123. }
  124. }
  125. if (genericArguments.Length == 2)
  126. {
  127. if (genericTypeDefinition == typeof(IDictionary<,>))
  128. {
  129. Type dictionaryType = typeof(Dictionary<,>).MakeGenericType(genericArguments);
  130. return GenerateDictionary(dictionaryType, collectionSize, createdObjectReferences);
  131. }
  132. Type closedDictionaryType = typeof(IDictionary<,>).MakeGenericType(genericArguments[0], genericArguments[1]);
  133. if (closedDictionaryType.IsAssignableFrom(type))
  134. {
  135. return GenerateDictionary(type, collectionSize, createdObjectReferences);
  136. }
  137. }
  138. if (type.IsPublic || type.IsNestedPublic)
  139. {
  140. return GenerateComplexObject(type, createdObjectReferences);
  141. }
  142. return null;
  143. }
  144. private static object GenerateTuple(Type type, Dictionary<Type, object> createdObjectReferences)
  145. {
  146. Type[] genericArgs = type.GetGenericArguments();
  147. object[] parameterValues = new object[genericArgs.Length];
  148. bool failedToCreateTuple = true;
  149. ObjectGenerator objectGenerator = new ObjectGenerator();
  150. for (int i = 0; i < genericArgs.Length; i++)
  151. {
  152. parameterValues[i] = objectGenerator.GenerateObject(genericArgs[i], createdObjectReferences);
  153. failedToCreateTuple &= parameterValues[i] == null;
  154. }
  155. if (failedToCreateTuple)
  156. {
  157. return null;
  158. }
  159. object result = Activator.CreateInstance(type, parameterValues);
  160. return result;
  161. }
  162. private static bool IsTuple(Type genericTypeDefinition)
  163. {
  164. return genericTypeDefinition == typeof(Tuple<>) ||
  165. genericTypeDefinition == typeof(Tuple<,>) ||
  166. genericTypeDefinition == typeof(Tuple<,,>) ||
  167. genericTypeDefinition == typeof(Tuple<,,,>) ||
  168. genericTypeDefinition == typeof(Tuple<,,,,>) ||
  169. genericTypeDefinition == typeof(Tuple<,,,,,>) ||
  170. genericTypeDefinition == typeof(Tuple<,,,,,,>) ||
  171. genericTypeDefinition == typeof(Tuple<,,,,,,,>);
  172. }
  173. private static object GenerateKeyValuePair(Type keyValuePairType, Dictionary<Type, object> createdObjectReferences)
  174. {
  175. Type[] genericArgs = keyValuePairType.GetGenericArguments();
  176. Type typeK = genericArgs[0];
  177. Type typeV = genericArgs[1];
  178. ObjectGenerator objectGenerator = new ObjectGenerator();
  179. object keyObject = objectGenerator.GenerateObject(typeK, createdObjectReferences);
  180. object valueObject = objectGenerator.GenerateObject(typeV, createdObjectReferences);
  181. if (keyObject == null && valueObject == null)
  182. {
  183. // Failed to create key and values
  184. return null;
  185. }
  186. object result = Activator.CreateInstance(keyValuePairType, keyObject, valueObject);
  187. return result;
  188. }
  189. private static object GenerateArray(Type arrayType, int size, Dictionary<Type, object> createdObjectReferences)
  190. {
  191. Type type = arrayType.GetElementType();
  192. Array result = Array.CreateInstance(type, size);
  193. bool areAllElementsNull = true;
  194. ObjectGenerator objectGenerator = new ObjectGenerator();
  195. for (int i = 0; i < size; i++)
  196. {
  197. object element = objectGenerator.GenerateObject(type, createdObjectReferences);
  198. result.SetValue(element, i);
  199. areAllElementsNull &= element == null;
  200. }
  201. if (areAllElementsNull)
  202. {
  203. return null;
  204. }
  205. return result;
  206. }
  207. private static object GenerateDictionary(Type dictionaryType, int size, Dictionary<Type, object> createdObjectReferences)
  208. {
  209. Type typeK = typeof(object);
  210. Type typeV = typeof(object);
  211. if (dictionaryType.IsGenericType)
  212. {
  213. Type[] genericArgs = dictionaryType.GetGenericArguments();
  214. typeK = genericArgs[0];
  215. typeV = genericArgs[1];
  216. }
  217. object result = Activator.CreateInstance(dictionaryType);
  218. MethodInfo addMethod = dictionaryType.GetMethod("Add") ?? dictionaryType.GetMethod("TryAdd");
  219. MethodInfo containsMethod = dictionaryType.GetMethod("Contains") ?? dictionaryType.GetMethod("ContainsKey");
  220. ObjectGenerator objectGenerator = new ObjectGenerator();
  221. for (int i = 0; i < size; i++)
  222. {
  223. object newKey = objectGenerator.GenerateObject(typeK, createdObjectReferences);
  224. if (newKey == null)
  225. {
  226. // Cannot generate a valid key
  227. return null;
  228. }
  229. bool containsKey = (bool)containsMethod.Invoke(result, new object[] { newKey });
  230. if (!containsKey)
  231. {
  232. object newValue = objectGenerator.GenerateObject(typeV, createdObjectReferences);
  233. addMethod.Invoke(result, new object[] { newKey, newValue });
  234. }
  235. }
  236. return result;
  237. }
  238. private static object GenerateEnum(Type enumType)
  239. {
  240. Array possibleValues = Enum.GetValues(enumType);
  241. if (possibleValues.Length > 0)
  242. {
  243. return possibleValues.GetValue(0);
  244. }
  245. return null;
  246. }
  247. private static object GenerateQueryable(Type queryableType, int size, Dictionary<Type, object> createdObjectReferences)
  248. {
  249. bool isGeneric = queryableType.IsGenericType;
  250. object list;
  251. if (isGeneric)
  252. {
  253. Type listType = typeof(List<>).MakeGenericType(queryableType.GetGenericArguments());
  254. list = GenerateCollection(listType, size, createdObjectReferences);
  255. }
  256. else
  257. {
  258. list = GenerateArray(typeof(object[]), size, createdObjectReferences);
  259. }
  260. if (list == null)
  261. {
  262. return null;
  263. }
  264. if (isGeneric)
  265. {
  266. Type argumentType = typeof(IEnumerable<>).MakeGenericType(queryableType.GetGenericArguments());
  267. MethodInfo asQueryableMethod = typeof(Queryable).GetMethod("AsQueryable", new[] { argumentType });
  268. return asQueryableMethod.Invoke(null, new[] { list });
  269. }
  270. return Queryable.AsQueryable((IEnumerable)list);
  271. }
  272. private static object GenerateCollection(Type collectionType, int size, Dictionary<Type, object> createdObjectReferences)
  273. {
  274. Type type = collectionType.IsGenericType ?
  275. collectionType.GetGenericArguments()[0] :
  276. typeof(object);
  277. object result = Activator.CreateInstance(collectionType);
  278. MethodInfo addMethod = collectionType.GetMethod("Add");
  279. bool areAllElementsNull = true;
  280. ObjectGenerator objectGenerator = new ObjectGenerator();
  281. for (int i = 0; i < size; i++)
  282. {
  283. object element = objectGenerator.GenerateObject(type, createdObjectReferences);
  284. addMethod.Invoke(result, new object[] { element });
  285. areAllElementsNull &= element == null;
  286. }
  287. if (areAllElementsNull)
  288. {
  289. return null;
  290. }
  291. return result;
  292. }
  293. private static object GenerateNullable(Type nullableType, Dictionary<Type, object> createdObjectReferences)
  294. {
  295. Type type = nullableType.GetGenericArguments()[0];
  296. ObjectGenerator objectGenerator = new ObjectGenerator();
  297. return objectGenerator.GenerateObject(type, createdObjectReferences);
  298. }
  299. private static object GenerateComplexObject(Type type, Dictionary<Type, object> createdObjectReferences)
  300. {
  301. object result = null;
  302. if (createdObjectReferences.TryGetValue(type, out result))
  303. {
  304. // The object has been created already, just return it. This will handle the circular reference case.
  305. return result;
  306. }
  307. if (type.IsValueType)
  308. {
  309. result = Activator.CreateInstance(type);
  310. }
  311. else
  312. {
  313. ConstructorInfo defaultCtor = type.GetConstructor(Type.EmptyTypes);
  314. if (defaultCtor == null)
  315. {
  316. // Cannot instantiate the type because it doesn't have a default constructor
  317. return null;
  318. }
  319. result = defaultCtor.Invoke(new object[0]);
  320. }
  321. createdObjectReferences.Add(type, result);
  322. SetPublicProperties(type, result, createdObjectReferences);
  323. SetPublicFields(type, result, createdObjectReferences);
  324. return result;
  325. }
  326. private static void SetPublicProperties(Type type, object obj, Dictionary<Type, object> createdObjectReferences)
  327. {
  328. PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
  329. ObjectGenerator objectGenerator = new ObjectGenerator();
  330. foreach (PropertyInfo property in properties)
  331. {
  332. if (property.CanWrite)
  333. {
  334. object propertyValue = objectGenerator.GenerateObject(property.PropertyType, createdObjectReferences);
  335. property.SetValue(obj, propertyValue, null);
  336. }
  337. }
  338. }
  339. private static void SetPublicFields(Type type, object obj, Dictionary<Type, object> createdObjectReferences)
  340. {
  341. FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
  342. ObjectGenerator objectGenerator = new ObjectGenerator();
  343. foreach (FieldInfo field in fields)
  344. {
  345. object fieldValue = objectGenerator.GenerateObject(field.FieldType, createdObjectReferences);
  346. field.SetValue(obj, fieldValue);
  347. }
  348. }
  349. private class SimpleTypeObjectGenerator
  350. {
  351. private long _index = 0;
  352. private static readonly Dictionary<Type, Func<long, object>> DefaultGenerators = InitializeGenerators();
  353. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These are simple type factories and cannot be split up.")]
  354. private static Dictionary<Type, Func<long, object>> InitializeGenerators()
  355. {
  356. return new Dictionary<Type, Func<long, object>>
  357. {
  358. { typeof(Boolean), index => true },
  359. { typeof(Byte), index => (Byte)64 },
  360. { typeof(Char), index => (Char)65 },
  361. { typeof(DateTime), index => DateTime.Now },
  362. { typeof(DateTimeOffset), index => new DateTimeOffset(DateTime.Now) },
  363. { typeof(DBNull), index => DBNull.Value },
  364. { typeof(Decimal), index => (Decimal)index },
  365. { typeof(Double), index => (Double)(index + 0.1) },
  366. { typeof(Guid), index => Guid.NewGuid() },
  367. { typeof(Int16), index => (Int16)(index % Int16.MaxValue) },
  368. { typeof(Int32), index => (Int32)(index % Int32.MaxValue) },
  369. { typeof(Int64), index => (Int64)index },
  370. { typeof(Object), index => new object() },
  371. { typeof(SByte), index => (SByte)64 },
  372. { typeof(Single), index => (Single)(index + 0.1) },
  373. {
  374. typeof(String), index =>
  375. {
  376. return String.Format(CultureInfo.CurrentCulture, "sample string {0}", index);
  377. }
  378. },
  379. {
  380. typeof(TimeSpan), index =>
  381. {
  382. return TimeSpan.FromTicks(1234567);
  383. }
  384. },
  385. { typeof(UInt16), index => (UInt16)(index % UInt16.MaxValue) },
  386. { typeof(UInt32), index => (UInt32)(index % UInt32.MaxValue) },
  387. { typeof(UInt64), index => (UInt64)index },
  388. {
  389. typeof(Uri), index =>
  390. {
  391. return new Uri(String.Format(CultureInfo.CurrentCulture, "http://webapihelppage{0}.com", index));
  392. }
  393. },
  394. };
  395. }
  396. public static bool CanGenerateObject(Type type)
  397. {
  398. return DefaultGenerators.ContainsKey(type);
  399. }
  400. public object GenerateObject(Type type)
  401. {
  402. return DefaultGenerators[type](++_index);
  403. }
  404. }
  405. }
  406. }