using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
namespace Cinemachine.Utility
{
/// An ad-hoc collection of helpers for reflection, used by Cinemachine
/// or its editor tools in various places
[DocumentationSorting(DocumentationSortingAttribute.Level.Undoc)]
public static class ReflectionHelpers
{
/// Copy the fields from one object to another
/// The source object to copy from
/// The destination object to copy to
/// The mask to filter the attributes.
/// Only those fields that get caught in the filter will be copied
public static void CopyFields(
System.Object src, System.Object dst,
System.Reflection.BindingFlags bindingAttr
= System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance)
{
if (src != null && dst != null)
{
Type type = src.GetType();
FieldInfo[] fields = type.GetFields(bindingAttr);
for (int i = 0; i < fields.Length; ++i)
if (!fields[i].IsStatic)
fields[i].SetValue(dst, fields[i].GetValue(src));
}
}
/// Search the assembly for all types that match a predicate
/// The assembly to search
/// The type to look for
/// A list of types found in the assembly that inherit from the predicate
public static IEnumerable GetTypesInAssembly(
Assembly assembly, Predicate predicate)
{
if (assembly == null)
return null;
Type[] types = new Type[0];
try
{
types = assembly.GetTypes();
}
catch (Exception)
{
// Can't load the types in this assembly
}
types = (from t in types
where t != null && predicate(t)
select t).ToArray();
return types;
}
/// Get a type from a name
/// The name of the type to search for
/// The type matching the name, or null if not found
public static Type GetTypeInAllDependentAssemblies(string typeName)
{
foreach (Type type in GetTypesInAllDependentAssemblies(t => t.Name == typeName))
return type;
return null;
}
/// Search all assemblies for all types that match a predicate
/// The type to look for
/// A list of types found in the assembly that inherit from the predicate
public static IEnumerable GetTypesInAllDependentAssemblies(Predicate predicate)
{
List foundTypes = new List(100);
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
string definedIn = typeof(CinemachineComponentBase).Assembly.GetName().Name;
foreach (Assembly assembly in assemblies)
{
// Note that we have to call GetName().Name. Just GetName() will not work.
if ((!assembly.GlobalAssemblyCache)
&& ((assembly.GetName().Name == definedIn)
|| assembly.GetReferencedAssemblies().Any(a => a.Name == definedIn)))
try
{
foreach (Type foundType in GetTypesInAssembly(assembly, predicate))
foundTypes.Add(foundType);
}
catch (Exception) {} // Just skip uncooperative assemblies
}
return foundTypes;
}
#if false
/// call GetTypesInAssembly() for all assemblies that match a predicate
/// Which assemblies to search
/// What type to look for
public static IEnumerable GetTypesInLoadedAssemblies(
Predicate assemblyPredicate, Predicate predicate)
{
Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
assemblies = assemblies.Where((Assembly assembly)
=> { return assemblyPredicate(assembly); }).OrderBy((Assembly ass)
=> { return ass.FullName; }).ToArray();
List foundTypes = new List(100);
foreach (Assembly assembly in assemblies)
{
foreach (Type foundType in GetTypesInAssembly(assembly, predicate))
foundTypes.Add(foundType);
}
return foundTypes;
}
/// Is a type defined and visible
/// Fullly-qualified type name
/// true if the type exists
public static bool TypeIsDefined(string fullname)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
try
{
foreach (var type in assembly.GetTypes())
if (type.FullName == fullname)
return true;
}
catch (System.Exception) {} // Just skip uncooperative assemblies
}
return false;
}
#endif
/// Cheater extension to access internal field of an object
/// The field type
/// The type of the field
/// The object to access
/// The string name of the field to access
/// The value of the field in the objects
public static T AccessInternalField(this Type type, object obj, string memberName)
{
if (string.IsNullOrEmpty(memberName) || (type == null))
return default(T);
System.Reflection.BindingFlags bindingFlags = System.Reflection.BindingFlags.NonPublic;
if (obj != null)
bindingFlags |= System.Reflection.BindingFlags.Instance;
else
bindingFlags |= System.Reflection.BindingFlags.Static;
FieldInfo field = type.GetField(memberName, bindingFlags);
if ((field != null) && (field.FieldType == typeof(T)))
return (T)field.GetValue(obj);
else
return default(T);
}
#if false
/// Cheater extension to access internal property of an object
/// The field type
/// The type of the field
/// The object to access
/// The string name of the field to access
/// The value of the field in the objects
public static T AccessInternalProperty(this Type type, object obj, string memberName)
{
if (string.IsNullOrEmpty(memberName) || (type == null))
return default(T);
System.Reflection.BindingFlags bindingFlags = System.Reflection.BindingFlags.NonPublic;
if (obj != null)
bindingFlags |= System.Reflection.BindingFlags.Instance;
else
bindingFlags |= System.Reflection.BindingFlags.Static;
PropertyInfo pi = type.GetProperty(memberName, bindingFlags);
if ((pi != null) && (pi.PropertyType == typeof(T)))
return (T)pi.GetValue(obj, null);
else
return default(T);
}
#endif
/// Get the object owner of a field. This method processes
/// the '.' separator to get from the object that owns the compound field
/// to the object that owns the leaf field
/// The name of the field, which may contain '.' separators
/// the owner of the compound field
/// The object owner of the field
public static object GetParentObject(string path, object obj)
{
var fields = path.Split('.');
if (fields.Length == 1)
return obj;
var info = obj.GetType().GetField(
fields[0], System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance);
obj = info.GetValue(obj);
return GetParentObject(string.Join(".", fields, 1, fields.Length - 1), obj);
}
/// Returns a string path from an expression - mostly used to retrieve serialized properties
/// without hardcoding the field path. Safer, and allows for proper refactoring.
/// Magic expression
/// Magic expression
/// Magic expression
/// The string version of the field path
public static string GetFieldPath(Expression> expr)
{
MemberExpression me;
switch (expr.Body.NodeType)
{
case ExpressionType.MemberAccess:
me = expr.Body as MemberExpression;
break;
default:
throw new InvalidOperationException();
}
var members = new List();
while (me != null)
{
members.Add(me.Member.Name);
me = me.Expression as MemberExpression;
}
var sb = new StringBuilder();
for (int i = members.Count - 1; i >= 0; i--)
{
sb.Append(members[i]);
if (i > 0) sb.Append('.');
}
return sb.ToString();
}
}
}