using System; using System.Collections.Generic; using System.Linq; #if SIMPLSHARP using Crestron.SimplSharp.Reflection; #else using System.Reflection; #endif using ICD.Common.Properties; using ICD.Common.Utils; using ICD.Common.Utils.Collections; using ICD.Common.Utils.Extensions; namespace ICD.Common.Attributes.Rpc { /// /// Represents a method that can be called by the server via RPC. /// [PublicAPI] [MeansImplicitUse] [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = true, AllowMultiple = false)] public sealed class RpcAttribute : AbstractIcdAttribute { private static readonly Dictionary>> s_MethodCache; private static readonly Dictionary>> s_PropertyCache; private static readonly SafeCriticalSection s_MethodCacheSection; private static readonly SafeCriticalSection s_PropertyCacheSection; private readonly string m_Key; #region Constructors /// /// Constructor. /// static RpcAttribute() { s_MethodCache = new Dictionary>>(); s_PropertyCache = new Dictionary>>(); s_MethodCacheSection = new SafeCriticalSection(); s_PropertyCacheSection = new SafeCriticalSection(); } /// /// Constructor. /// /// public RpcAttribute(string key) { m_Key = key; } #endregion #region Methods /// /// Gets the method on the client with the given key, matching the parameter types. /// /// /// /// /// [CanBeNull] public static MethodInfo GetMethod(object client, string key, IEnumerable parameters) { return GetMethods(client, key).FirstOrDefault(m => ReflectionUtils.MatchesMethodParameters(m, parameters)); } /// /// Gets the property on the client with the given key, matching the parameter type. /// /// /// /// /// [CanBeNull] public static PropertyInfo GetProperty(object client, string key, object parameter) { return GetProperties(client, key).FirstOrDefault(p => ReflectionUtils.MatchesPropertyParameter(p, parameter)); } #endregion #region Private Methods /// /// Returns the methods on the client with the given key. /// /// /// /// private static IEnumerable GetMethods(object client, string key) { s_MethodCacheSection.Enter(); try { Type clientType = client.GetType(); // Cache the methods for the key so subsequent calls are faster. if (!s_MethodCache.ContainsKey(clientType)) s_MethodCache[clientType] = new Dictionary>(); if (!s_MethodCache[clientType].ContainsKey(key)) { s_MethodCache[clientType][key] = clientType #if SIMPLSHARP .GetCType() #else .GetTypeInfo() #endif .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(m => m.GetCustomAttributes(true) .Any(a => a.m_Key == key)) .ToHashSet(); } return s_MethodCache[clientType][key]; } finally { s_MethodCacheSection.Leave(); } } /// /// Returns the properties on the client with the given key. /// /// /// /// private static IEnumerable GetProperties(object client, string key) { s_PropertyCacheSection.Enter(); try { Type clientType = client.GetType(); // Cache the properties for the key so subsequent calls are faster. if (!s_PropertyCache.ContainsKey(clientType)) s_PropertyCache[clientType] = new Dictionary>(); if (!s_PropertyCache[clientType].ContainsKey(key)) { s_PropertyCache[clientType][key] = clientType #if SIMPLSHARP .GetCType() #else .GetTypeInfo() #endif .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(p => p.CanWrite && p.GetCustomAttributes(true) .Any(a => a.m_Key == key)) .ToHashSet(); } return s_PropertyCache[clientType][key]; } finally { s_PropertyCacheSection.Leave(); } } #endregion } }