2021-10-01 09:09:20 +00:00
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Diagnostics.CodeAnalysis ;
using System.Globalization ;
using System.Linq ;
using System.Reflection ;
namespace Service.MeterProcessState.Areas.HelpPage
{
/// <summary>
2022-06-23 08:20:26 +00:00
/// This class will create an object of a given type and populate it with sample data. force push
2021-10-01 09:09:20 +00:00
/// </summary>
public class ObjectGenerator
{
2022-10-19 10:58:08 +00:00
internal const Int32 DefaultCollectionSize = 2 ;
2021-10-01 09:09:20 +00:00
private readonly SimpleTypeObjectGenerator SimpleObjectGenerator = new SimpleTypeObjectGenerator ( ) ;
/// <summary>
/// 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:
/// Simple types: <see cref="int"/>, <see cref="string"/>, <see cref="Enum"/>, <see cref="DateTime"/>, <see cref="Uri"/>, etc.
/// Complex types: POCO types.
/// Nullables: <see cref="Nullable{T}"/>.
/// Arrays: arrays of simple types or complex types.
/// Key value pairs: <see cref="KeyValuePair{TKey,TValue}"/>
/// Tuples: <see cref="Tuple{T1}"/>, <see cref="Tuple{T1,T2}"/>, etc
/// Dictionaries: <see cref="IDictionary{TKey,TValue}"/> or anything deriving from <see cref="IDictionary{TKey,TValue}"/>.
/// 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"/>.
/// Queryables: <see cref="IQueryable"/>, <see cref="IQueryable{T}"/>.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>An object of the given type.</returns>
2022-10-19 10:58:08 +00:00
public Object GenerateObject ( Type type )
2021-10-01 09:09:20 +00:00
{
2022-10-19 10:58:08 +00:00
return GenerateObject ( type , new Dictionary < Type , Object > ( ) ) ;
2021-10-01 09:09:20 +00:00
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Here we just want to return null if anything goes wrong.")]
2022-10-19 10:58:08 +00:00
private Object GenerateObject ( Type type , Dictionary < Type , Object > createdObjectReferences )
2021-10-01 09:09:20 +00:00
{
try
{
if ( SimpleTypeObjectGenerator . CanGenerateObject ( type ) )
{
return SimpleObjectGenerator . GenerateObject ( type ) ;
}
if ( type . IsArray )
{
return GenerateArray ( type , DefaultCollectionSize , createdObjectReferences ) ;
}
if ( type . IsGenericType )
{
return GenerateGenericType ( type , DefaultCollectionSize , createdObjectReferences ) ;
}
if ( type = = typeof ( IDictionary ) )
{
return GenerateDictionary ( typeof ( Hashtable ) , DefaultCollectionSize , createdObjectReferences ) ;
}
if ( typeof ( IDictionary ) . IsAssignableFrom ( type ) )
{
return GenerateDictionary ( type , DefaultCollectionSize , createdObjectReferences ) ;
}
if ( type = = typeof ( IList ) | |
type = = typeof ( IEnumerable ) | |
type = = typeof ( ICollection ) )
{
return GenerateCollection ( typeof ( ArrayList ) , DefaultCollectionSize , createdObjectReferences ) ;
}
if ( typeof ( IList ) . IsAssignableFrom ( type ) )
{
return GenerateCollection ( type , DefaultCollectionSize , createdObjectReferences ) ;
}
if ( type = = typeof ( IQueryable ) )
{
return GenerateQueryable ( type , DefaultCollectionSize , createdObjectReferences ) ;
}
if ( type . IsEnum )
{
return GenerateEnum ( type ) ;
}
if ( type . IsPublic | | type . IsNestedPublic )
{
return GenerateComplexObject ( type , createdObjectReferences ) ;
}
}
catch
{
// Returns null if anything fails
return null ;
}
return null ;
}
2022-10-19 10:58:08 +00:00
private static Object GenerateGenericType ( Type type , Int32 collectionSize , Dictionary < Type , Object > createdObjectReferences )
2021-10-01 09:09:20 +00:00
{
Type genericTypeDefinition = type . GetGenericTypeDefinition ( ) ;
if ( genericTypeDefinition = = typeof ( Nullable < > ) )
{
return GenerateNullable ( type , createdObjectReferences ) ;
}
if ( genericTypeDefinition = = typeof ( KeyValuePair < , > ) )
{
return GenerateKeyValuePair ( type , createdObjectReferences ) ;
}
if ( IsTuple ( genericTypeDefinition ) )
{
return GenerateTuple ( type , createdObjectReferences ) ;
}
Type [ ] genericArguments = type . GetGenericArguments ( ) ;
if ( genericArguments . Length = = 1 )
{
if ( genericTypeDefinition = = typeof ( IList < > ) | |
genericTypeDefinition = = typeof ( IEnumerable < > ) | |
genericTypeDefinition = = typeof ( ICollection < > ) )
{
Type collectionType = typeof ( List < > ) . MakeGenericType ( genericArguments ) ;
return GenerateCollection ( collectionType , collectionSize , createdObjectReferences ) ;
}
if ( genericTypeDefinition = = typeof ( IQueryable < > ) )
{
return GenerateQueryable ( type , collectionSize , createdObjectReferences ) ;
}
Type closedCollectionType = typeof ( ICollection < > ) . MakeGenericType ( genericArguments [ 0 ] ) ;
if ( closedCollectionType . IsAssignableFrom ( type ) )
{
return GenerateCollection ( type , collectionSize , createdObjectReferences ) ;
}
}
if ( genericArguments . Length = = 2 )
{
if ( genericTypeDefinition = = typeof ( IDictionary < , > ) )
{
Type dictionaryType = typeof ( Dictionary < , > ) . MakeGenericType ( genericArguments ) ;
return GenerateDictionary ( dictionaryType , collectionSize , createdObjectReferences ) ;
}
Type closedDictionaryType = typeof ( IDictionary < , > ) . MakeGenericType ( genericArguments [ 0 ] , genericArguments [ 1 ] ) ;
if ( closedDictionaryType . IsAssignableFrom ( type ) )
{
return GenerateDictionary ( type , collectionSize , createdObjectReferences ) ;
}
}
if ( type . IsPublic | | type . IsNestedPublic )
{
return GenerateComplexObject ( type , createdObjectReferences ) ;
}
return null ;
}
2022-10-19 10:58:08 +00:00
private static Object GenerateTuple ( Type type , Dictionary < Type , Object > createdObjectReferences )
2021-10-01 09:09:20 +00:00
{
Type [ ] genericArgs = type . GetGenericArguments ( ) ;
2022-10-19 10:58:08 +00:00
Object [ ] parameterValues = new Object [ genericArgs . Length ] ;
Boolean failedToCreateTuple = true ;
2021-10-01 09:09:20 +00:00
ObjectGenerator objectGenerator = new ObjectGenerator ( ) ;
2022-10-19 10:58:08 +00:00
for ( Int32 i = 0 ; i < genericArgs . Length ; i + + )
2021-10-01 09:09:20 +00:00
{
parameterValues [ i ] = objectGenerator . GenerateObject ( genericArgs [ i ] , createdObjectReferences ) ;
failedToCreateTuple & = parameterValues [ i ] = = null ;
}
if ( failedToCreateTuple )
{
return null ;
}
2022-10-19 10:58:08 +00:00
Object result = Activator . CreateInstance ( type , parameterValues ) ;
2021-10-01 09:09:20 +00:00
return result ;
}
2022-10-19 10:58:08 +00:00
private static Boolean IsTuple ( Type genericTypeDefinition )
2021-10-01 09:09:20 +00:00
{
return genericTypeDefinition = = typeof ( Tuple < > ) | |
genericTypeDefinition = = typeof ( Tuple < , > ) | |
genericTypeDefinition = = typeof ( Tuple < , , > ) | |
genericTypeDefinition = = typeof ( Tuple < , , , > ) | |
genericTypeDefinition = = typeof ( Tuple < , , , , > ) | |
genericTypeDefinition = = typeof ( Tuple < , , , , , > ) | |
genericTypeDefinition = = typeof ( Tuple < , , , , , , > ) | |
genericTypeDefinition = = typeof ( Tuple < , , , , , , , > ) ;
}
2022-10-19 10:58:08 +00:00
private static Object GenerateKeyValuePair ( Type keyValuePairType , Dictionary < Type , Object > createdObjectReferences )
2021-10-01 09:09:20 +00:00
{
Type [ ] genericArgs = keyValuePairType . GetGenericArguments ( ) ;
Type typeK = genericArgs [ 0 ] ;
Type typeV = genericArgs [ 1 ] ;
ObjectGenerator objectGenerator = new ObjectGenerator ( ) ;
2022-10-19 10:58:08 +00:00
Object keyObject = objectGenerator . GenerateObject ( typeK , createdObjectReferences ) ;
Object valueObject = objectGenerator . GenerateObject ( typeV , createdObjectReferences ) ;
2021-10-01 09:09:20 +00:00
if ( keyObject = = null & & valueObject = = null )
{
// Failed to create key and values
return null ;
}
2022-10-19 10:58:08 +00:00
Object result = Activator . CreateInstance ( keyValuePairType , keyObject , valueObject ) ;
2021-10-01 09:09:20 +00:00
return result ;
}
2022-10-19 10:58:08 +00:00
private static Object GenerateArray ( Type arrayType , Int32 size , Dictionary < Type , Object > createdObjectReferences )
2021-10-01 09:09:20 +00:00
{
Type type = arrayType . GetElementType ( ) ;
Array result = Array . CreateInstance ( type , size ) ;
2022-10-19 10:58:08 +00:00
Boolean areAllElementsNull = true ;
2021-10-01 09:09:20 +00:00
ObjectGenerator objectGenerator = new ObjectGenerator ( ) ;
2022-10-19 10:58:08 +00:00
for ( Int32 i = 0 ; i < size ; i + + )
2021-10-01 09:09:20 +00:00
{
2022-10-19 10:58:08 +00:00
Object element = objectGenerator . GenerateObject ( type , createdObjectReferences ) ;
2021-10-01 09:09:20 +00:00
result . SetValue ( element , i ) ;
areAllElementsNull & = element = = null ;
}
if ( areAllElementsNull )
{
return null ;
}
return result ;
}
2022-10-19 10:58:08 +00:00
private static Object GenerateDictionary ( Type dictionaryType , Int32 size , Dictionary < Type , Object > createdObjectReferences )
2021-10-01 09:09:20 +00:00
{
2022-10-19 10:58:08 +00:00
Type typeK = typeof ( Object ) ;
Type typeV = typeof ( Object ) ;
2021-10-01 09:09:20 +00:00
if ( dictionaryType . IsGenericType )
{
Type [ ] genericArgs = dictionaryType . GetGenericArguments ( ) ;
typeK = genericArgs [ 0 ] ;
typeV = genericArgs [ 1 ] ;
}
2022-10-19 10:58:08 +00:00
Object result = Activator . CreateInstance ( dictionaryType ) ;
2021-10-01 09:09:20 +00:00
MethodInfo addMethod = dictionaryType . GetMethod ( "Add" ) ? ? dictionaryType . GetMethod ( "TryAdd" ) ;
MethodInfo containsMethod = dictionaryType . GetMethod ( "Contains" ) ? ? dictionaryType . GetMethod ( "ContainsKey" ) ;
ObjectGenerator objectGenerator = new ObjectGenerator ( ) ;
2022-10-19 10:58:08 +00:00
for ( Int32 i = 0 ; i < size ; i + + )
2021-10-01 09:09:20 +00:00
{
2022-10-19 10:58:08 +00:00
Object newKey = objectGenerator . GenerateObject ( typeK , createdObjectReferences ) ;
2021-10-01 09:09:20 +00:00
if ( newKey = = null )
{
// Cannot generate a valid key
return null ;
}
2022-10-19 10:58:08 +00:00
Boolean containsKey = ( Boolean ) containsMethod . Invoke ( result , new Object [ ] { newKey } ) ;
2021-10-01 09:09:20 +00:00
if ( ! containsKey )
{
2022-10-19 10:58:08 +00:00
Object newValue = objectGenerator . GenerateObject ( typeV , createdObjectReferences ) ;
addMethod . Invoke ( result , new Object [ ] { newKey , newValue } ) ;
2021-10-01 09:09:20 +00:00
}
}
return result ;
}
2022-10-19 10:58:08 +00:00
private static Object GenerateEnum ( Type enumType )
2021-10-01 09:09:20 +00:00
{
Array possibleValues = Enum . GetValues ( enumType ) ;
if ( possibleValues . Length > 0 )
{
return possibleValues . GetValue ( 0 ) ;
}
return null ;
}
2022-10-19 10:58:08 +00:00
private static Object GenerateQueryable ( Type queryableType , Int32 size , Dictionary < Type , Object > createdObjectReferences )
2021-10-01 09:09:20 +00:00
{
2022-10-19 10:58:08 +00:00
Boolean isGeneric = queryableType . IsGenericType ;
Object list ;
2021-10-01 09:09:20 +00:00
if ( isGeneric )
{
Type listType = typeof ( List < > ) . MakeGenericType ( queryableType . GetGenericArguments ( ) ) ;
list = GenerateCollection ( listType , size , createdObjectReferences ) ;
}
else
{
2022-10-19 10:58:08 +00:00
list = GenerateArray ( typeof ( Object [ ] ) , size , createdObjectReferences ) ;
2021-10-01 09:09:20 +00:00
}
if ( list = = null )
{
return null ;
}
if ( isGeneric )
{
Type argumentType = typeof ( IEnumerable < > ) . MakeGenericType ( queryableType . GetGenericArguments ( ) ) ;
MethodInfo asQueryableMethod = typeof ( Queryable ) . GetMethod ( "AsQueryable" , new [ ] { argumentType } ) ;
return asQueryableMethod . Invoke ( null , new [ ] { list } ) ;
}
return Queryable . AsQueryable ( ( IEnumerable ) list ) ;
}
2022-10-19 10:58:08 +00:00
private static Object GenerateCollection ( Type collectionType , Int32 size , Dictionary < Type , Object > createdObjectReferences )
2021-10-01 09:09:20 +00:00
{
Type type = collectionType . IsGenericType ?
collectionType . GetGenericArguments ( ) [ 0 ] :
2022-10-19 10:58:08 +00:00
typeof ( Object ) ;
Object result = Activator . CreateInstance ( collectionType ) ;
2021-10-01 09:09:20 +00:00
MethodInfo addMethod = collectionType . GetMethod ( "Add" ) ;
2022-10-19 10:58:08 +00:00
Boolean areAllElementsNull = true ;
2021-10-01 09:09:20 +00:00
ObjectGenerator objectGenerator = new ObjectGenerator ( ) ;
2022-10-19 10:58:08 +00:00
for ( Int32 i = 0 ; i < size ; i + + )
2021-10-01 09:09:20 +00:00
{
2022-10-19 10:58:08 +00:00
Object element = objectGenerator . GenerateObject ( type , createdObjectReferences ) ;
addMethod . Invoke ( result , new Object [ ] { element } ) ;
2021-10-01 09:09:20 +00:00
areAllElementsNull & = element = = null ;
}
if ( areAllElementsNull )
{
return null ;
}
return result ;
}
2022-10-19 10:58:08 +00:00
private static Object GenerateNullable ( Type nullableType , Dictionary < Type , Object > createdObjectReferences )
2021-10-01 09:09:20 +00:00
{
Type type = nullableType . GetGenericArguments ( ) [ 0 ] ;
ObjectGenerator objectGenerator = new ObjectGenerator ( ) ;
return objectGenerator . GenerateObject ( type , createdObjectReferences ) ;
}
2022-10-19 10:58:08 +00:00
private static Object GenerateComplexObject ( Type type , Dictionary < Type , Object > createdObjectReferences )
2021-10-01 09:09:20 +00:00
{
2022-10-19 10:58:08 +00:00
Object result = null ;
2021-10-01 09:09:20 +00:00
if ( createdObjectReferences . TryGetValue ( type , out result ) )
{
// The object has been created already, just return it. This will handle the circular reference case.
return result ;
}
if ( type . IsValueType )
{
result = Activator . CreateInstance ( type ) ;
}
else
{
ConstructorInfo defaultCtor = type . GetConstructor ( Type . EmptyTypes ) ;
if ( defaultCtor = = null )
{
// Cannot instantiate the type because it doesn't have a default constructor
return null ;
}
2022-10-19 10:58:08 +00:00
result = defaultCtor . Invoke ( new Object [ 0 ] ) ;
2021-10-01 09:09:20 +00:00
}
createdObjectReferences . Add ( type , result ) ;
SetPublicProperties ( type , result , createdObjectReferences ) ;
SetPublicFields ( type , result , createdObjectReferences ) ;
return result ;
}
2022-10-19 10:58:08 +00:00
private static void SetPublicProperties ( Type type , Object obj , Dictionary < Type , Object > createdObjectReferences )
2021-10-01 09:09:20 +00:00
{
PropertyInfo [ ] properties = type . GetProperties ( BindingFlags . Public | BindingFlags . Instance ) ;
ObjectGenerator objectGenerator = new ObjectGenerator ( ) ;
foreach ( PropertyInfo property in properties )
{
if ( property . CanWrite )
{
2022-10-19 10:58:08 +00:00
Object propertyValue = objectGenerator . GenerateObject ( property . PropertyType , createdObjectReferences ) ;
2021-10-01 09:09:20 +00:00
property . SetValue ( obj , propertyValue , null ) ;
}
}
}
2022-10-19 10:58:08 +00:00
private static void SetPublicFields ( Type type , Object obj , Dictionary < Type , Object > createdObjectReferences )
2021-10-01 09:09:20 +00:00
{
FieldInfo [ ] fields = type . GetFields ( BindingFlags . Public | BindingFlags . Instance ) ;
ObjectGenerator objectGenerator = new ObjectGenerator ( ) ;
foreach ( FieldInfo field in fields )
{
2022-10-19 10:58:08 +00:00
Object fieldValue = objectGenerator . GenerateObject ( field . FieldType , createdObjectReferences ) ;
2021-10-01 09:09:20 +00:00
field . SetValue ( obj , fieldValue ) ;
}
}
private class SimpleTypeObjectGenerator
{
2022-10-19 10:58:08 +00:00
private Int64 _index = 0 ;
private static readonly Dictionary < Type , Func < Int64 , Object > > DefaultGenerators = InitializeGenerators ( ) ;
2021-10-01 09:09:20 +00:00
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These are simple type factories and cannot be split up.")]
2022-10-19 10:58:08 +00:00
private static Dictionary < Type , Func < Int64 , Object > > InitializeGenerators ( )
2021-10-01 09:09:20 +00:00
{
2022-10-19 10:58:08 +00:00
return new Dictionary < Type , Func < Int64 , Object > >
2021-10-01 09:09:20 +00:00
{
{ typeof ( Boolean ) , index = > true } ,
{ typeof ( Byte ) , index = > ( Byte ) 64 } ,
{ typeof ( Char ) , index = > ( Char ) 65 } ,
{ typeof ( DateTime ) , index = > DateTime . Now } ,
{ typeof ( DateTimeOffset ) , index = > new DateTimeOffset ( DateTime . Now ) } ,
{ typeof ( DBNull ) , index = > DBNull . Value } ,
{ typeof ( Decimal ) , index = > ( Decimal ) index } ,
{ typeof ( Double ) , index = > ( Double ) ( index + 0.1 ) } ,
{ typeof ( Guid ) , index = > Guid . NewGuid ( ) } ,
2022-10-19 10:58:08 +00:00
{ typeof ( Int16 ) , index = > ( Int16 ) ( index % short . MaxValue ) } ,
{ typeof ( Int32 ) , index = > ( Int32 ) ( index % int . MaxValue ) } ,
2021-10-01 09:09:20 +00:00
{ typeof ( Int64 ) , index = > ( Int64 ) index } ,
2022-10-19 10:58:08 +00:00
{ typeof ( Object ) , index = > new Object ( ) } ,
2021-10-01 09:09:20 +00:00
{ typeof ( SByte ) , index = > ( SByte ) 64 } ,
{ typeof ( Single ) , index = > ( Single ) ( index + 0.1 ) } ,
{
typeof ( String ) , index = >
{
2022-10-19 10:58:08 +00:00
return string . Format ( CultureInfo . CurrentCulture , "sample string {0}" , index ) ;
2021-10-01 09:09:20 +00:00
}
} ,
{
typeof ( TimeSpan ) , index = >
{
return TimeSpan . FromTicks ( 1234567 ) ;
}
} ,
2022-10-19 10:58:08 +00:00
{ typeof ( UInt16 ) , index = > ( UInt16 ) ( index % ushort . MaxValue ) } ,
{ typeof ( UInt32 ) , index = > ( UInt32 ) ( index % uint . MaxValue ) } ,
2021-10-01 09:09:20 +00:00
{ typeof ( UInt64 ) , index = > ( UInt64 ) index } ,
{
typeof ( Uri ) , index = >
{
2022-10-19 10:58:08 +00:00
return new Uri ( string . Format ( CultureInfo . CurrentCulture , "http://webapihelppage{0}.com" , index ) ) ;
2021-10-01 09:09:20 +00:00
}
} ,
} ;
}
2022-10-19 10:58:08 +00:00
public static Boolean CanGenerateObject ( Type type )
2021-10-01 09:09:20 +00:00
{
return DefaultGenerators . ContainsKey ( type ) ;
}
2022-10-19 10:58:08 +00:00
public Object GenerateObject ( Type type )
2021-10-01 09:09:20 +00:00
{
return DefaultGenerators [ type ] ( + + _index ) ;
}
}
}
}