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.

209 lines
6.3 KiB

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEngine;
namespace UnityTest
{
public class MemberResolver
{
private object m_CallingObjectRef;
private MemberInfo[] m_Callstack;
private readonly GameObject m_GameObject;
private readonly string m_Path;
public MemberResolver(GameObject gameObject, string path)
{
path = path.Trim();
ValidatePath(path);
m_GameObject = gameObject;
m_Path = path.Trim();
}
public object GetValue(bool useCache)
{
if (useCache && m_CallingObjectRef != null)
{
object val = m_CallingObjectRef;
for (int i = 0; i < m_Callstack.Length; i++)
val = GetValueFromMember(val, m_Callstack[i]);
return val;
}
object result = GetBaseObject();
var fullCallStack = GetCallstack();
m_CallingObjectRef = result;
var tempCallstack = new List<MemberInfo>();
for (int i = 0; i < fullCallStack.Length; i++)
{
var member = fullCallStack[i];
result = GetValueFromMember(result, member);
tempCallstack.Add(member);
if (result == null) return null;
var type = result.GetType();
//String is not a value type but we don't want to cache it
if (!IsValueType(type) && type != typeof(System.String))
{
tempCallstack.Clear();
m_CallingObjectRef = result;
}
}
m_Callstack = tempCallstack.ToArray();
return result;
}
public Type GetMemberType()
{
var callstack = GetCallstack();
if (callstack.Length == 0) return GetBaseObject().GetType();
var member = callstack[callstack.Length - 1];
if (member is FieldInfo)
return (member as FieldInfo).FieldType;
if (member is MethodInfo)
return (member as MethodInfo).ReturnType;
return null;
}
#region Static wrappers
public static bool TryGetMemberType(GameObject gameObject, string path, out Type value)
{
try
{
var mr = new MemberResolver(gameObject, path);
value = mr.GetMemberType();
return true;
}
catch (InvalidPathException)
{
value = null;
return false;
}
}
public static bool TryGetValue(GameObject gameObject, string path, out object value)
{
try
{
var mr = new MemberResolver(gameObject, path);
value = mr.GetValue(false);
return true;
}
catch (InvalidPathException)
{
value = null;
return false;
}
}
#endregion
private object GetValueFromMember(object obj, MemberInfo memberInfo)
{
if (memberInfo is FieldInfo)
return (memberInfo as FieldInfo).GetValue(obj);
if (memberInfo is MethodInfo)
return (memberInfo as MethodInfo).Invoke(obj, null);
throw new InvalidPathException(memberInfo.Name);
}
private object GetBaseObject()
{
if (string.IsNullOrEmpty(m_Path)) return m_GameObject;
var firstElement = m_Path.Split('.')[0];
var comp = m_GameObject.GetComponent(firstElement);
if (comp != null)
return comp;
return m_GameObject;
}
private MemberInfo[] GetCallstack()
{
if (m_Path == "") return new MemberInfo[0];
var propsQueue = new Queue<string>(m_Path.Split('.'));
Type type = GetBaseObject().GetType();
if (type != typeof(GameObject))
propsQueue.Dequeue();
PropertyInfo propertyTemp;
FieldInfo fieldTemp;
var list = new List<MemberInfo>();
while (propsQueue.Count != 0)
{
var nameToFind = propsQueue.Dequeue();
fieldTemp = GetField(type, nameToFind);
if (fieldTemp != null)
{
type = fieldTemp.FieldType;
list.Add(fieldTemp);
continue;
}
propertyTemp = GetProperty(type, nameToFind);
if (propertyTemp != null)
{
type = propertyTemp.PropertyType;
var getMethod = GetGetMethod(propertyTemp);
list.Add(getMethod);
continue;
}
throw new InvalidPathException(nameToFind);
}
return list.ToArray();
}
private void ValidatePath(string path)
{
bool invalid = false;
if (path.StartsWith(".") || path.EndsWith("."))
invalid = true;
if (path.IndexOf("..") >= 0)
invalid = true;
if (Regex.IsMatch(path, @"\s"))
invalid = true;
if (invalid)
throw new InvalidPathException(path);
}
private static bool IsValueType(Type type)
{
#if !UNITY_METRO
return type.IsValueType;
#else
return false;
#endif
}
private static FieldInfo GetField(Type type, string fieldName)
{
#if !UNITY_METRO
return type.GetField(fieldName);
#else
return null;
#endif
}
private static PropertyInfo GetProperty(Type type, string propertyName)
{
#if !UNITY_METRO
return type.GetProperty(propertyName);
#else
return null;
#endif
}
private static MethodInfo GetGetMethod(PropertyInfo propertyInfo)
{
#if !UNITY_METRO
return propertyInfo.GetGetMethod();
#else
return null;
#endif
}
}
}