2
0
Эх сурвалжийг харах

Added a simple way to retrieve properties from managed objects, using a path search

BearishSun 9 жил өмнө
parent
commit
80a6eb2f9f

+ 31 - 0
Source/MBansheeEngine/Serialization/SerializableArray.cs

@@ -93,6 +93,37 @@ namespace BansheeEngine
                 return 0;
         }
 
+        /// <summary>
+        /// Uses the provided path elements to find an array element at the specified index, and returns a property
+        /// to the element, or to a child property of that element.
+        /// </summary>
+        /// <param name="pathElements">Path elements representing field names and keys to look for.</param>
+        /// <param name="elementIdx">Index in the <paramref name="pathElements"/> array to start the search at.</param>
+        /// <returns>Property representing the final path element, or null if not found (array index is out of range, or 
+        ///          property with that path doesn't exist).</returns>
+        internal SerializableProperty FindProperty(PropertyPathElement[] pathElements, int elementIdx)
+        {
+            int arrayIdx;
+            if (string.IsNullOrEmpty(pathElements[elementIdx].key))
+                arrayIdx = 0;
+            else
+            {
+                if (!int.TryParse(pathElements[elementIdx].key, out arrayIdx))
+                    return null;
+                else
+                {
+                    if (arrayIdx < 0 || arrayIdx >= GetLength())
+                        return null;
+                }
+            }
+
+            SerializableProperty property = GetProperty(arrayIdx);
+            if (elementIdx == (pathElements.Length - 1))
+                return property;
+
+            return property.FindProperty(pathElements, elementIdx + 1);
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern SerializableProperty Internal_CreateProperty(IntPtr nativeInstance);
     }

+ 52 - 0
Source/MBansheeEngine/Serialization/SerializableDictionary.cs

@@ -134,6 +134,58 @@ namespace BansheeEngine
                 return 0;
         }
 
