247 lines
9.2 KiB
C#
247 lines
9.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using UnityEditor;
|
|
|
|
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;
|
|
}
|
|
|
|
public static float CalculateHeight(UnityEngine.Object target, ResultPrimary[] primaries)
|
|
{
|
|
float total = EditorGUIUtility.singleLineHeight;
|
|
|
|
for (var i = 0; i < primaries.Length; i++)
|
|
{
|
|
ResultPrimary primary = primaries[i];
|
|
total += primary.drawer.GetHeight(target, primary.methodInfo);
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
} |