using UnityEditor; using System.Reflection; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace NaughtyAttributes.Editor { public static class PropertyUtility { public static T GetAttribute(SerializedProperty property) where T : class { T[] attributes = GetAttributes(property); return (attributes.Length > 0) ? attributes[0] : null; } public static T[] GetAttributes(SerializedProperty property) where T : class { FieldInfo fieldInfo = ReflectionUtility.GetField(GetTargetObjectWithProperty(property), property.name); if (fieldInfo == null) { return new T[] { }; } return (T[])fieldInfo.GetCustomAttributes(typeof(T), true); } public static GUIContent GetLabel(SerializedProperty property) { LabelAttribute labelAttribute = GetAttribute(property); string labelText = (labelAttribute == null) ? property.displayName : labelAttribute.Label; GUIContent label = new GUIContent(labelText); return label; } public static void CallOnValueChangedCallbacks(SerializedProperty property) { OnValueChangedAttribute[] onValueChangedAttributes = GetAttributes(property); if (onValueChangedAttributes.Length == 0) { return; } object target = GetTargetObjectWithProperty(property); property.serializedObject.ApplyModifiedProperties(); // We must apply modifications so that the new value is updated in the serialized object foreach (var onValueChangedAttribute in onValueChangedAttributes) { MethodInfo callbackMethod = ReflectionUtility.GetMethod(target, onValueChangedAttribute.CallbackName); if (callbackMethod != null && callbackMethod.ReturnType == typeof(void) && callbackMethod.GetParameters().Length == 0) { callbackMethod.Invoke(target, new object[] { }); } else { string warning = string.Format( "{0} can invoke only methods with 'void' return type and 0 parameters", onValueChangedAttribute.GetType().Name); Debug.LogWarning(warning, property.serializedObject.targetObject); } } } public static bool IsEnabled(SerializedProperty property) { ReadOnlyAttribute readOnlyAttribute = GetAttribute(property); if (readOnlyAttribute != null) { return false; } EnableIfAttributeBase enableIfAttribute = GetAttribute(property); if (enableIfAttribute == null) { return true; } object target = GetTargetObjectWithProperty(property); // deal with enum conditions if (enableIfAttribute.EnumValue != null) { Enum value = GetEnumValue(target, enableIfAttribute.Conditions[0]); if (value != null) { bool matched = value.GetType().GetCustomAttribute() == null ? enableIfAttribute.EnumValue.Equals(value) : value.HasFlag(enableIfAttribute.EnumValue); return matched != enableIfAttribute.Inverted; } string message = enableIfAttribute.GetType().Name + " needs a valid enum field, property or method name to work"; Debug.LogWarning(message, property.serializedObject.targetObject); return false; } // deal with normal conditions List conditionValues = GetConditionValues(target, enableIfAttribute.Conditions); if (conditionValues.Count > 0) { bool enabled = GetConditionsFlag(conditionValues, enableIfAttribute.ConditionOperator, enableIfAttribute.Inverted); return enabled; } else { string message = enableIfAttribute.GetType().Name + " needs a valid boolean condition field, property or method name to work"; Debug.LogWarning(message, property.serializedObject.targetObject); return false; } } public static bool IsVisible(SerializedProperty property) { ShowIfAttributeBase showIfAttribute = GetAttribute(property); if (showIfAttribute == null) { return true; } object target = GetTargetObjectWithProperty(property); // deal with enum conditions if (showIfAttribute.EnumValue != null) { Enum value = GetEnumValue(target, showIfAttribute.Conditions[0]); if (value != null) { bool matched = value.GetType().GetCustomAttribute() == null ? showIfAttribute.EnumValue.Equals(value) : value.HasFlag(showIfAttribute.EnumValue); return matched != showIfAttribute.Inverted; } string message = showIfAttribute.GetType().Name + " needs a valid enum field, property or method name to work"; Debug.LogWarning(message, property.serializedObject.targetObject); return false; } // deal with normal conditions List conditionValues = GetConditionValues(target, showIfAttribute.Conditions); if (conditionValues.Count > 0) { bool enabled = GetConditionsFlag(conditionValues, showIfAttribute.ConditionOperator, showIfAttribute.Inverted); return enabled; } else { string message = showIfAttribute.GetType().Name + " needs a valid boolean condition field, property or method name to work"; Debug.LogWarning(message, property.serializedObject.targetObject); return false; } } /// /// Gets an enum value from reflection. /// /// The target object. /// Name of a field, property, or method that returns an enum. /// Null if can't find an enum value. internal static Enum GetEnumValue(object target, string enumName) { FieldInfo enumField = ReflectionUtility.GetField(target, enumName); if (enumField != null && enumField.FieldType.IsSubclassOf(typeof(Enum))) { return (Enum)enumField.GetValue(target); } PropertyInfo enumProperty = ReflectionUtility.GetProperty(target, enumName); if (enumProperty != null && enumProperty.PropertyType.IsSubclassOf(typeof(Enum))) { return (Enum)enumProperty.GetValue(target); } MethodInfo enumMethod = ReflectionUtility.GetMethod(target, enumName); if (enumMethod != null && enumMethod.ReturnType.IsSubclassOf(typeof(Enum))) { return (Enum)enumMethod.Invoke(target, null); } return null; } internal static List GetConditionValues(object target, string[] conditions) { List conditionValues = new List(); foreach (var condition in conditions) { FieldInfo conditionField = ReflectionUtility.GetField(target, condition); if (conditionField != null && conditionField.FieldType == typeof(bool)) { conditionValues.Add((bool)conditionField.GetValue(target)); } PropertyInfo conditionProperty = ReflectionUtility.GetProperty(target, condition); if (conditionProperty != null && conditionProperty.PropertyType == typeof(bool)) { conditionValues.Add((bool)conditionProperty.GetValue(target)); } MethodInfo conditionMethod = ReflectionUtility.GetMethod(target, condition); if (conditionMethod != null && conditionMethod.ReturnType == typeof(bool) && conditionMethod.GetParameters().Length == 0) { conditionValues.Add((bool)conditionMethod.Invoke(target, null)); } } return conditionValues; } internal static bool GetConditionsFlag(List conditionValues, EConditionOperator conditionOperator, bool invert) { bool flag; if (conditionOperator == EConditionOperator.And) { flag = true; foreach (var value in conditionValues) { flag = flag && value; } } else { flag = false; foreach (var value in conditionValues) { flag = flag || value; } } if (invert) { flag = !flag; } return flag; } public static Type GetPropertyType(SerializedProperty property) { object obj = GetTargetObjectOfProperty(property); Type objType = obj.GetType(); return objType; } /// /// Gets the object the property represents. /// /// /// public static object GetTargetObjectOfProperty(SerializedProperty property) { if (property == null) { return null; } string path = property.propertyPath.Replace(".Array.data[", "["); object obj = property.serializedObject.targetObject; string[] elements = path.Split('.'); foreach (var element in elements) { if (element.Contains("[")) { string elementName = element.Substring(0, element.IndexOf("[")); int index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", "")); obj = GetValue_Imp(obj, elementName, index); } else { obj = GetValue_Imp(obj, element); } } return obj; } /// /// Gets the object that the property is a member of /// /// /// public static object GetTargetObjectWithProperty(SerializedProperty property) { string path = property.propertyPath.Replace(".Array.data[", "["); object obj = property.serializedObject.targetObject; string[] elements = path.Split('.'); for (int i = 0; i < elements.Length - 1; i++) { string element = elements[i]; if (element.Contains("[")) { string elementName = element.Substring(0, element.IndexOf("[")); int index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", "")); obj = GetValue_Imp(obj, elementName, index); } else { obj = GetValue_Imp(obj, element); } } return obj; } private static object GetValue_Imp(object source, string name) { if (source == null) { return null; } Type type = source.GetType(); while (type != null) { FieldInfo field = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (field != null) { return field.GetValue(source); } PropertyInfo property = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); if (property != null) { return property.GetValue(source, null); } type = type.BaseType; } return null; } private static object GetValue_Imp(object source, string name, int index) { IEnumerable enumerable = GetValue_Imp(source, name) as IEnumerable; if (enumerable == null) { return null; } IEnumerator enumerator = enumerable.GetEnumerator(); for (int i = 0; i <= index; i++) { if (!enumerator.MoveNext()) { return null; } } return enumerator.Current; } } }