Files
Hasebe/Library/PackageCache/com.annulusgames.alchemy@1402075f473d/Editor/Internal/SerializedPropertyExtensions.cs
T
2026-09-21 12:15:57 +03:00

319 lines
12 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
namespace Alchemy.Editor
{
internal static class SerializedPropertyExtensions
{
public static bool TryGetAttribute<TAttribute>(this SerializedProperty property, out TAttribute result) where TAttribute : Attribute
{
return TryGetAttribute(property, false, out result);
}
public static bool TryGetAttribute<TAttribute>(this SerializedProperty property, bool inherit, out TAttribute result) where TAttribute : Attribute
{
TAttribute attribute = GetAttribute<TAttribute>(property, inherit);
result = attribute;
return attribute != null;
}
public static TAttribute GetAttribute<TAttribute>(this SerializedProperty property, bool inherit = false) where TAttribute : Attribute
{
if (property == null) throw new ArgumentNullException(nameof(property));
return property.GetFieldInfo().GetCustomAttribute<TAttribute>(inherit);
}
public static IEnumerable<TAttribute> GetAttributes<TAttribute>(this SerializedProperty property, bool inherit) where TAttribute : Attribute
{
if (property == null) throw new ArgumentNullException(nameof(property));
return property.GetFieldInfo().GetCustomAttributes<TAttribute>(inherit);
}
public static float GetHeight(this SerializedProperty property)
{
return EditorGUI.GetPropertyHeight(property, true);
}
public static float GetHeight(this SerializedProperty property, bool includeChildren)
{
return EditorGUI.GetPropertyHeight(property, includeChildren);
}
public static float GetHeight(this SerializedProperty property, GUIContent label, bool includeChildren)
{
return EditorGUI.GetPropertyHeight(property, label, includeChildren);
}
public static T GetValue<T>(this SerializedProperty property)
{
return GetNestedObject<T>(property.propertyPath, GetSerializedPropertyRootObject(property), true);
}
public static bool SetValue<T>(this SerializedProperty property, T value)
{
object obj = GetSerializedPropertyRootObject(property);
var fieldStructure = property.propertyPath.Split('.');
for (int i = 0; i < fieldStructure.Length - 1; i++)
{
obj = GetFieldOrPropertyValue<object>(fieldStructure[i], obj);
}
var fieldName = fieldStructure.Last();
return SetFieldOrPropertyValue(fieldName, obj, value);
}
static readonly Regex IndexerRegex = new(@"[^0-9]+");
public static FieldInfo GetFieldInfo(this SerializedProperty property)
{
object target = property.serializedObject.targetObject;
var splits = property.propertyPath.Split('.');
var fieldInfo = ReflectionHelper.GetField(target.GetType(), splits[0], includingBaseNonPublic: true);
target = fieldInfo.GetValue(target);
for (var i = 1; i < splits.Length; i++)
{
if (target == null) return null;
if (splits[i] == "Array")
{
i++;
if (i >= splits.Length) continue;
var index = int.Parse(IndexerRegex.Replace(splits[i], string.Empty));
var targetType = target.GetType();
if (targetType.IsArray) target = (target as Array).GetValue(index);
else target = (target as IList)[index];
i++;
if (i >= splits.Length) continue;
targetType = target.GetType();
fieldInfo = ReflectionHelper.GetField(targetType, splits[i], includingBaseNonPublic: true);
}
else
{
var targetType = target.GetType();
fieldInfo = ReflectionHelper.GetField(targetType, splits[i], includingBaseNonPublic: true);
}
target = fieldInfo?.GetValue(target);
}
return fieldInfo;
}
public static Type GetPropertyType(this SerializedProperty property, bool isCollectionType = false)
{
var fieldInfo = property.GetFieldInfo();
if (isCollectionType && property.propertyType != SerializedPropertyType.String)
return fieldInfo.FieldType.IsArray ?
fieldInfo.FieldType.GetElementType() :
fieldInfo.FieldType.GetGenericArguments()[0];
return fieldInfo.FieldType;
}
public static object SetManagedReferenceType(this SerializedProperty property, Type type)
{
var obj = (type != null) ? Activator.CreateInstance(type) : null;
property.managedReferenceValue = obj;
return obj;
}
public static string GetManagedReferenceFieldTypeName(this SerializedProperty property)
{
var typeName = property.managedReferenceFieldTypename;
var splitIndex = typeName.IndexOf(' ');
return typeName[(splitIndex + 1)..];
}
public static Type GetManagedReferenceFieldType(this SerializedProperty property)
{
var typeName = property.managedReferenceFieldTypename;
var splitIndex = typeName.IndexOf(' ');
var assembly = Assembly.Load(typeName[..splitIndex]);
return assembly.GetType(typeName[(splitIndex + 1)..]);
}
static UnityEngine.Object GetSerializedPropertyRootObject(SerializedProperty property)
{
return property.serializedObject.targetObject;
}
static T GetNestedObject<T>(string path, object obj, bool includeAllBases = false)
{
var parts = path.Split('.');
for (int i = 0; i < parts.Length; i++)
{
string part = parts[i];
if (part == "Array")
{
var regex = new Regex(@"[^0-9]");
var countText = regex.Replace(parts[i + 1], "");
if (!int.TryParse(countText, out var index))
{
index = -1;
}
obj = GetElementAtOrDefault(obj, index);
i++;
}
else
{
obj = GetFieldOrPropertyValue<object>(part, obj, includeAllBases);
}
}
return (T)obj;
}
static object GetElementAtOrDefault(object arrayOrListObj, int index)
{
if (arrayOrListObj is IEnumerable<object> referenceEnumerable)
{
return referenceEnumerable.ElementAtOrDefault(index);
}
if (arrayOrListObj is IList valueList)
{
object result;
if (index < 0 || index >= valueList.Count)
{
Type listType = valueList.GetType();
Type elementType = listType.IsArray ? listType.GetElementType() : listType.GetGenericArguments()[0];
result = Activator.CreateInstance(elementType);
}
else
{
result = valueList[index];
}
return result;
}
throw new ArgumentException($"Can't parse {arrayOrListObj.GetType()} as Array or List");
}
public static object GetParentObject(this SerializedProperty property)
{
if (property == null) return null;
var path = property.propertyPath.Replace(".Array.data[", "[");
object obj = property.serializedObject.targetObject;
var elements = path.Split('.');
foreach (var element in elements)
{
if (element.Contains("["))
{
var elementName = element[..element.IndexOf("[")];
var index = Convert.ToInt32(element[element.IndexOf("[")..].Replace("[", "").Replace("]", ""));
obj = ReflectionHelper.GetValue(obj, elementName, index);
}
else
{
obj = ReflectionHelper.GetValue(obj, element);
}
}
return obj;
}
public static object GetDeclaredObject(this SerializedProperty property)
{
if (property == null) return null;
var path = property.propertyPath.Replace(".Array.data[", "[");
object obj = property.serializedObject.targetObject;
var elements = path.Split('.');
for (int i = 0; i < elements.Length - 1; i++)
{
var element = elements[i];
if (element.Contains("["))
{
var elementName = element[..element.IndexOf("[")];
var index = Convert.ToInt32(element[element.IndexOf("[")..].Replace("[", "").Replace("]", ""));
obj = ReflectionHelper.GetValue(obj, elementName, index);
}
else
{
obj = ReflectionHelper.GetValue(obj, element);
}
}
return obj;
}
static T GetFieldOrPropertyValue<T>(string fieldName, object obj, bool includeAllBases = false, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
{
var field = obj.GetType().GetField(fieldName, bindings);
if (field != null) return (T)field.GetValue(obj);
var property = obj.GetType().GetProperty(fieldName, bindings);
if (property != null) return (T)property.GetValue(obj, null);
if (includeAllBases)
{
foreach (var type in TypeHelper.GetBaseClassesAndInterfaces(obj.GetType()))
{
field = type.GetField(fieldName, bindings);
if (field != null) return (T)field.GetValue(obj);
property = type.GetProperty(fieldName, bindings);
if (property != null) return (T)property.GetValue(obj, null);
}
}
return default;
}
static bool SetFieldOrPropertyValue(string fieldName, object obj, object value, bool includeAllBases = false, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
{
var field = obj.GetType().GetField(fieldName, bindings);
if (field != null)
{
field.SetValue(obj, value);
return true;
}
var property = obj.GetType().GetProperty(fieldName, bindings);
if (property != null)
{
property.SetValue(obj, value, null);
return true;
}
if (includeAllBases)
{
foreach (var type in TypeHelper.GetBaseClassesAndInterfaces(obj.GetType()))
{
field = type.GetField(fieldName, bindings);
if (field != null)
{
field.SetValue(obj, value);
return true;
}
property = type.GetProperty(fieldName, bindings);
if (property != null)
{
property.SetValue(obj, value, null);
return true;
}
}
}
return false;
}
}
}