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(); 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(m_Path.Split('.')); Type type = GetBaseObject().GetType(); if (type != typeof(GameObject)) propsQueue.Dequeue(); PropertyInfo propertyTemp; FieldInfo fieldTemp; var list = new List(); 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 } } }