using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace Module.Inspector.Editor.Utilities { internal static class EditorMethodUtility { private static Dictionary CACHED_TYPE_TO_PRIMARIES; private static Dictionary CACHED_RESULTS; private static Dictionary CACHED_ATT_TO_DRAWER; private static void InitializeCache() { if (CACHED_ATT_TO_DRAWER != null) return; CACHED_ATT_TO_DRAWER = new Dictionary(); 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 attributes = type.GetCustomAttributesData(); for (var j = 0; j < attributes.Count; j++) { CustomAttributeData attData = attributes[j]; IList 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 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(); 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(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 drawer = null; ResultValue decorator = null; var accessModifiers = new List>(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(attDrawer); if (prop != null) drawer = new ResultValue(attDrawer, prop); } else if (att is AccessModifierMethodAttribute accessModifier) { var prop = InternalCreateInstanceOf(accessModifier); if (prop != null) accessModifiers.Add(new ResultValue(accessModifier, prop)); } else if (att is DecoratorMethodAttribute attDecorator) { if (decorator != null) continue; var prop = InternalCreateInstanceOf(attDecorator); if (prop != null) decorator = new ResultValue(attDecorator, prop); } } if (drawer == null && decorator == null && accessModifiers.Count == 0) return null; return new Result(drawer, decorator, accessModifiers); } private static T InternalCreateInstanceOf(AbstractMethodAttribute att) where T : AbstractMethodDrawer { if (CACHED_ATT_TO_DRAWER.TryGetValue(att.GetType(), out Type drawerType)) return Activator.CreateInstance(drawerType) as T; return null; } /// /// Class: Result from method query /// public sealed class Result { public readonly ResultValue draw; public readonly ResultValue decorator; public readonly List> accessModifiers; public Result(ResultValue draw, ResultValue decorator, List> accessModifiers) { this.draw = draw; this.decorator = decorator; this.accessModifiers = accessModifiers; } public T GetAccessModifier() where T : AccessModifierMethodAttribute { for (var i = 0; i < accessModifiers.Count; i++) { if (accessModifiers[i].attribute is T att) return att; } return null; } } /// /// Class: Contains attribute and drawer values /// /// /// public sealed class ResultValue 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; } } /// /// Class: Contains primary method info and drawer for a specific method /// 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; } } } }