module-inspector/Editor/Utilities/EditorMethodUtility.cs

233 lines
8.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Module.Inspector.Editor.Utilities
{
internal static class EditorMethodUtility
{
private static Dictionary<Type, ResultPrimary[]> CACHED_TYPE_TO_PRIMARIES;
private static Dictionary<MethodInfo, Result> CACHED_RESULTS;
private static Dictionary<Type, Type> CACHED_ATT_TO_DRAWER;
private static void InitializeCache()
{
if (CACHED_ATT_TO_DRAWER != null)
return;
CACHED_ATT_TO_DRAWER = new Dictionary<Type, Type>();
Type baseDrawerType = typeof(AbstractMethodDrawer);
var assembly = Assembly.GetAssembly(baseDrawerType);
Type[] types = assembly.GetTypes();
for (var i = 0; i < types.Length; i++)
{
Type type = types[i];
if (type.IsAbstract || !baseDrawerType.IsAssignableFrom(type))
continue;
IList<CustomAttributeData> attributes = type.GetCustomAttributesData();
for (var j = 0; j < attributes.Count; j++)
{
CustomAttributeData attData = attributes[j];
IList<CustomAttributeTypedArgument> arguments = attData.ConstructorArguments;
for (var k = 0; k < arguments.Count; k++)
{
if (arguments[k].Value is Type argType)
CACHED_ATT_TO_DRAWER.Add(argType, type);
}
}
}
}
public static ResultPrimary[] QueryPrimary(Object target)
{
InitializeCache();
if (CACHED_TYPE_TO_PRIMARIES == null)
CACHED_TYPE_TO_PRIMARIES = new Dictionary<Type, ResultPrimary[]>();
Type type = target.GetType();
if (CACHED_TYPE_TO_PRIMARIES.TryGetValue(type, out ResultPrimary[] primaries))
return primaries;
primaries = InternalFetchPrimary(type);
CACHED_TYPE_TO_PRIMARIES.Add(type, primaries);
return primaries;
}
public static Result Query(MethodInfo methodInfo)
{
InitializeCache();
if (CACHED_RESULTS == null)
CACHED_RESULTS = new Dictionary<MethodInfo, Result>();
if (CACHED_RESULTS.TryGetValue(methodInfo, out Result result))
return result;
result = InternalFetchProperties(methodInfo);
if (result != null)
CACHED_RESULTS.Add(methodInfo, result);
return result;
}
private static ResultPrimary[] InternalFetchPrimary(Type type)
{
const BindingFlags FLAGS = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
MethodInfo[] methods = type.GetMethods(FLAGS)
.Where(m => m.GetParameters().Length == 0)
.ToArray();
var list = new List<ResultPrimary>(methods.Length);
for (var i = 0; i < methods.Length; i++)
{
Result result = Query(methods[i]);
if (result != null)
list.Add(new ResultPrimary(methods[i], result));
}
return list.ToArray();
}
private static Result InternalFetchProperties(MethodInfo methodInfo)
{
ResultValue<DrawerMethodAttribute, DrawerMethodDrawer> drawer = null;
ResultValue<DecoratorMethodAttribute, DecoratorMethodDrawer> decorator = null;
var accessModifiers = new List<ResultValue<AccessModifierMethodAttribute, AccessModifierMethodDrawer>>(2);
object[] attributes = methodInfo.GetCustomAttributes(false);
for (var i = 0; i < attributes.Length; i++)
{
object att = attributes[i];
if (att is DrawerMethodAttribute attDrawer)
{
if (drawer != null)
continue;
var prop = InternalCreateInstanceOf<DrawerMethodDrawer>(attDrawer);
if (prop != null)
drawer = new ResultValue<DrawerMethodAttribute, DrawerMethodDrawer>(attDrawer, prop);
}
else if (att is AccessModifierMethodAttribute accessModifier)
{
var prop = InternalCreateInstanceOf<AccessModifierMethodDrawer>(accessModifier);
if (prop != null)
accessModifiers.Add(new ResultValue<AccessModifierMethodAttribute, AccessModifierMethodDrawer>(accessModifier, prop));
}
else if (att is DecoratorMethodAttribute attDecorator)
{
if (decorator != null)
continue;
var prop = InternalCreateInstanceOf<DecoratorMethodDrawer>(attDecorator);
if (prop != null)
decorator = new ResultValue<DecoratorMethodAttribute, DecoratorMethodDrawer>(attDecorator, prop);
}
}
if (drawer == null && decorator == null && accessModifiers.Count == 0)
return null;
return new Result(drawer, decorator, accessModifiers);
}
private static T InternalCreateInstanceOf<T>(AbstractMethodAttribute att) where T : AbstractMethodDrawer
{
if (CACHED_ATT_TO_DRAWER.TryGetValue(att.GetType(), out Type drawerType))
return Activator.CreateInstance(drawerType) as T;
return null;
}
/// <summary>
/// Class: Result from method query
/// </summary>
public sealed class Result
{
public readonly ResultValue<DrawerMethodAttribute, DrawerMethodDrawer> draw;
public readonly ResultValue<DecoratorMethodAttribute, DecoratorMethodDrawer> decorator;
public readonly List<ResultValue<AccessModifierMethodAttribute, AccessModifierMethodDrawer>> accessModifiers;
public Result(ResultValue<DrawerMethodAttribute, DrawerMethodDrawer> draw,
ResultValue<DecoratorMethodAttribute, DecoratorMethodDrawer> decorator,
List<ResultValue<AccessModifierMethodAttribute, AccessModifierMethodDrawer>> accessModifiers)
{
this.draw = draw;
this.decorator = decorator;
this.accessModifiers = accessModifiers;
}
public T GetAccessModifier<T>() where T : AccessModifierMethodAttribute
{
for (var i = 0; i < accessModifiers.Count; i++)
{
if (accessModifiers[i].attribute is T att)
return att;
}
return null;
}
}
/// <summary>
/// Class: Contains attribute and drawer values
/// </summary>
/// <typeparam name="T0"></typeparam>
/// <typeparam name="T1"></typeparam>
public sealed class ResultValue<T0, T1> where T0 : AbstractMethodAttribute
where T1 : AbstractMethodDrawer
{
public readonly T0 attribute;
public readonly T1 drawer;
public ResultValue(T0 attribute, T1 drawer)
{
this.attribute = attribute;
this.drawer = drawer;
}
}
/// <summary>
/// Class: Contains primary method info and drawer for a specific method
/// </summary>
public sealed class ResultPrimary
{
public readonly MethodInfo methodInfo;
public readonly AbstractMethodDrawer drawer;
public ResultPrimary(MethodInfo methodInfo, AbstractMethodDrawer drawer)
{
this.methodInfo = methodInfo;
this.drawer = drawer;
}
public ResultPrimary(MethodInfo methodInfo, Result result)
{
this.methodInfo = methodInfo;
if (result.draw != null)
drawer = result.draw.drawer;
else if (result.decorator != null)
drawer = result.decorator.drawer;
else if (result.accessModifiers != null && result.accessModifiers.Count > 0)
drawer = result.accessModifiers[0].drawer;
}
}
}
}