+        /// <summary>
+        /// Uses the provided path elements to find an dictionary element with the specified key, and returns a property
+        /// to the element, or to a child property of that element.
+        /// </summary>
+        /// <param name="pathElements">Path elements representing field names and keys to look for.</param>
+        /// <param name="elementIdx">Index in the <paramref name="pathElements"/> array to start the search at.</param>
+        /// <returns>Property representing the final path element, or null if not found (key or property with that path
+        ///          doesn't exist).</returns>
+        internal SerializableProperty FindProperty(PropertyPathElement[] pathElements, int elementIdx)
+        {
+            if (pathElements[elementIdx].key == null)
+                return null;
+
+            object key = null;
+            switch (KeyPropertyType)
+            {
+                case SerializableProperty.FieldType.String:
+                    key = pathElements[elementIdx].key;
+                    break;
+                case SerializableProperty.FieldType.Bool:
+                    bool boolKey;
+                    if (bool.TryParse(pathElements[elementIdx].key, out boolKey))
+                        key = boolKey;
+
+                    break;
+                case SerializableProperty.FieldType.Int:
+                    int intKey;
+                    if (int.TryParse(pathElements[elementIdx].key, out intKey))
+                        key = intKey;
+
+                    break;
+                case SerializableProperty.FieldType.Float:
+                    float floatKey;
+                    if (float.TryParse(pathElements[elementIdx].key, out floatKey))
+                        key = floatKey;
+
+                    break;
+            }
+
+            if (key == null)
+                return null;
+
+            SerializableProperty property = GetProperty(key).Value;
+            if (elementIdx == (pathElements.Length - 1))
+                return property;
+
+            if(property != null)
+                return property.FindProperty(pathElements, elementIdx + 1);
+
+            return null;
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern SerializableProperty Internal_CreateKeyProperty(IntPtr nativeInstance);
 

+ 2 - 2
Source/MBansheeEngine/Serialization/SerializableField.cs

@@ -10,9 +10,9 @@ namespace BansheeEngine
      */
 
     /// <summary>
-    /// Flags as defined in native code in BsManagedSerializableObjectInfo.h
+    /// Flags that are used to define properties of a single field in a managed object.
     /// </summary>
-    enum SerializableFieldAttributes : byte
+    internal enum SerializableFieldAttributes // Note: Must match C++ enum ScriptFieldFlags
     {
         Serializable = 0x01,
         Inspectable = 0x02,

+ 31 - 0
Source/MBansheeEngine/Serialization/SerializableList.cs

@@ -94,6 +94,37 @@ namespace BansheeEngine
                 return 0;
         }
 
+        /// <summary>
+        /// Uses the provided path elements to find an list element at the specified index, and returns a property
+        /// to the element, or to a child property of that element.
+        /// </summary>
+        /// <param name="pathElements">Path elements representing field names and keys to look for.</param>
+        /// <param name="elementIdx">Index in the <paramref name="pathElements"/> array to start the search at.</param>
+        /// <returns>Property representing the final path element, or null if not found (list index is out of range, or 
+        ///          property with that path doesn't exist).</returns>
+        internal SerializableProperty FindProperty(PropertyPathElement[] pathElements, int elementIdx)
+        {
+            int arrayIdx;
+            if (string.IsNullOrEmpty(pathElements[elementIdx].key))
+                arrayIdx = 0;
+            else
+            {
+                if (!int.TryParse(pathElements[elementIdx].key, out arrayIdx))
+                    return null;
+                else
+                {
+                    if (arrayIdx < 0 || arrayIdx >= GetLength())
+                        return null;
+                }
+            }
+
+            SerializableProperty property = GetProperty(arrayIdx);
+            if (elementIdx == (pathElements.Length - 1))
+                return property;
+
+            return property.FindProperty(pathElements, elementIdx + 1);
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern SerializableProperty Internal_CreateProperty(IntPtr nativeInstance);
     }

+ 103 - 0
Source/MBansheeEngine/Serialization/SerializableObject.cs

@@ -2,6 +2,7 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 using System;
 using System.Runtime.CompilerServices;
+using System.Text;
 
 namespace BansheeEngine
 {
@@ -78,9 +79,111 @@ namespace BansheeEngine
                 return parentObject;
         }
 
+        /// <summary>
+        /// Searches the object, and all child objects for a field or entry with the specified name.
+        /// </summary>
+        /// <param name="path">Slash separated path with field name(s) to search for. If a field contains an array, list or
+        ///                    a dictionary append its name with a "[x]" where x is the element index in the array/list, or
+        ///                    a key name (surrounded by "") in case of a dictionary. Only primitive dictionary keys are
+        ///                    supported.
+        /// 
+        ///                    Example path: subObject/myDictionary["someElement"]/theArray[4]/fieldToGet
+        ///                    </param>
+        /// <returns>Property you can use for reading or modifying the property, or null if not found.</returns>
+        public SerializableProperty FindProperty(string path)
+        {
+            if (path == null)
+                return null;
+
+            string[] pathEntries = path.Split('/');
+            PropertyPathElement[] pathElements = new PropertyPathElement[pathEntries.Length];
+            for (int i = 0; i < pathEntries.Length; i++)
+            {
+                string entry = pathEntries[i];
+                if (string.IsNullOrEmpty(entry))
+                    return null;
+
+                pathElements[i] = new PropertyPathElement();
+                if (entry[entry.Length - 1] == ']')
+                {
+                    bool foundKey = false;
+                    int j = entry.Length - 2;
+                    for (; j >= 0; j--)
+                    {
+                        if (entry[j] == '[')
+                        {
+                            foundKey = true;
+                            break;
+                        }
+                    }
+
+                    if (foundKey)
+                    {
+                        pathElements[i].name = entry.Substring(0, j);
+                        pathElements[i].key = entry.Substring(j + 1, (entry.Length - 1) - (j + 1));
+                    }
+                    else
+                        pathElements[i].name = entry;
+                }
+                else
+                {
+                    pathElements[i].name = entry;
+                }
+            }
+
+            return FindProperty(pathElements, 0);
+        }
+
+        /// <summary>
+        /// Searches the object hierarchy using the provided path elements. <see cref="FindProperty(string)"/>
+        /// </summary>
+        /// <param name="pathElements">Path elements representing field names and keys to look for.</param>
+        /// <param name="elementIdx">Index in the <paramref name="pathElements"/> array to start the search at.</param>
+        /// <returns>Property representing the final path element, or null if not found.</returns>
+        internal SerializableProperty FindProperty(PropertyPathElement[] pathElements, int elementIdx)
+        {
+            SerializableField field = FindField(pathElements[0].name);
+            if (field != null)
+            {
+                SerializableProperty property = field.GetProperty();
+                if (elementIdx == (pathElements.Length - 1))
+                    return property;
+
+                return property.FindProperty(pathElements, elementIdx + 1);
+            }
+
+            return null;
+        }
+
+        /// <summary>
+        /// Searches the object for a field with the specified name.
+        /// </summary>
+        /// <param name="name">Name of the field.</param>
+        /// <returns>Object representing the field, if found, null otherwise.</returns>
+        public SerializableField FindField(string name)
+        {
+            foreach (var field in _fields)
+            {
+                if (field.Name == name)
+                    return field;
+            }
+
+            return null;
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(SerializableObject instance, Type objectType);
     }
 
+    /// <summary>
+    /// Contains a single element of a path to a field or array/list/dictionary entry, as used for 
+    /// <see cref="SerializableObject"/>.
+    /// </summary>
+    internal struct PropertyPathElement
+    {
+        public string name;
+        public string key;
+    }
+
     /** @} */
 }

+ 41 - 0
Source/MBansheeEngine/Serialization/SerializableProperty.cs

@@ -242,6 +242,47 @@ namespace BansheeEngine
             return Internal_CreateManagedDictionaryInstance(mCachedPtr);
         }
 
+        /// <summary>
+        /// Helper method used for finding child properties of the specified property, using a property path.
+        /// <see cref="SerializableObject.FindProperty(string)"/>.
+        /// </summary>
+        /// <param name="pathElements">Path elements representing field names and keys to look for.</param>
+        /// <param name="elementIdx">Index in the <paramref name="pathElements"/> array to start the search at.</param>
+        /// <returns>Property representing the final path element, or null if not found (array index is out of range, or 
+        ///          property with that path doesn't exist).</returns>
+        internal SerializableProperty FindProperty(PropertyPathElement[] pathElements, int elementIdx)
+        {
+            switch (type)
+            {
+                case FieldType.Object:
+                    {
+                        SerializableObject childObject = GetObject();
+
+                        return childObject.FindProperty(pathElements, elementIdx);
+                    }
+                case FieldType.Array:
+                    {
+                        SerializableArray childArray = GetArray();
+
+                        return childArray.FindProperty(pathElements, elementIdx);
+                    }
+                case FieldType.List:
+                    {
+                        SerializableList childList = GetList();
+
+                        return childList.FindProperty(pathElements, elementIdx);
+                    }
+                case FieldType.Dictionary:
+                    {
+                        SerializableDictionary childDictionary = GetDictionary();
+
+                        return childDictionary.FindProperty(pathElements, elementIdx);
+                    }
+            }
+
+            return null;
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(SerializableProperty instance, Type type);