using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using UnityEngine; namespace UnityTest { [Serializable] public class PropertyResolver { public string[] ExcludedFieldNames { get; set; } public Type[] ExcludedTypes { get; set; } public Type[] AllowedTypes { get; set; } public PropertyResolver() { ExcludedFieldNames = new string[] { }; ExcludedTypes = new Type[] { }; AllowedTypes = new Type[] { }; } public IList GetFieldsAndPropertiesUnderPath(GameObject go, string propertPath) { propertPath = propertPath.Trim(); if (!PropertyPathIsValid(propertPath)) { throw new ArgumentException("Incorrect property path: " + propertPath); } var idx = propertPath.LastIndexOf('.'); if (idx < 0) { var components = GetFieldsAndPropertiesFromGameObject(go, 2, null); return components; } var propertyToSearch = propertPath; Type type; if (MemberResolver.TryGetMemberType(go, propertyToSearch, out type)) { idx = propertPath.Length - 1; } else { propertyToSearch = propertPath.Substring(0, idx); if (!MemberResolver.TryGetMemberType(go, propertyToSearch, out type)) { var components = GetFieldsAndPropertiesFromGameObject(go, 2, null); return components.Where(s => s.StartsWith(propertPath.Substring(idx + 1))).ToArray(); } } var resultList = new List(); var path = ""; if (propertyToSearch.EndsWith(".")) propertyToSearch = propertyToSearch.Substring(0, propertyToSearch.Length - 1); foreach (var c in propertyToSearch) { if (c == '.') resultList.Add(path); path += c; } resultList.Add(path); foreach (var prop in type.GetProperties().Where(info => info.GetIndexParameters().Length == 0)) { if (prop.Name.StartsWith(propertPath.Substring(idx + 1))) resultList.Add(propertyToSearch + "." + prop.Name); } foreach (var prop in type.GetFields()) { if (prop.Name.StartsWith(propertPath.Substring(idx + 1))) resultList.Add(propertyToSearch + "." + prop.Name); } return resultList.ToArray(); } internal bool PropertyPathIsValid(string propertPath) { if (propertPath.StartsWith(".")) return false; if (propertPath.IndexOf("..") >= 0) return false; if (Regex.IsMatch(propertPath, @"\s")) return false; return true; } public IList GetFieldsAndPropertiesFromGameObject(GameObject gameObject, int depthOfSearch, string extendPath) { if (depthOfSearch < 1) throw new ArgumentOutOfRangeException("depthOfSearch has to be greater than 0"); var goVals = GetPropertiesAndFieldsFromType(typeof(GameObject), depthOfSearch - 1).Select(s => "gameObject." + s); var result = new List(); if (AllowedTypes == null || !AllowedTypes.Any() || AllowedTypes.Contains(typeof(GameObject))) result.Add("gameObject"); result.AddRange(goVals); foreach (var componentType in GetAllComponents(gameObject)) { if (AllowedTypes == null || !AllowedTypes.Any() || AllowedTypes.Any(t => t.IsAssignableFrom(componentType))) result.Add(componentType.Name); if (depthOfSearch > 1) { var vals = GetPropertiesAndFieldsFromType(componentType, depthOfSearch - 1); var valsFullName = vals.Select(s => componentType.Name + "." + s); result.AddRange(valsFullName); } } if (!string.IsNullOrEmpty(extendPath)) { var memberResolver = new MemberResolver(gameObject, extendPath); var pathType = memberResolver.GetMemberType(); var vals = GetPropertiesAndFieldsFromType(pathType, depthOfSearch - 1); var valsFullName = vals.Select(s => extendPath + "." + s); result.AddRange(valsFullName); } return result; } private string[] GetPropertiesAndFieldsFromType(Type type, int level) { level--; var result = new List(); var fields = new List(); fields.AddRange(type.GetFields().Where(f => !Attribute.IsDefined(f, typeof(ObsoleteAttribute))).ToArray()); fields.AddRange(type.GetProperties().Where(info => info.GetIndexParameters().Length == 0 && !Attribute.IsDefined(info, typeof(ObsoleteAttribute))).ToArray()); foreach (var member in fields) { var memberType = GetMemberFieldType(member); var memberTypeName = memberType.Name; if (AllowedTypes == null || !AllowedTypes.Any() || (AllowedTypes.Any(t => t.IsAssignableFrom(memberType)) && !ExcludedFieldNames.Contains(memberTypeName))) { result.Add(member.Name); } if (level > 0 && IsTypeOrNameNotExcluded(memberType, memberTypeName)) { var vals = GetPropertiesAndFieldsFromType(memberType, level); var valsFullName = vals.Select(s => member.Name + "." + s); result.AddRange(valsFullName); } } return result.ToArray(); } private Type GetMemberFieldType(MemberInfo info) { if (info.MemberType == MemberTypes.Property) return (info as PropertyInfo).PropertyType; if (info.MemberType == MemberTypes.Field) return (info as FieldInfo).FieldType; throw new Exception("Only properties and fields are allowed"); } internal Type[] GetAllComponents(GameObject gameObject) { var result = new List(); var components = gameObject.GetComponents(typeof(Component)); foreach (var component in components) { var componentType = component.GetType(); if (IsTypeOrNameNotExcluded(componentType, null)) { result.Add(componentType); } } return result.ToArray(); } private bool IsTypeOrNameNotExcluded(Type memberType, string memberTypeName) { return !ExcludedTypes.Contains(memberType) && !ExcludedFieldNames.Contains(memberTypeName); } } }