#if UNITY_EDITOR
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using Object = UnityEngine.Object;
namespace Jovian.InspectorTools.EditorTools {
public static class MySerializedProperty {
#region Collections Handling
///
/// Get array of property childs, if parent property is array
///
public static SerializedProperty[] AsArray(this SerializedProperty property) {
List items = new List();
for(int i = 0; i < property.arraySize; i++)
items.Add(property.GetArrayElementAtIndex(i));
return items.ToArray();
}
///
/// Get array of property childs casted to specific type
///
public static T[] AsArray(this SerializedProperty property) {
var propertiesArray = property.AsArray();
return propertiesArray.Select(s => s.objectReferenceValue).OfType().ToArray();
}
///
/// Get array of property childs, if parent property is array
///
public static IEnumerable AsIEnumerable(this SerializedProperty property) {
for(int i = 0; i < property.arraySize; i++)
yield return property.GetArrayElementAtIndex(i);
}
///
/// Replace array contents of SerializedProperty with another array
///
public static void ReplaceArray(this SerializedProperty property, Object[] newElements) {
property.arraySize = 0;
property.serializedObject.ApplyModifiedProperties();
property.arraySize = newElements.Length;
for(var i = 0; i < newElements.Length; i++) {
property.GetArrayElementAtIndex(i).objectReferenceValue = newElements[i];
}
property.serializedObject.ApplyModifiedProperties();
}
///
/// If property is array, insert new element at the end and get it as a property
///
public static SerializedProperty NewElement(this SerializedProperty property) {
int newElementIndex = property.arraySize;
property.InsertArrayElementAtIndex(newElementIndex);
return property.GetArrayElementAtIndex(newElementIndex);
}
#endregion
///
/// Property is float, int, vector or int vector
///
public static bool IsNumerical(this SerializedProperty property) {
var propertyType = property.propertyType;
switch(propertyType) {
case SerializedPropertyType.Float:
case SerializedPropertyType.Integer:
case SerializedPropertyType.Vector2:
case SerializedPropertyType.Vector3:
case SerializedPropertyType.Vector4:
case SerializedPropertyType.Vector2Int:
case SerializedPropertyType.Vector3Int:
return true;
default: return false;
}
}
///
/// Get string representation of serialized property
///
public static string AsStringValue(this SerializedProperty property) {
switch(property.propertyType) {
case SerializedPropertyType.String:
return property.stringValue;
case SerializedPropertyType.Character:
case SerializedPropertyType.Integer:
if(property.type == "char") return Convert.ToChar(property.intValue).ToString();
return property.intValue.ToString();
case SerializedPropertyType.ObjectReference:
return property.objectReferenceValue != null ? property.objectReferenceValue.ToString() : "null";
case SerializedPropertyType.Boolean:
return property.boolValue.ToString();
case SerializedPropertyType.Enum:
return property.GetValue().ToString();
default:
return string.Empty;
}
}
///
/// Property path for collection without ".Array.data[x]" in it
///
public static string GetFixedPropertyPath(this SerializedProperty property) => property.propertyPath.Replace(".Array.data[", "[");
///
/// Get FieldInfo out of SerializedProperty
///
public static FieldInfo GetFieldInfo(this SerializedProperty property) {
var targetObject = property.serializedObject.targetObject;
var targetType = targetObject.GetType();
return targetType.GetField(property.propertyPath);
}
///
/// Get raw object value out of the SerializedProperty
///
public static object GetValue(this SerializedProperty property) {
if(property == null) return null;
object obj = property.serializedObject.targetObject;
var elements = property.GetFixedPropertyPath().Split('.');
foreach(var element in elements) {
if(element.Contains("[")) {
var elementName = element.Substring(0, element.IndexOf("[", StringComparison.Ordinal));
var index = Convert.ToInt32(element.Substring(element.IndexOf("[", StringComparison.Ordinal)).Replace("[", "").Replace("]", ""));
obj = GetValueByArrayFieldName(obj, elementName, index);
}
else obj = GetValueByFieldName(obj, element);
}
return obj;
object GetValueByArrayFieldName(object source, string name, int index) {
if(!(GetValueByFieldName(source, name) is IEnumerable enumerable)) return null;
var enumerator = enumerable.GetEnumerator();
for(var i = 0; i <= index; i++) if(!enumerator.MoveNext()) return null;
return enumerator.Current;
}
// Search "source" object for a field with "name" and get it's value
object GetValueByFieldName(object source, string name) {
if(source == null) return null;
var type = source.GetType();
while(type != null) {
var fieldInfo = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
if(fieldInfo != null) return fieldInfo.GetValue(source);
var propertyInfo = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if(propertyInfo != null) return propertyInfo.GetValue(source, null);
type = type.BaseType;
}
return null;
}
}
///
/// Set raw object value to the SerializedProperty
///
public static void SetValue(this SerializedProperty property, object value) {
GetFieldInfo(property).SetValue(property.serializedObject.targetObject, value);
}
///
/// Is specific attribute defined on SerializedProperty
///
///
///
///
public static bool IsAttributeDefined(this SerializedProperty property) where T : Attribute {
var fieldInfo = property.GetFieldInfo();
if(fieldInfo == null) return false;
return Attribute.IsDefined(fieldInfo, typeof(T));
}
#region SerializedProperty Get Parent
// Found here http://answers.unity.com/answers/425602/view.html
// Update here https://gist.github.com/AdrienVR/1548a145c039d2fddf030ebc22f915de to support inherited private members.
///
/// Get parent object of SerializedProperty
///
public static object GetParent(this SerializedProperty prop) {
var path = prop.propertyPath.Replace(".Array.data[", "[");
object obj = prop.serializedObject.targetObject;
var elements = path.Split('.');
foreach(var element in elements.Take(elements.Length - 1)) {
if(element.Contains("[")) {
var elementName = element.Substring(0, element.IndexOf("[", StringComparison.Ordinal));
var index = Convert.ToInt32(element.Substring(element.IndexOf("[", StringComparison.Ordinal)).Replace("[", "").Replace("]", ""));
obj = GetValueAt(obj, elementName, index);
}
else {
obj = GetValue(obj, element);
}
}
return obj;
}
private static object GetValue(object source, string name) {
if(source == null)
return null;
foreach(var type in GetHierarchyTypes(source.GetType())) {
var f = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
if(f != null)
return f.GetValue(source);
var p = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if(p != null)
return p.GetValue(source, null);
}
return null;
}
private static IEnumerable GetHierarchyTypes(Type sourceType) {
yield return sourceType;
while(sourceType.BaseType != null) {
yield return sourceType.BaseType;
sourceType = sourceType.BaseType;
}
}
private static object GetValueAt(object source, string name, int index) {
var enumerable = GetValue(source, name) as IEnumerable;
if(enumerable == null) return null;
var enm = enumerable.GetEnumerator();
while(index-- >= 0)
enm.MoveNext();
return enm.Current;
}
#endregion
}
}
#endif