module-inspector/Editor/Utilities/EditorPropertyUtility.cs

245 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
namespace Module.Inspector.Editor.Utilities
{
public static class EditorPropertyUtility
{
private static Dictionary<FieldInfo, 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(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<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))
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<FieldInfo, Result>();
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<DrawerPropertyAttribute, DrawerPropertyDrawer> drawer = null;
var typeDrawer = InternalCreateInstanceOf<DrawerPropertyDrawer>(fieldInfo);
if (typeDrawer != null)
drawer = new ResultValue<DrawerPropertyAttribute, DrawerPropertyDrawer>(null, typeDrawer);
var predrawerModifiers = new List<ResultValue<PredrawerModifierPropertyAttribute, PredrawerModifierPropertyDrawer>>(2);
var valueModifiers = new List<ResultValue<ValueModifierPropertyAttribute, ValueModifierPropertyDrawer>>(2);
var accessModifiers = new List<ResultValue<AccessModifierPropertyAttribute, AccessModifierPropertyDrawer>>(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<DrawerPropertyDrawer>(attDrawer);
if (prop != null)
drawer = new ResultValue<DrawerPropertyAttribute, DrawerPropertyDrawer>(attDrawer, prop);
}
else if (att is PredrawerModifierPropertyAttribute predawerModifier)
{
var prop = InternalCreateInstanceOf<PredrawerModifierPropertyDrawer>(predawerModifier);
if (prop != null)
predrawerModifiers.Add(new ResultValue<PredrawerModifierPropertyAttribute, PredrawerModifierPropertyDrawer>(predawerModifier, prop));
}
else if (att is ValueModifierPropertyAttribute valueModifier)
{
var prop = InternalCreateInstanceOf<ValueModifierPropertyDrawer>(valueModifier);
if (prop != null)
valueModifiers.Add(new ResultValue<ValueModifierPropertyAttribute, ValueModifierPropertyDrawer>(valueModifier, prop));
}
else if (att is AccessModifierPropertyAttribute accessModifier)
{
var prop = InternalCreateInstanceOf<AccessModifierPropertyDrawer>(accessModifier);
if (prop != null)
accessModifiers.Add(new ResultValue<AccessModifierPropertyAttribute, AccessModifierPropertyDrawer>(accessModifier, prop));
}
else if (att is TooltipAttribute attTooltip)
{
tooltip = attTooltip.tooltip;
}
else if (att is ObsoleteAttribute attObsolete)
{
isObsolete = true;
obsoleteText = attObsolete.Message;
}
}
if (isObsolete)
{
if (string.IsNullOrEmpty(obsoleteText))
{
if (tooltip != null)
tooltip += $"\n[Obsolete]";
else
tooltip = $"[Obsolete]";
}
else
{
if (tooltip != null)
tooltip += $"\n[Obsolete: {obsoleteText}]";
else
tooltip = $"[Obsolete: {obsoleteText}]";
}
}
return new Result(drawer, predrawerModifiers, valueModifiers, accessModifiers, tooltip, isObsolete);
}
private static T InternalCreateInstanceOf<T>(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<T>(FieldInfo fieldInfo) where T : AbstractPropertyDrawer
{
if (CACHED_ATT_TO_DRAWER.TryGetValue(fieldInfo.FieldType, out Type drawerType))
return Activator.CreateInstance(drawerType) as T;
return null;
}
/// <summary>
/// Class: Result from property query
/// </summary>
public sealed class Result
{
public readonly ResultValue<DrawerPropertyAttribute, DrawerPropertyDrawer> draw;
public readonly List<ResultValue<PredrawerModifierPropertyAttribute, PredrawerModifierPropertyDrawer>> predrawerModifiers;
public readonly List<ResultValue<ValueModifierPropertyAttribute, ValueModifierPropertyDrawer>> valueModifiers;
public readonly List<ResultValue<AccessModifierPropertyAttribute, AccessModifierPropertyDrawer>> accessModifiers;
public readonly string tooltip;
public readonly bool isObsolete;
public Result(ResultValue<DrawerPropertyAttribute, DrawerPropertyDrawer> draw,
List<ResultValue<PredrawerModifierPropertyAttribute, PredrawerModifierPropertyDrawer>> predrawerModifiers,
List<ResultValue<ValueModifierPropertyAttribute, ValueModifierPropertyDrawer>> valueModifiers,
List<ResultValue<AccessModifierPropertyAttribute, AccessModifierPropertyDrawer>> 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<T>() 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<T>() 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<T>() where T : AccessModifierPropertyAttribute
{
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 : AbstractPropertyAttribute
where T1 : AbstractPropertyDrawer
{
public readonly T0 attribute;
public readonly T1 drawer;
public ResultValue(T0 attribute, T1 drawer)
{
this.attribute = attribute;
this.drawer = drawer;
}
}
}
}