// -------------------------------------------------------------------------------------------------------------------- // // HiddenMonk // http://answers.unity3d.com/users/496850/hiddenmonk.html // // Johannes Deml // send@johannesdeml.com // // -------------------------------------------------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEditor; using System.Reflection; using System.Text.RegularExpressions; namespace TheraBytes.BetterUi.Editor { /// /// Extension class for SerializedProperties /// See also: http://answers.unity3d.com/questions/627090/convert-serializedproperty-to-custom-class.html /// public static class SerializedPropertyExtensions { /// /// Get the object the serialized property holds by using reflection /// /// The object type that the property contains /// /// Returns the object type T if it is the type the property actually contains public static T GetValue(this SerializedProperty property) { return GetNestedObject(property.propertyPath, GetSerializedPropertyRoot(property)); } /// /// Set the value of a field of the property with the type T /// /// The type of the field that is set /// The serialized property that should be set /// The new value for the specified property /// Returns if the operation was successful or failed public static bool SetValue(this SerializedProperty property, T value) { object obj = GetSerializedPropertyRoot(property); //Iterate to parent object of the value, necessary if it is a nested object string[] fieldStructure = property.propertyPath.Split('.'); for (int i = 0; i < fieldStructure.Length - 1; i++) { obj = GetFieldOrPropertyValue(fieldStructure[i], obj); } string fieldName = fieldStructure.Last(); return SetFieldOrPropertyValue(fieldName, obj, value); } /// /// Get the container object of a serialized property (Component or ScriptableObject) /// /// The property that is part of the object /// The root of the property public static UnityEngine.Object GetSerializedPropertyRoot(SerializedProperty property) { return property.serializedObject.targetObject; } /// /// Iterates through objects to handle objects that are nested in the root object /// /// The type of the nested object /// Path to the object through other properties e.g. PlayerInformation.Health /// The root object from which this path leads to the property /// Include base classes and interfaces as well /// Returns the nested object casted to the type T public static T GetNestedObject(string path, object obj, bool includeAllBases = true) { // replace "Array.data[x]" just with "x" Regex regex = new Regex(@"([a-zA-Z\.]+.)?Array.data\[(\d+)\]"); path = regex.Replace(path, "$1$2"); foreach (string part in path.Split('.')) { obj = GetFieldOrPropertyValue(part, obj, includeAllBases); } return (T)obj; } public static T GetFieldOrPropertyValue(string fieldName, object obj, bool includeAllBases = true, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) { int idx; if (int.TryParse(fieldName, out idx)) { var method = obj.GetType().GetMethod("get_Item", bindings); if (method != null) return (T)method.Invoke(obj, new object[] { idx }); } FieldInfo field = obj.GetType().GetField(fieldName, bindings); if (field != null) return (T)field.GetValue(obj); PropertyInfo property = obj.GetType().GetProperty(fieldName, bindings); if (property != null) return (T)property.GetValue(obj, null); if (includeAllBases) { foreach (Type type in 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(T); } public static bool SetFieldOrPropertyValue(string fieldName, object obj, object value, bool includeAllBases = false, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) { FieldInfo field = obj.GetType().GetField(fieldName, bindings); if (field != null) { field.SetValue(obj, value); return true; } PropertyInfo property = obj.GetType().GetProperty(fieldName, bindings); if (property != null) { property.SetValue(obj, value, null); return true; } if (includeAllBases) { foreach (Type type in 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; } public static IEnumerable GetBaseClassesAndInterfaces(this Type type, bool includeSelf = false) { List allTypes = new List(); if (includeSelf) allTypes.Add(type); if (type.BaseType == typeof(object)) { allTypes.AddRange(type.GetInterfaces()); } else { allTypes.AddRange( Enumerable .Repeat(type.BaseType, 1) .Concat(type.GetInterfaces()) .Concat(type.BaseType.GetBaseClassesAndInterfaces()) .Distinct()); } return allTypes; } } }