Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Diagnostics.CodeAnalysis
Imports System.Globalization
Imports System.Linq
Imports System.Reflection
Imports Microsoft.VisualBasic
Namespace Areas.HelpPage
'''
''' This class will create an object of a given type and populate it with sample data.
'''
Public Class ObjectGenerator
Friend Const DefaultCollectionSize As Integer = 2
Private ReadOnly SimpleObjectGenerator As New SimpleTypeObjectGenerator()
'''
''' 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: , , , , , etc.
''' Complex types: POCO types.
''' Nullables: .
''' Arrays: arrays of simple types or complex types.
''' Key value pairs:
''' Tuples: , , etc
''' Dictionaries: or anything deriving from .
''' Collections: , , , , , or anything deriving from or .
''' Queryables: , .
'''
''' The type.
''' An object of the given type.
Public Function GenerateObject(type As Type) As Object
GenerateObject = GenerateObject(type, New Dictionary(Of Type, Object)())
End Function
Private Function GenerateObject(type As Type, createdObjectReferences As Dictionary(Of Type, Object)) As Object
Try
If (SimpleTypeObjectGenerator.CanGenerateObject(type)) Then
Return SimpleObjectGenerator.GenerateObject(type)
End If
If (type.IsArray) Then
Return GenerateArray(type, DefaultCollectionSize, createdObjectReferences)
End If
If (type.IsGenericType) Then
Return GenerateGenericType(type, DefaultCollectionSize, createdObjectReferences)
End If
If (type Is GetType(IDictionary)) Then
Return GenerateDictionary(GetType(Hashtable), DefaultCollectionSize, createdObjectReferences)
End If
If (GetType(IDictionary).IsAssignableFrom(type)) Then
Return GenerateDictionary(type, DefaultCollectionSize, createdObjectReferences)
End If
If (type Is GetType(IList) Or
type Is GetType(IEnumerable) Or
type Is GetType(ICollection)) Then
Return GenerateCollection(GetType(ArrayList), DefaultCollectionSize, createdObjectReferences)
End If
If (GetType(IList).IsAssignableFrom(type)) Then
Return GenerateCollection(type, DefaultCollectionSize, createdObjectReferences)
End If
If (type Is GetType(IQueryable)) Then
Return GenerateQueryable(type, DefaultCollectionSize, createdObjectReferences)
End If
If (type.IsEnum) Then
Return GenerateEnum(type)
End If
If (type.IsPublic Or type.IsNestedPublic) Then
Return GenerateComplexObject(type, createdObjectReferences)
End If
Catch
' Returns Nothing if anything fails
Return Nothing
End Try
Return Nothing
End Function
Private Shared Function GenerateGenericType(type As Type, collectionSize As Integer, createdObjectReferences As Dictionary(Of Type, Object)) As Object
Dim genericTypeDefinition As Type = type.GetGenericTypeDefinition()
If (genericTypeDefinition Is GetType(Nullable()) Or genericTypeDefinition Is GetType(Nullable(Of ))) Then
Return GenerateNullable(type, createdObjectReferences)
End If
If (genericTypeDefinition Is GetType(KeyValuePair(Of ,))) Then
Return GenerateKeyValuePair(type, createdObjectReferences)
End If
If (IsTuple(genericTypeDefinition)) Then
Return GenerateTuple(type, createdObjectReferences)
End If
Dim genericArguments() As Type = type.GetGenericArguments()
If (genericArguments.Length = 1) Then
If (genericTypeDefinition Is GetType(IList(Of )) Or
genericTypeDefinition Is GetType(IEnumerable(Of )) Or
genericTypeDefinition Is GetType(ICollection(Of ))) Then
Dim collectionType As Type = GetType(List(Of )).MakeGenericType(genericArguments)
Return GenerateCollection(collectionType, collectionSize, createdObjectReferences)
End If
If (genericTypeDefinition Is GetType(IQueryable(Of ))) Then
Return GenerateQueryable(type, collectionSize, createdObjectReferences)
End If
Dim closedCollectionType As Type = GetType(ICollection(Of )).MakeGenericType(genericArguments(0))
If (closedCollectionType.IsAssignableFrom(type)) Then
Return GenerateCollection(type, collectionSize, createdObjectReferences)
End If
End If
If (genericArguments.Length = 2) Then
If (genericTypeDefinition Is GetType(IDictionary(Of ,))) Then
Dim dictionaryType As Type = GetType(Dictionary(Of ,)).MakeGenericType(genericArguments)
Return GenerateDictionary(dictionaryType, collectionSize, createdObjectReferences)
End If
Dim closedDictionaryType As Type = GetType(IDictionary(Of ,)).MakeGenericType(genericArguments(0), genericArguments(1))
If (closedDictionaryType.IsAssignableFrom(type)) Then
Return GenerateDictionary(type, collectionSize, createdObjectReferences)
End If
End If
If (type.IsPublic Or type.IsNestedPublic) Then
Return GenerateComplexObject(type, createdObjectReferences)
End If
Return Nothing
End Function
Private Shared Function GenerateTuple(type As Type, createdObjectReferences As Dictionary(Of Type, Object)) As Object
Dim genericArgs() As Type = type.GetGenericArguments()
Dim parameterValues(genericArgs.Length - 1) As Object
Dim failedToCreateTuple As Boolean = True
Dim objectGenerator As New ObjectGenerator()
For i As Integer = 0 To genericArgs.Length - 1
parameterValues(i) = objectGenerator.GenerateObject(genericArgs(i), createdObjectReferences)
failedToCreateTuple = failedToCreateTuple And (parameterValues(i) Is Nothing)
Next
If (failedToCreateTuple) Then
Return Nothing
End If
Return Activator.CreateInstance(type, parameterValues)
End Function
Private Shared Function IsTuple(genericTypeDefinition As Type) As Boolean
Return (genericTypeDefinition Is GetType(Tuple(Of )) Or
genericTypeDefinition Is GetType(Tuple(Of ,)) Or
genericTypeDefinition Is GetType(Tuple(Of ,,)) Or
genericTypeDefinition Is GetType(Tuple(Of ,,,)) Or
genericTypeDefinition Is GetType(Tuple(Of ,,,,)) Or
genericTypeDefinition Is GetType(Tuple(Of ,,,,,)) Or
genericTypeDefinition Is GetType(Tuple(Of ,,,,,,)) Or
genericTypeDefinition Is GetType(Tuple(Of ,,,,,,,)))
End Function
Private Shared Function GenerateKeyValuePair(keyValuePairType As Type, createdObjectReferences As Dictionary(Of Type, Object)) As Object
Dim genericArgs() As Type = keyValuePairType.GetGenericArguments()
Dim typeK As Type = genericArgs(0)
Dim typeV As Type = genericArgs(1)
Dim objectGenerator As New ObjectGenerator()
Dim keyObject As Object = objectGenerator.GenerateObject(typeK, createdObjectReferences)
Dim valueObject As Object = objectGenerator.GenerateObject(typeV, createdObjectReferences)
If (keyObject Is Nothing And valueObject Is Nothing) Then
' Failed to create key and values
Return Nothing
End If
Return Activator.CreateInstance(keyValuePairType, keyObject, valueObject)
End Function
Private Shared Function GenerateArray(arrayType As Type, size As Integer, createdObjectReferences As Dictionary(Of Type, Object)) As Object
Dim type As Type = arrayType.GetElementType()
Dim result As Array = Array.CreateInstance(type, size)
Dim areAllElementsNothing As Boolean = True
Dim objectGenerator As New ObjectGenerator()
For i As Integer = 0 To size - 1
Dim element As Object = objectGenerator.GenerateObject(type, createdObjectReferences)
result.SetValue(element, i)
areAllElementsNothing = areAllElementsNothing And (element Is Nothing)
Next
If (areAllElementsNothing) Then
Return Nothing
End If
Return result
End Function
Private Shared Function GenerateDictionary(dictionaryType As Type, size As Integer, createdObjectReferences As Dictionary(Of Type, Object)) As Object
Dim typeK As Type = GetType(Object)
Dim typeV As Type = GetType(Object)
If (dictionaryType.IsGenericType) Then
Dim genericArgs() As Type = dictionaryType.GetGenericArguments()
typeK = genericArgs(0)
typeV = genericArgs(1)
End If
Dim result As Object = Activator.CreateInstance(dictionaryType)
Dim addMethod As MethodInfo = If(dictionaryType.GetMethod("Add"), dictionaryType.GetMethod("TryAdd"))
Dim containsMethod As MethodInfo = If(dictionaryType.GetMethod("Contains"), dictionaryType.GetMethod("ContainsKey"))
Dim objectGenerator As New ObjectGenerator()
For i As Integer = 0 To size - 1
Dim newKey As Object = objectGenerator.GenerateObject(typeK, createdObjectReferences)
If (newKey Is Nothing) Then
' Cannot generate a valid key
Return Nothing
End If
Dim containsKey As Boolean = DirectCast(containsMethod.Invoke(result, New Object() {newKey}), Boolean)
If Not containsKey Then
Dim newValue As Object = objectGenerator.GenerateObject(typeV, createdObjectReferences)
addMethod.Invoke(result, New Object() {newKey, newValue})
End If
Next
Return result
End Function
Private Shared Function GenerateEnum(enumType As Type) As Object
Dim possibleValues As Array = [Enum].GetValues(enumType)
If possibleValues.Length > 0 Then
Return possibleValues.GetValue(0)
End If
Return Nothing
End Function
Private Shared Function GenerateQueryable(queryableType As Type, size As Integer, createdObjectReferences As Dictionary(Of Type, Object)) As Object
Dim isGeneric As Boolean = queryableType.IsGenericType
Dim list As Object = Nothing
If (isGeneric) Then
Dim listType As Type = GetType(List(Of )).MakeGenericType(queryableType.GetGenericArguments())
list = GenerateCollection(listType, size, createdObjectReferences)
Else
list = GenerateArray(GetType(Object()), size, createdObjectReferences)
End If
If (list Is Nothing) Then
Return Nothing
End If
If (isGeneric) Then
Dim argumentType As Type = GetType(IEnumerable(Of )).MakeGenericType(queryableType.GetGenericArguments())
Dim asQueryableMethod As MethodInfo = GetType(Queryable).GetMethod("AsQueryable", New Type() {argumentType})
Return asQueryableMethod.Invoke(Nothing, New Object() {list})
End If
Return Queryable.AsQueryable(DirectCast(list, IEnumerable))
End Function
Private Shared Function GenerateCollection(collectionType As Type, size As Integer, createdObjectReferences As Dictionary(Of Type, Object)) As Object
Dim type As Type = If(collectionType.IsGenericType, collectionType.GetGenericArguments()(0), GetType(Object))
Dim result As Object = Activator.CreateInstance(collectionType)
Dim addMethod As MethodInfo = collectionType.GetMethod("Add")
Dim areAllElementsNothing As Boolean = True
Dim objectGenerator As New ObjectGenerator()
For i As Integer = 0 To size - 1
Dim element As Object = objectGenerator.GenerateObject(type, createdObjectReferences)
addMethod.Invoke(result, New Object() {element})
areAllElementsNothing = areAllElementsNothing And (element Is Nothing)
Next
If (areAllElementsNothing) Then
Return Nothing
End If
Return result
End Function
Private Shared Function GenerateNullable(nullableType As Type, createdObjectReferences As Dictionary(Of Type, Object)) As Object
Dim type As Type = nullableType.GetGenericArguments()(0)
Dim objectGenerator As New ObjectGenerator()
Return objectGenerator.GenerateObject(type, createdObjectReferences)
End Function
Private Shared Function GenerateComplexObject(type As Type, createdObjectReferences As Dictionary(Of Type, Object)) As Object
Dim result As Object = Nothing
If (createdObjectReferences.TryGetValue(type, result)) Then
' The object has been created already, just return it. This will handle the circular reference case.
Return result
End If
If (type.IsValueType) Then
result = Activator.CreateInstance(type)
Else
Dim defaultCtor As ConstructorInfo = type.GetConstructor(type.EmptyTypes)
If (defaultCtor Is Nothing) Then
' Cannot instantiate the type because it doesn't have a default constructor
Return Nothing
End If
result = defaultCtor.Invoke(New Object() {})
End If
createdObjectReferences.Add(type, result)
SetPublicProperties(type, result, createdObjectReferences)
SetPublicFields(type, result, createdObjectReferences)
Return result
End Function
Private Shared Sub SetPublicProperties(type As Type, obj As Object, createdObjectReferences As Dictionary(Of Type, Object))
Dim properties() As PropertyInfo = type.GetProperties(BindingFlags.Public Or BindingFlags.Instance)
Dim objectGenerator As New ObjectGenerator()
For Each prop As PropertyInfo In properties
If (prop.CanWrite) Then
Dim propertyValue As Object = objectGenerator.GenerateObject(prop.PropertyType, createdObjectReferences)
prop.SetValue(obj, propertyValue, Nothing)
End If
Next
End Sub
Private Shared Sub SetPublicFields(type As Type, obj As Object, createdObjectReferences As Dictionary(Of Type, Object))
Dim fields() As FieldInfo = type.GetFields(BindingFlags.Public Or BindingFlags.Instance)
Dim objectGenerator As New ObjectGenerator()
For Each field As FieldInfo In fields
Dim fieldValue As Object = objectGenerator.GenerateObject(field.FieldType, createdObjectReferences)
field.SetValue(obj, fieldValue)
Next
End Sub
Private Class SimpleTypeObjectGenerator
Private _index As Long = 0
Private Shared ReadOnly DefaultGenerators As Dictionary(Of Type, Func(Of Long, Object)) = InitializeGenerators()
Private Shared Function InitializeGenerators() As Dictionary(Of Type, Func(Of Long, Object))
Return New Dictionary(Of Type, Func(Of Long, Object)) From
{
{GetType(Boolean), Function(index As Long) True},
{GetType(Byte), Function(index As Long) CByte(64)},
{GetType(Char), Function(index As Long) ChrW(65)},
{GetType(DateTime), Function(index As Long) DateTime.Now},
{GetType(DateTimeOffset), Function(index As Long) New DateTimeOffset(DateTime.Now)},
{GetType(DBNull), Function(index As Long) DBNull.Value},
{GetType(Decimal), Function(index As Long) CDec(index)},
{GetType(Double), Function(index As Long) CDbl(index) + 0.1},
{GetType(Guid), Function(index As Long) Guid.NewGuid()},
{GetType(Int16), Function(index As Long) CType(index Mod Int16.MaxValue, Int16)},
{GetType(Int32), Function(index As Long) CType(index Mod Int32.MaxValue, Int32)},
{GetType(Int64), Function(index As Long) CType(index, Int64)},
{GetType(Object), Function(index As Long) New Object},
{GetType(SByte), Function(index As Long) CSByte(64)},
{GetType(Single), Function(index As Long) CSng(index + 0.1)},
{GetType(String), Function(index As Long) String.Format(CultureInfo.CurrentCulture, "sample string {0}", index)},
{GetType(TimeSpan), Function(index As Long) TimeSpan.FromTicks(1234567)},
{GetType(UInt16), Function(index As Long) CType(index Mod UInt16.MaxValue, UInt16)},
{GetType(UInt32), Function(index As Long) CType(index Mod UInt32.MaxValue, UInt32)},
{GetType(UInt64), Function(index As Long) CType(index, UInt64)},
{GetType(Uri), Function(index As Long) New Uri(String.Format(CultureInfo.CurrentCulture, "http://webapihelppage{0}.com", index))}
}
End Function
Public Shared Function CanGenerateObject(type As Type) As Boolean
Return DefaultGenerators.ContainsKey(type)
End Function
Public Function GenerateObject(type As Type) As Object
_index += 1
Return DefaultGenerators(type)(_index)
End Function
End Class
End Class
End Namespace