An easy to use Unity 3D library for creating illustrated Interactive Fiction games and more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
7.1 KiB

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<string> 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<string>();
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<string> 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<string>();
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<string>();
var fields = new List<MemberInfo>();
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<Type>();
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);
}
}
}