// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor
{
/// [Editor-Only] A cache to optimize repeated attribute access.
///
/// If implements for ,
/// its method will be called automatically.
///
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AttributeCache_1
public static class AttributeCache
where TAttribute : class
{
/************************************************************************************************************************/
private static readonly Dictionary
MemberToAttribute = new();
/************************************************************************************************************************/
///
/// Returns the attribute on the specified `member` (if there is one).
///
public static TAttribute GetAttribute(MemberInfo member)
{
if (!MemberToAttribute.TryGetValue(member, out var attribute))
{
try
{
attribute = member.GetAttribute();
if (attribute is IInitializable initializable)
initializable.Initialize(member);
}
catch (Exception exception)
{
Debug.LogException(exception);
attribute = null;
}
MemberToAttribute.Add(member, attribute);
}
return attribute;
}
/************************************************************************************************************************/
///
/// Returns the attribute (if any)
/// on the specified `type` or its (recursively).
///
public static TAttribute GetAttribute(Type type)
{
if (type == null)
return null;
var attribute = GetAttribute((MemberInfo)type);
if (attribute != null)
return attribute;
return MemberToAttribute[type] = GetAttribute(type.BaseType);
}
/************************************************************************************************************************/
///
/// Returns the attribute on the specified `field` or its
/// or .
///
public static TAttribute FindAttribute(FieldInfo field)
{
var attribute = GetAttribute(field);
if (attribute != null)
return attribute;
attribute = GetAttribute(field.FieldType);
if (attribute != null)
return MemberToAttribute[field] = attribute;
attribute = GetAttribute(field.DeclaringType);
if (attribute != null)
return MemberToAttribute[field] = attribute;
return attribute;
}
/************************************************************************************************************************/
/// [Editor-Only]
/// Returns the attribute on the underlying field
/// of the `property` or its or
/// or any of the parent properties
/// or the type of the .
///
public static TAttribute FindAttribute(SerializedProperty property)
{
var accessor = property.GetAccessor();
while (accessor != null)
{
var field = accessor.GetField(property);
var attribute = GetAttribute(field);
if (attribute != null)
return attribute;
var value = accessor.GetValue(property);
if (value != null)
{
attribute = GetAttribute(value.GetType());
if (attribute != null)
return attribute;
}
accessor = accessor.Parent;
}
// If none of the fields of types they are declared in have names, try the actual type of the target.
{
var attribute = GetAttribute(property.serializedObject.targetObject.GetType());
if (attribute != null)
return attribute;
}
return null;
}
/************************************************************************************************************************/
}
}
#endif