using System; using System.Collections.Generic; using System.Reflection; using UnityEngine; namespace Module.Inspector.Editor.Utilities { public static class EditorPropertyUtility { 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(AbstractPropertyDrawer); 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)) continue; if (CACHED_ATT_TO_DRAWER.ContainsKey(argType)) Debug.LogWarningFormat("Already contained {0}, but tried to add with {1} and already had {2}.", argType, type, CACHED_ATT_TO_DRAWER[argType]); else CACHED_ATT_TO_DRAWER.Add(argType, type); } } } } public static Result Query(FieldInfo fieldInfo) { InitializeCache(); if (CACHED_RESULTS == null) CACHED_RESULTS = new Dictionary(); if (CACHED_RESULTS.TryGetValue(fieldInfo, out Result result)) return result; result = InternalFetchProperties(fieldInfo); CACHED_RESULTS.Add(fieldInfo, result); return result; } private static Result InternalFetchProperties(FieldInfo fieldInfo) { ResultValue drawer = null; var typeDrawer = InternalCreateInstanceOf(fieldInfo); if (typeDrawer != null) drawer = new ResultValue(null, typeDrawer); var predrawerModifiers = new List>(2); var valueModifiers = new List>(2); var accessModifiers = new List>(2); object[] attributes = fieldInfo.GetCustomAttributes(false); string tooltip = null; var isObsolete = false; string obsoleteText = null; for (var i = 0; i < attributes.Length; i++) { object att = attributes[i]; if (att is DrawerPropertyAttribute attDrawer) { if (drawer != null) continue; var prop = InternalCreateInstanceOf(attDrawer); if (prop != null) drawer = new ResultValue(attDrawer, prop); } else if (att is PredrawerModifierPropertyAttribute predawerModifier) { var prop = InternalCreateInstanceOf(predawerModifier); if (prop != null) predrawerModifiers.Add(new ResultValue(predawerModifier, prop)); } else if (att is ValueModifierPropertyAttribute valueModifier) { var prop = InternalCreateInstanceOf(valueModifier); if (prop != null) valueModifiers.Add(new ResultValue(valueModifier, prop)); } else if (att is AccessModifierPropertyAttribute accessModifier) { var prop = InternalCreateInstanceOf(accessModifier); if (prop != null) accessModifiers.Add(new ResultValue(accessModifier, prop)); } else if (att is TooltipAttribute attTooltip) { tooltip = attTooltip.tooltip; } else if (att is ObsoleteAttribute attObsolete) { isObsolete = true; obsoleteText = attObsolete.Message; } } if (!string.IsNullOrEmpty(obsoleteText)) { if (tooltip != null) tooltip += $"\n[Obsolete: {obsoleteText}"; else tooltip = $"Obsolete: {obsoleteText}"; } return new Result(drawer, predrawerModifiers, valueModifiers, accessModifiers, tooltip, isObsolete); } private static T InternalCreateInstanceOf(AbstractPropertyAttribute att) where T : AbstractPropertyDrawer { if (CACHED_ATT_TO_DRAWER.TryGetValue(att.GetType(), out Type drawerType)) return Activator.CreateInstance(drawerType) as T; return null; } private static T InternalCreateInstanceOf(FieldInfo fieldInfo) where T : AbstractPropertyDrawer { if (CACHED_ATT_TO_DRAWER.TryGetValue(fieldInfo.FieldType, out Type drawerType)) return Activator.CreateInstance(drawerType) as T; return null; } /// /// Class: Result from property query /// public sealed class Result { public readonly ResultValue draw; public readonly List> predrawerModifiers; public readonly List> valueModifiers; public readonly List> accessModifiers; public readonly string tooltip; public readonly bool isObsolete; public Result(ResultValue draw, List> predrawerModifiers, List> valueModifiers, List> accessModifiers, string tooltip, bool isObsolete) { this.draw = draw; this.predrawerModifiers = predrawerModifiers; this.valueModifiers = valueModifiers; this.accessModifiers = accessModifiers; this.tooltip = tooltip; this.isObsolete = isObsolete; } public T GetPredrawerModifier() where T : PredrawerModifierPropertyAttribute { for (var i = 0; i < predrawerModifiers.Count; i++) { if (predrawerModifiers[i].attribute is T att) return att; } return null; } public T GetValueModifier() where T : ValueModifierPropertyAttribute { for (var i = 0; i < valueModifiers.Count; i++) { if (valueModifiers[i].attribute is T att) return att; } return null; } public T GetAccessModifier() where T : AccessModifierPropertyAttribute { 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 : AbstractPropertyAttribute where T1 : AbstractPropertyDrawer { public readonly T0 attribute; public readonly T1 drawer; public ResultValue(T0 attribute, T1 drawer) { this.attribute = attribute; this.drawer = drawer; } } } }