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.Services; using ICD.Common.Services.Logging; using ICD.Common.Utils.Collections; using ICD.Common.Utils.Extensions; namespace ICD.Common.Utils { /// /// Utility methods for browsing code attributes. /// Provides some basic caching for faster subsequent searches. /// public static class AttributeUtils { // Avoid caching the same assembly multiple times. private static readonly IcdHashSet s_CachedAssemblies; private static readonly IcdHashSet s_CachedTypes; private static readonly Dictionary s_AttributeToMethodCache; private static readonly Dictionary> s_TypeToAttributesCache; /// /// Constructor. /// static AttributeUtils() { s_CachedAssemblies = new IcdHashSet(); s_CachedTypes = new IcdHashSet(); s_AttributeToMethodCache = new Dictionary(); s_TypeToAttributesCache = new Dictionary>(); } #region Caching /// /// Pre-emptively caches the given assemblies for lookup. /// /// public static void CacheAssemblies(IEnumerable assemblies) { if (assemblies == null) throw new ArgumentNullException("assemblies"); foreach (Assembly assembly in assemblies) CacheAssembly(assembly); } /// /// Pre-emptively caches the given assembly for lookup. /// /// public static void CacheAssembly(Assembly assembly) { if (assembly == null) throw new ArgumentNullException("assembly"); if (s_CachedAssemblies.Contains(assembly)) return; s_CachedAssemblies.Add(assembly); #if SIMPLSHARP CType[] types = new CType[0]; #else Type[] types = new Type[0]; #endif try { types = assembly.GetTypes(); } catch (TypeLoadException e) { ServiceProvider.TryGetService() .AddEntry(eSeverity.Error, e, "Failed to cache assembly {0} - {1}", assembly.GetName().Name, e.Message); } foreach (var type in types) CacheType(type); } /// /// Pre-emptively caches the given type for lookup. /// /// #if SIMPLSHARP public static void CacheType(CType type) #else public static void CacheType(Type type) #endif { if (type == null) throw new ArgumentNullException("type"); if (s_CachedTypes.Contains(type)) return; s_CachedTypes.Add(type); MethodInfo[] methods; try { #if SIMPLSHARP s_TypeToAttributesCache[type] = new IcdHashSet(type.GetCustomAttributes(false)); methods = type.GetMethods(); #else s_TypeToAttributesCache[type] = new IcdHashSet(type.GetTypeInfo().GetCustomAttributes(false)); methods = type.GetTypeInfo().GetMethods(); #endif } // GetMethods for Open Generic Types is not supported. catch (NotSupportedException) { return; } // Not sure why this happens :/ catch (InvalidProgramException) { return; } foreach (MethodInfo method in methods) CacheMethod(method); } /// /// Caches the method. /// /// private static void CacheMethod(MethodInfo method) { if (method == null) throw new ArgumentNullException("method"); foreach (Attribute attribute in method.GetCustomAttributes(false)) s_AttributeToMethodCache[attribute] = method; } #endregion #region Lookup /// /// Gets the first attribute on the given class type matching the generic type. /// /// /// /// [CanBeNull] public static T GetClassAttribute(Type type) where T : Attribute { if (type == null) throw new ArgumentNullException("type"); return GetClassAttributes(type).FirstOrDefault(); } /// /// Gets the attributes on the given class type matching the generic type. /// /// /// /// public static IEnumerable GetClassAttributes(Type type) where T : Attribute { if (type == null) throw new ArgumentNullException("type"); return GetClassAttributes(type).OfType(); } /// /// Gets the attributes on the given class. /// /// /// public static IEnumerable GetClassAttributes(Type type) { if (type == null) throw new ArgumentNullException("type"); CacheType(type); return s_TypeToAttributesCache.ContainsKey(type) ? s_TypeToAttributesCache[type].ToArray() : Enumerable.Empty(); } /// /// Gets all of the cached method attributes of the given type. /// /// /// public static IEnumerable GetMethodAttributes() where T : Attribute { return s_AttributeToMethodCache.Select(p => p.Key) .OfType() .ToArray(); } /// /// Gets the cached method for the given attribute. /// /// /// public static MethodInfo GetMethod(Attribute attribute) { if (attribute == null) throw new ArgumentNullException("attribute"); return s_AttributeToMethodCache[attribute]; } #endregion } }