Retriever.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Reflection;
  6. using AttributeGroup = System.Collections.Generic.Dictionary<
  7. System.Type,
  8. com.bbbirder.DirectRetrieveAttribute[]
  9. >;
  10. //using TypeSet = System.Collections.Generic.HashSet<System.Type>;
  11. namespace com.bbbirder
  12. {
  13. public static class Retriever
  14. {
  15. static BindingFlags bindingFlags = 0
  16. | BindingFlags.Instance
  17. | BindingFlags.Public
  18. | BindingFlags.NonPublic
  19. | BindingFlags.Static
  20. | BindingFlags.DeclaredOnly
  21. ;
  22. static WeakHolder<AttributeGroup> m_attrLut;
  23. static WeakHolder<AttributeGroup> attrLut => m_attrLut ??= new(() =>
  24. {
  25. // var al = AppDomain.CurrentDomain.GetAssemblies()
  26. // .Where(a => a.IsDefined(typeof(GeneratedDirectRetrieveAttribute),false))
  27. // .SelectMany(a => a.GetCustomAttributes<GeneratedDirectRetrieveAttribute>())
  28. // .Select(a => a.type)
  29. // .Distinct()
  30. // .ToArray();
  31. return AppDomain.CurrentDomain.GetAssemblies()
  32. .Where(a => a.IsDefined(typeof(GeneratedDirectRetrieveAttribute)))
  33. .SelectMany(a => GetAllAttributes<DirectRetrieveAttribute>(a))
  34. .GroupBy(a => a.GetType() ?? typeof(object), a => a)
  35. .ToDictionary(e => e.Key, e => e.ToArray());
  36. });
  37. static WeakHolder<Type[]> m_typeSet;
  38. static WeakHolder<Type[]> typeSet => m_typeSet ??= new(() =>
  39. {
  40. return AppDomain.CurrentDomain.GetAssemblies()
  41. .Where(a => a.IsDefined(typeof(GeneratedDirectRetrieveAttribute), false))
  42. .SelectMany(a => a.GetCustomAttributes<GeneratedDirectRetrieveAttribute>())
  43. .Select(a => a.type)
  44. .Distinct()
  45. .ToArray();
  46. });
  47. /// <summary>
  48. /// Retrieve attributes of type T in all loaded assemblies
  49. /// </summary>
  50. /// <typeparam name="T"></typeparam>
  51. /// <returns></returns>
  52. public static T[] GetAllAttributes<T>() where T : DirectRetrieveAttribute
  53. {
  54. return GetAllAttributes(typeof(T)).OfType<T>().ToArray();
  55. }
  56. /// <summary>
  57. /// Retrieve attributes of target type in all loaded assemblies
  58. /// </summary>
  59. /// <param name="attributeType"></param>
  60. /// <returns></returns>
  61. public static DirectRetrieveAttribute[] GetAllAttributes(Type attributeType)
  62. {
  63. #if DEBUG
  64. CheckAttribute(attributeType);
  65. #endif
  66. return attrLut.Value.ToArray()
  67. .Where(a => attributeType.IsAssignableFrom(a.Key))
  68. .SelectMany(a => a.Value)
  69. .ToArray()
  70. ;
  71. }
  72. /// <summary>
  73. /// Retrieve attributes of target type in a specific assembly
  74. /// </summary>
  75. /// <param name="attributeType"></param>
  76. /// <param name="assembly"></param>
  77. /// <returns></returns>
  78. public static DirectRetrieveAttribute[] GetAllAttributes(Type attributeType, Assembly assembly)
  79. {
  80. #if DEBUG
  81. CheckAttribute(attributeType);
  82. #endif
  83. return assembly.GetCustomAttributes<GeneratedDirectRetrieveAttribute>()
  84. .SelectMany(a =>
  85. {
  86. var targetType = a.type;
  87. IEnumerable<DirectRetrieveAttribute> result;
  88. if (a.HasMemberName)
  89. {
  90. result = targetType.GetMember(a.memberName, bindingFlags).SelectMany(
  91. member => member.GetCustomAttributes(attributeType, false).Select(
  92. ca => SetAttributeValue(ca as DirectRetrieveAttribute, member)
  93. )
  94. );
  95. }
  96. else
  97. {
  98. result = targetType.GetCustomAttributes(attributeType, false).Select(
  99. ca => SetAttributeValue(ca as DirectRetrieveAttribute, targetType)
  100. );
  101. }
  102. if (result.Count() == 0)
  103. {
  104. throw new("cannot find attribute but the assembly metadata preset, please check your script-strip setting");
  105. }
  106. return result;
  107. })
  108. .ToArray();
  109. }
  110. /// <summary>
  111. /// Retrieve attributes of type T in a specific assembly
  112. /// </summary>
  113. /// <param name="assembly"></param>
  114. /// <typeparam name="T"></typeparam>
  115. /// <returns></returns>
  116. public static T[] GetAllAttributes<T>(Assembly assembly) where T : DirectRetrieveAttribute
  117. {
  118. var attrType = typeof(T);
  119. return assembly.GetCustomAttributes<GeneratedDirectRetrieveAttribute>()
  120. .SelectMany(a =>
  121. {
  122. var targetType = a.type;
  123. if (a.HasMemberName)
  124. return targetType.GetMember(a.memberName, bindingFlags).SelectMany(
  125. member => member.GetCustomAttributes<T>(false).Select(
  126. ca => SetAttributeValue(ca, member)
  127. )
  128. );
  129. else
  130. return targetType.GetCustomAttributes<T>(false).Select(
  131. ca => SetAttributeValue(ca, targetType)
  132. );
  133. })
  134. .ToArray();
  135. }
  136. /// <summary>
  137. /// Retrieve all subclasses that inherit from the target type
  138. /// </summary>
  139. /// <param name="baseType"></param>
  140. /// <returns></returns>
  141. public static Type[] GetAllSubtypes(Type baseType)
  142. {
  143. #if DEBUG
  144. CheckBasetype(baseType);
  145. #endif
  146. if (baseType.IsGenericTypeDefinition)
  147. {
  148. return typeSet.Value
  149. .Where(a => a != baseType)
  150. .Where(a => GetTypesTowardsBase(a).Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == baseType)
  151. || a.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == baseType))
  152. .ToArray()
  153. ;
  154. }
  155. else
  156. {
  157. return typeSet.Value
  158. .Where(a => a != baseType && baseType.IsAssignableFrom(a))
  159. .ToArray()
  160. ;
  161. }
  162. static IEnumerable<Type> GetTypesTowardsBase(Type type)
  163. {
  164. while (type != null)
  165. {
  166. yield return type;
  167. type = type.BaseType;
  168. }
  169. }
  170. }
  171. /// <summary>
  172. /// Retrieve all subclasses that inherit from the target type
  173. /// </summary>
  174. /// <typeparam name="T"></typeparam>
  175. /// <returns></returns>
  176. public static Type[] GetAllSubtypes<T>()
  177. {
  178. return GetAllSubtypes(typeof(T));
  179. }
  180. /// <summary>
  181. /// Retrieve all subclasses that inherit from the target type in a specific assembly
  182. /// </summary>
  183. /// <param name="assembly"></param>
  184. /// <typeparam name="T"></typeparam>
  185. /// <returns></returns>
  186. public static Type[] GetAllSubtypes<T>(Assembly assembly)
  187. {
  188. return GetAllSubtypes(typeof(T), assembly);
  189. }
  190. /// <summary>
  191. /// Retrieve all subclasses that inherit from the target type in a specific assembly
  192. /// </summary>
  193. /// <param name="assembly"></param>
  194. /// <param name="baseType"></param>
  195. /// <returns></returns>
  196. public static Type[] GetAllSubtypes(Type baseType, Assembly assembly)
  197. {
  198. #if DEBUG
  199. CheckBasetype(baseType);
  200. #endif
  201. return assembly.GetCustomAttributes<GeneratedDirectRetrieveAttribute>()
  202. .Where(a => IsBaseType(a.type, baseType))
  203. .Select(a => a.type)
  204. .Distinct()
  205. .ToArray()
  206. ;
  207. }
  208. static void CheckAttribute(Type attributeType)
  209. {
  210. if (!typeof(DirectRetrieveAttribute).IsAssignableFrom(attributeType))
  211. {
  212. throw new($"type {attributeType} is not a DirectRetrieveAttribute");
  213. }
  214. }
  215. // [Conditional("DIRECT_RETRIEVE_ATTRIBUTE_STRICT")]
  216. static void CheckBasetype(Type baseType)
  217. {
  218. if (!IsTypeRetrievable(baseType))
  219. {
  220. throw new($"type {baseType} is not retrievable, which should inherit from IDirectRetrieve");
  221. }
  222. }
  223. public static bool IsTypeRetrievable(Type type)
  224. {
  225. return IsBaseType(type, typeof(IDirectRetrieve));
  226. }
  227. static bool IsBaseType(Type subType, Type baseType)
  228. {
  229. if (subType == baseType)
  230. return false;
  231. if (baseType.IsInterface)
  232. return baseType.IsAssignableFrom(subType);
  233. else
  234. return subType.IsSubclassOf(baseType);
  235. }
  236. static T SetAttributeValue<T>(T attr, MemberInfo targetInfo) where T : DirectRetrieveAttribute
  237. {
  238. attr.targetInfo = targetInfo;
  239. attr.OnReceiveTarget();
  240. return attr;
  241. }
  242. }
  243. }