using System; using System.Linq; using System.Reflection; using Robust.Shared.GameObjects; using Robust.Shared.Reflection; namespace Robust.Shared.ViewVariables; /// /// Represents a ViewVariables path. Allows you to "Get", "Set" or "Invoke" the path. /// [Virtual] public abstract class ViewVariablesPath { /// /// The type that is both returned by the method and used by the method. /// public abstract Type Type { get; } /// /// Gets the value of the path, if possible. /// /// The value of the path, or null. Same type as . public abstract object? Get(); /// /// Sets the value of the path, if possible. /// /// The new value to set the path to. Must be of the same type as . public abstract void Set(object? value); /// /// Invokes the path, if possible. /// /// The parameters that the function takes. /// The object returned by invoking the function, or null. public abstract object? Invoke(object?[]? parameters); /// /// The types of all parameters in the method. /// /// public virtual Type[] InvokeParameterTypes { get; } = Array.Empty(); /// /// The number of optional parameters in the method, starting from the end of the array. /// /// public virtual uint InvokeOptionalParameters { get; } = 0; /// /// The type of the object returned by the method, or if none. /// public virtual Type InvokeReturnType { get; } = typeof(void); #region Static helper methods /// /// Creates a given an object. /// public static ViewVariablesFakePath FromObject(object obj) => new(() => obj, null, null, obj.GetType()); /// /// Creates a given a getter function. /// public static ViewVariablesFakePath FromGetter(Func getter, Type type) => new(getter, null, null, type); /// /// Creates a given a setter function. /// public static ViewVariablesFakePath FromSetter(Action setter, Type type) => new(null, setter, null, type); /// /// Creates a given a function to be invoked. /// public static ViewVariablesFakePath FromInvoker(Func invoker, Type[]? invokeParameterTypes = null, uint invokeOptionalParameters = 0, Type? invokeReturnType = null) => new(null, null, invoker, null, invokeParameterTypes, invokeOptionalParameters, invokeReturnType); /// /// Creates a given a function to be invoked. /// public static ViewVariablesFakePath FromInvoker(Action invoker, Type[]? invokeParameterTypes = null, uint invokeOptionalParameters = 0, Type? invokeReturnType = null) => new(null, null, invoker, null, invokeParameterTypes, invokeOptionalParameters, invokeReturnType); #endregion } internal sealed class ViewVariablesFieldOrPropertyPath : ViewVariablesPath { internal ViewVariablesFieldOrPropertyPath(object? obj, MemberInfo member) { if (member is not (FieldInfo or PropertyInfo)) throw new ArgumentException("Member must be either a field or a property!", nameof(member)); _object = obj; _member = member; ViewVariablesUtility.TryGetViewVariablesAccess(member, out _access); } private readonly object? _object; private readonly MemberInfo _member; private readonly VVAccess? _access; public override Type Type => _member.GetUnderlyingType(); public override object? Get() { if (_access == null) return null; try { return _object != null ? _member.GetValue(_object) : null; } catch (Exception) { return null; } } public override void Set(object? value) { if (_access != VVAccess.ReadWrite) return; if (_object != null) _member.SetValue(_object, value); } public override object? Invoke(object?[]? parameters) => null; } internal sealed class ViewVariablesMethodPath : ViewVariablesPath { internal ViewVariablesMethodPath(object? obj, MethodInfo method) { _object = obj; _method = method; ViewVariablesUtility.TryGetViewVariablesAccess(method, out _access); } private readonly object? _object; private readonly MethodInfo _method; private readonly VVAccess? _access; public override Type Type => typeof(void); public override Type InvokeReturnType => _method.ReturnType; public override object? Get() => null; public override void Set(object? value) { } public override object? Invoke(object?[]? parameters) { if (_access != VVAccess.ReadWrite) return null; return _object != null ? _method.Invoke(_object, parameters) : null; } public override Type[] InvokeParameterTypes => _access == VVAccess.ReadWrite ? _method.GetParameters().Select(info => info.ParameterType).ToArray() : Array.Empty(); public override uint InvokeOptionalParameters => _access == VVAccess.ReadWrite ? (uint) _method.GetParameters().Count(info => info.IsOptional) : 0; } internal sealed class ViewVariablesIndexedPath : ViewVariablesPath { internal ViewVariablesIndexedPath(object? obj, PropertyInfo indexer, object?[] index, VVAccess? parentAccess) { if (indexer.GetIndexParameters().Length == 0) throw new ArgumentException("PropertyInfo is not an indexer!", nameof(indexer)); _object = obj; _indexer = indexer; _index = index; _access = parentAccess; } private readonly object? _object; private readonly PropertyInfo _indexer; private readonly object?[] _index; private readonly VVAccess? _access; public override Type Type => _indexer.GetUnderlyingType(); public override object? Get() { if (_access == null) return null; try { return _object != null ? _indexer.GetValue(_object, _index) : null; } catch (Exception) { return null; } } public override void Set(object? value) { if(_access == VVAccess.ReadWrite && _object != null) _indexer.SetValue(_object, value, _index); } public override object? Invoke(object?[]? parameters) => null; } public sealed class ViewVariablesInstancePath : ViewVariablesPath { public ViewVariablesInstancePath(object? obj) { _object = obj; } private readonly object? _object; public override Type Type => _object?.GetType() ?? typeof(void); public override object? Get() => _object; public override void Set(object? value) { } public override object? Invoke(object?[]? parameters) => null; } public sealed class ViewVariablesComponentPath : ViewVariablesPath { public readonly object? Component; public readonly EntityUid Owner; public override Type Type => Component?.GetType() ?? typeof(void); public ViewVariablesComponentPath(object? component, EntityUid owner) { Component = component; Owner = owner; } public override object? Get() { return Component; } public override void Set(object? value) { } public override object? Invoke(object?[]? parameters) => null; } public sealed class ViewVariablesFakePath : ViewVariablesPath { public ViewVariablesFakePath(Func? getter, Action? setter, Func? invoker = null, Type? type = null, Type[]? invokeParameterTypes = null, uint invokeOptionalParameters = 0, Type? invokeReturnType = null) { _getter = getter; _setter = setter; _invoker = invoker; Type = type ?? typeof(void); InvokeParameterTypes = invokeParameterTypes ?? Array.Empty(); InvokeOptionalParameters = invokeOptionalParameters; InvokeReturnType = invokeReturnType ?? typeof(void); } public ViewVariablesFakePath(Func? getter, Action? setter, Action invoker, Type? type = null, Type[]? invokeParameterTypes = null, uint invokeOptionalParameters = 0, Type? invokeReturnType = null) : this(getter, setter, null, type, invokeParameterTypes, invokeOptionalParameters, invokeReturnType) { _invoker = p => { invoker(p); return null; }; } private readonly Func? _getter; private readonly Action? _setter; private readonly Func? _invoker; public override Type Type { get; } public override Type[] InvokeParameterTypes { get; } public override uint InvokeOptionalParameters { get; } public override Type InvokeReturnType { get; } public override object? Get() { return _getter?.Invoke(); } public override void Set(object? value) { _setter?.Invoke(value); } public override object? Invoke(object?[]? parameters) { return _invoker?.Invoke(parameters); } public ViewVariablesFakePath WithGetter(Func getter, Type? type = null) => new(getter, _setter, _invoker, type ?? Type, InvokeParameterTypes, InvokeOptionalParameters, InvokeReturnType); public ViewVariablesFakePath WithSetter(Action setter, Type? type = null) => new(_getter, setter, _invoker, type ?? Type, InvokeParameterTypes, InvokeOptionalParameters, InvokeReturnType); public ViewVariablesFakePath WithInvoker(Func invoker, Type[]? invokeParameterTypes = null, uint invokeOptionalParameters = 0, Type? invokeReturnType = null) => new(_getter, _setter, invoker, Type, invokeParameterTypes, invokeOptionalParameters, invokeReturnType); }