SMAPI/ModLoader/Harmony/Tools/Traverse.cs

298 lines
7.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Harmony
{
public class Traverse<T>
{
private Traverse traverse;
Traverse()
{
}
public Traverse(Traverse traverse)
{
this.traverse = traverse;
}
public T Value
{
get => traverse.GetValue<T>();
set => traverse.SetValue(value);
}
}
public class Traverse
{
static AccessCache Cache;
Type _type;
object _root;
MemberInfo _info;
MethodBase _method;
object[] _params;
[MethodImpl(MethodImplOptions.Synchronized)]
static Traverse()
{
if (Cache == null)
Cache = new AccessCache();
}
public static Traverse Create(Type type)
{
return new Traverse(type);
}
public static Traverse Create<T>()
{
return Create(typeof(T));
}
public static Traverse Create(object root)
{
return new Traverse(root);
}
public static Traverse CreateWithType(string name)
{
return new Traverse(AccessTools.TypeByName(name));
}
Traverse()
{
}
public Traverse(Type type)
{
_type = type;
}
public Traverse(object root)
{
_root = root;
_type = root?.GetType();
}
Traverse(object root, MemberInfo info, object[] index)
{
_root = root;
_type = root?.GetType();
_info = info;
_params = index;
}
Traverse(object root, MethodInfo method, object[] parameter)
{
_root = root;
_type = method.ReturnType;
_method = method;
_params = parameter;
}
public object GetValue()
{
if (_info is FieldInfo)
return ((FieldInfo)_info).GetValue(_root);
if (_info is PropertyInfo)
return ((PropertyInfo)_info).GetValue(_root, AccessTools.all, null, _params, CultureInfo.CurrentCulture);
if (_method != null)
return _method.Invoke(_root, _params);
if (_root == null && _type != null) return _type;
return _root;
}
public T GetValue<T>()
{
var value = GetValue();
if (value == null) return default(T);
return (T)value;
}
public object GetValue(params object[] arguments)
{
if (_method == null)
throw new Exception("cannot get method value without method");
return _method.Invoke(_root, arguments);
}
public T GetValue<T>(params object[] arguments)
{
if (_method == null)
throw new Exception("cannot get method value without method");
return (T)_method.Invoke(_root, arguments);
}
public Traverse SetValue(object value)
{
if (_info is FieldInfo)
((FieldInfo)_info).SetValue(_root, value, AccessTools.all, null, CultureInfo.CurrentCulture);
if (_info is PropertyInfo)
((PropertyInfo)_info).SetValue(_root, value, AccessTools.all, null, _params, CultureInfo.CurrentCulture);
if (_method != null)
throw new Exception("cannot set value of method " + _method.FullDescription());
return this;
}
public Type GetValueType()
{
if (_info is FieldInfo)
return ((FieldInfo)_info).FieldType;
if (_info is PropertyInfo)
return ((PropertyInfo)_info).PropertyType;
return null;
}
Traverse Resolve()
{
if (_root == null && _type != null) return this;
return new Traverse(GetValue());
}
public Traverse Type(string name)
{
if (name == null) throw new ArgumentNullException("name cannot be null");
if (_type == null) return new Traverse();
var type = AccessTools.Inner(_type, name);
if (type == null) return new Traverse();
return new Traverse(type);
}
public Traverse Field(string name)
{
if (name == null) throw new ArgumentNullException("name cannot be null");
var resolved = Resolve();
if (resolved._type == null) return new Traverse();
var info = Cache.GetFieldInfo(resolved._type, name);
if (info == null) return new Traverse();
if (info.IsStatic == false && resolved._root == null) return new Traverse();
return new Traverse(resolved._root, info, null);
}
public Traverse<T> Field<T>(string name)
{
return new Traverse<T>(Field(name));
}
public List<string> Fields()
{
var resolved = Resolve();
return AccessTools.GetFieldNames(resolved._type);
}
public Traverse Property(string name, object[] index = null)
{
if (name == null) throw new ArgumentNullException("name cannot be null");
var resolved = Resolve();
if (resolved._root == null || resolved._type == null) return new Traverse();
var info = Cache.GetPropertyInfo(resolved._type, name);
if (info == null) return new Traverse();
return new Traverse(resolved._root, info, index);
}
public Traverse<T> Property<T>(string name, object[] index = null)
{
return new Traverse<T>(Property(name, index));
}
public List<string> Properties()
{
var resolved = Resolve();
return AccessTools.GetPropertyNames(resolved._type);
}
public Traverse Method(string name, params object[] arguments)
{
if (name == null) throw new ArgumentNullException("name cannot be null");
var resolved = Resolve();
if (resolved._type == null) return new Traverse();
var types = AccessTools.GetTypes(arguments);
var method = Cache.GetMethodInfo(resolved._type, name, types);
if (method == null) return new Traverse();
return new Traverse(resolved._root, (MethodInfo)method, arguments);
}
public Traverse Method(string name, Type[] paramTypes, object[] arguments = null)
{
if (name == null) throw new ArgumentNullException("name cannot be null");
var resolved = Resolve();
if (resolved._type == null) return new Traverse();
var method = Cache.GetMethodInfo(resolved._type, name, paramTypes);
if (method == null) return new Traverse();
return new Traverse(resolved._root, (MethodInfo)method, arguments);
}
public List<string> Methods()
{
var resolved = Resolve();
return AccessTools.GetMethodNames(resolved._type);
}
public bool FieldExists()
{
return _info != null;
}
public bool MethodExists()
{
return _method != null;
}
public bool TypeExists()
{
return _type != null;
}
public static void IterateFields(object source, Action<Traverse> action)
{
var sourceTrv = Create(source);
AccessTools.GetFieldNames(source).ForEach(f => action(sourceTrv.Field(f)));
}
public static void IterateFields(object source, object target, Action<Traverse, Traverse> action)
{
var sourceTrv = Create(source);
var targetTrv = Create(target);
AccessTools.GetFieldNames(source).ForEach(f => action(sourceTrv.Field(f), targetTrv.Field(f)));
}
public static void IterateFields(object source, object target, Action<string, Traverse, Traverse> action)
{
var sourceTrv = Create(source);
var targetTrv = Create(target);
AccessTools.GetFieldNames(source).ForEach(f => action(f, sourceTrv.Field(f), targetTrv.Field(f)));
}
public static void IterateProperties(object source, Action<Traverse> action)
{
var sourceTrv = Create(source);
AccessTools.GetPropertyNames(source).ForEach(f => action(sourceTrv.Property(f)));
}
public static void IterateProperties(object source, object target, Action<Traverse, Traverse> action)
{
var sourceTrv = Create(source);
var targetTrv = Create(target);
AccessTools.GetPropertyNames(source).ForEach(f => action(sourceTrv.Property(f), targetTrv.Property(f)));
}
public static void IterateProperties(object source, object target, Action<string, Traverse, Traverse> action)
{
var sourceTrv = Create(source);
var targetTrv = Create(target);
AccessTools.GetPropertyNames(source).ForEach(f => action(f, sourceTrv.Property(f), targetTrv.Property(f)));
}
public static Action<Traverse, Traverse> CopyFields = (from, to) => { to.SetValue(from.GetValue()); };
public override string ToString()
{
var value = _method ?? GetValue();
return value?.ToString();
}
}
}