278 lines
8.6 KiB
C#
278 lines
8.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
|
|
namespace zero.Core.Utils
|
|
{
|
|
public class ObjectTraverser
|
|
{
|
|
private const string SEPARATOR = ".";
|
|
|
|
private const string SQBKT_OPEN = "[";
|
|
|
|
private const string SQBKT_CLOSE = "]";
|
|
|
|
private const string NEWTONSOFT_NS = "Newtonsoft.Json";
|
|
|
|
|
|
/// <summary>
|
|
/// Find all instances of a given type in an object (features nested + enumerable content)
|
|
/// </summary>
|
|
public static List<Result<T>> Find<T>(object value) where T : class
|
|
{
|
|
HashSet<object> exploredObjects = new HashSet<object>();
|
|
List<Result<T>> found = new List<Result<T>>();
|
|
|
|
Find<T>(value, null, String.Empty, null, exploredObjects, found);
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Find all instances of a given type in an object (features nested + enumerable content)
|
|
/// </summary>
|
|
public static List<Result> Find(Type type, object value)
|
|
{
|
|
HashSet<object> exploredObjects = new HashSet<object>();
|
|
List<Result> found = new List<Result>();
|
|
|
|
Find(type, value, null, String.Empty, null, exploredObjects, found);
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Find all instances of a given type in an object (features nested + enumerable content)
|
|
/// </summary>
|
|
public static List<Result<T>> FindAttribute<T>(object value) where T : Attribute
|
|
{
|
|
HashSet<string> exploredObjects = new HashSet<string>();
|
|
List<Result<T>> found = new List<Result<T>>();
|
|
|
|
FindAttribute<T>(value, null, String.Empty, null, exploredObjects, found);
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Recursively ind all instances of a given type in an object
|
|
/// </summary>
|
|
private static void Find<T>(object value, object parent, string key, PropertyInfo property, HashSet<object> exploredObjects, List<Result<T>> found) where T : class
|
|
{
|
|
if (value == null || value is string || exploredObjects.Contains(value) || value.GetType().FullName.StartsWith(NEWTONSOFT_NS))
|
|
{
|
|
return;
|
|
}
|
|
|
|
exploredObjects.Add(value);
|
|
|
|
System.Collections.ICollection enumerable = value as System.Collections.ICollection;
|
|
|
|
// this property is a list
|
|
if (enumerable != null)
|
|
{
|
|
int idx = 0;
|
|
foreach (object item in enumerable)
|
|
{
|
|
Find<T>(item, value, CombineKey(String.Empty, key, SQBKT_OPEN + idx + SQBKT_CLOSE), property, exploredObjects, found);
|
|
idx += 1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get type of value
|
|
Type type = typeof(T);
|
|
Type valueType = value.GetType();
|
|
Type genericValueType = valueType.IsGenericType ? valueType.GetGenericTypeDefinition() : null;
|
|
|
|
// check if the current property is our searched type
|
|
if (valueType == type || genericValueType == type || type.IsAssignableFrom(valueType) || (genericValueType != null && type.IsAssignableFrom(genericValueType)))
|
|
{
|
|
found.Add(new Result<T>()
|
|
{
|
|
Path = key,
|
|
Item = value as T,
|
|
Parent = parent,
|
|
Property = property
|
|
});
|
|
return;
|
|
}
|
|
|
|
// check if our property is a primitive type
|
|
if (value == null || valueType.IsPrimitive || value is string || value is DateTime || value is DateTimeOffset || value is Uri)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// iterate over nested properties
|
|
PropertyInfo[] properties = valueType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
|
|
foreach (PropertyInfo nestedProperty in properties)
|
|
{
|
|
object propertyValue = nestedProperty.GetValue(value, null);
|
|
Find<T>(propertyValue, value, CombineKey(SEPARATOR, key, nestedProperty.Name), nestedProperty, exploredObjects, found);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Recursively ind all instances of a given type in an object
|
|
/// </summary>
|
|
private static void Find(Type type, object value, object parent, string key, PropertyInfo property, HashSet<object> exploredObjects, List<Result> found)
|
|
{
|
|
if (value == null || value is string || exploredObjects.Contains(value) || value.GetType().FullName.StartsWith(NEWTONSOFT_NS))
|
|
{
|
|
return;
|
|
}
|
|
|
|
exploredObjects.Add(value);
|
|
|
|
System.Collections.ICollection enumerable = value as System.Collections.ICollection;
|
|
|
|
// this property is a list
|
|
if (enumerable != null)
|
|
{
|
|
int idx = 0;
|
|
foreach (object item in enumerable)
|
|
{
|
|
Find(type, item, value, CombineKey(String.Empty, key, SQBKT_OPEN + idx + SQBKT_CLOSE), property, exploredObjects, found);
|
|
idx += 1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get type of value
|
|
Type valueType = value.GetType();
|
|
Type genericValueType = valueType.IsGenericType ? valueType.GetGenericTypeDefinition() : null;
|
|
|
|
// check if the current property is our searched type
|
|
if (valueType == type || genericValueType == type || type.IsAssignableFrom(valueType) || (genericValueType != null && type.IsAssignableFrom(genericValueType)))
|
|
{
|
|
found.Add(new Result()
|
|
{
|
|
Path = key,
|
|
Item = value,
|
|
Type = valueType,
|
|
Parent = parent,
|
|
Property = property
|
|
});
|
|
return;
|
|
}
|
|
|
|
// check if our property is a primitive type
|
|
if (value == null || valueType.IsPrimitive || value is string || value is DateTime || value is DateTimeOffset || value is Uri)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// iterate over nested properties
|
|
PropertyInfo[] properties = valueType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
|
|
foreach (PropertyInfo nestedProperty in properties)
|
|
{
|
|
object propertyValue = nestedProperty.GetValue(value, null);
|
|
Find(type, propertyValue, value, CombineKey(SEPARATOR, key, nestedProperty.Name), nestedProperty, exploredObjects, found);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Recursively find all instances with the attribute of a given type in an object
|
|
/// </summary>
|
|
private static void FindAttribute<T>(object value, object parent, string key, PropertyInfo property, HashSet<string> exploredObjects, List<Result<T>> found) where T : Attribute
|
|
{
|
|
if (exploredObjects.Contains(key) || (value != null && value.GetType().FullName.StartsWith(NEWTONSOFT_NS)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
exploredObjects.Add(key);
|
|
|
|
// check if the current property contains the attribute
|
|
if (property != null)
|
|
{
|
|
T attribute = property.GetCustomAttribute<T>(true);
|
|
|
|
if (attribute != null)
|
|
{
|
|
found.Add(new Result<T>()
|
|
{
|
|
Path = key,
|
|
Item = attribute,
|
|
Parent = parent,
|
|
Property = property //parent.GetType().GetProperty(key)
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
System.Collections.ICollection enumerable = value as System.Collections.ICollection;
|
|
|
|
// this property is a list
|
|
if (enumerable != null && !(value is string))
|
|
{
|
|
int idx = 0;
|
|
foreach (object item in enumerable)
|
|
{
|
|
FindAttribute<T>(item, value, CombineKey(String.Empty, key, SQBKT_OPEN + idx + SQBKT_CLOSE), null, exploredObjects, found);
|
|
idx += 1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// check if our property is a primitive type
|
|
if (value == null || value.GetType().IsPrimitive || value is string || value is DateTime || value is DateTimeOffset || value is Uri)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// iterate over nested properties
|
|
PropertyInfo[] properties = value.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
|
|
foreach (PropertyInfo nestedProperty in properties)
|
|
{
|
|
object propertyValue = nestedProperty.GetValue(value, null);
|
|
FindAttribute<T>(propertyValue, value, CombineKey(SEPARATOR, key, nestedProperty.Name), nestedProperty, exploredObjects, found);
|
|
}
|
|
}
|
|
|
|
|
|
private static string CombineKey(string sep, string key1, string key2)
|
|
{
|
|
if (String.IsNullOrEmpty(key1))
|
|
{
|
|
return key2;
|
|
}
|
|
return String.Join(sep, key1, key2);
|
|
}
|
|
|
|
|
|
public class Result<T>
|
|
{
|
|
public string Path { get; set; }
|
|
|
|
public PropertyInfo Property { get; set; }
|
|
|
|
public object Parent { get; set; }
|
|
|
|
public T Item { get; set; }
|
|
}
|
|
|
|
public class Result
|
|
{
|
|
public string Path { get; set; }
|
|
|
|
public PropertyInfo Property { get; set; }
|
|
|
|
public object Parent { get; set; }
|
|
|
|
public object Item { get; set; }
|
|
|
|
public Type Type { get; set; }
|
|
|
|
public int Index { get; set; }
|
|
}
|
|
}
|
|
}
|