PageRenderTime 988ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/Styx.MF/AutoDiscovery/AutoDiscovery.cs

https://github.com/Vanaheimr/Styx
C# | 355 lines | 195 code | 107 blank | 53 comment | 28 complexity | 99a7936e14676f8cd13c86484a2c8e8e MD5 | raw file
  1. /*
  2. * Copyright (c) 2010-2012 Achim 'ahzf' Friedland <achim@graph-database.org>
  3. * This file is part of Illias Commons <http://www.github.com/ahzf/Illias>
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. #region Usings
  18. using System;
  19. using System.IO;
  20. using System.Linq;
  21. using System.Reflection;
  22. using System.Collections.Generic;
  23. using System.Collections.Concurrent;
  24. using System.Threading.Tasks;
  25. #endregion
  26. namespace de.ahzf.Illias.Commons
  27. {
  28. /// <summary>
  29. /// A factory which uses reflection to generate a apropriate
  30. /// implementation of T for you.
  31. /// </summary>
  32. public class AutoDiscovery<TClass> : IEnumerable<TClass>
  33. where TClass : class
  34. {
  35. #region Data
  36. private readonly ConcurrentDictionary<String, Type> _TypeLookup;
  37. private readonly ConcurrentDictionary<String, TClass> _InstanceLookup;
  38. #endregion
  39. #region Properties
  40. #region SearchingFor
  41. /// <summary>
  42. /// Returns the Name of the interface T.
  43. /// </summary>
  44. public String SearchingFor
  45. {
  46. get
  47. {
  48. return typeof(TClass).Name;
  49. }
  50. }
  51. #endregion
  52. #region RegisteredNames
  53. /// <summary>
  54. /// Returns an enumeration of the names of all registered types of T.
  55. /// </summary>
  56. public IEnumerable<String> RegisteredNames
  57. {
  58. get
  59. {
  60. return _TypeLookup.Keys;
  61. }
  62. }
  63. #endregion
  64. #region RegisteredTypes
  65. /// <summary>
  66. /// Returns an enumeration of activated instances of all registered types of T.
  67. /// </summary>
  68. public IEnumerable<TClass> RegisteredTypes
  69. {
  70. get
  71. {
  72. try
  73. {
  74. return from _StringTypePair
  75. in _TypeLookup
  76. select (TClass) Activator.CreateInstance(_StringTypePair.Value);
  77. }
  78. catch (Exception e)
  79. {
  80. throw new AutoDiscoveryException("Could not create instance! " + e);
  81. }
  82. }
  83. }
  84. #endregion
  85. #region Count
  86. /// <summary>
  87. /// Returns the number of registered implementations of the interface T.
  88. /// </summary>
  89. public UInt64 Count
  90. {
  91. get
  92. {
  93. return (UInt64) _TypeLookup.LongCount();
  94. }
  95. }
  96. #endregion
  97. #endregion
  98. #region Constructor(s)
  99. #region AutoDiscovery()
  100. /// <summary>
  101. /// Create a new AutoDiscovery instance and start the discovery.
  102. /// </summary>
  103. public AutoDiscovery()
  104. : this(true)
  105. { }
  106. #endregion
  107. #region AutoDiscovery(Autostart, IdentificatorFunc = null)
  108. /// <summary>
  109. /// Create a new AutoDiscovery instance. An automatic discovery
  110. /// can be avoided.
  111. /// </summary>
  112. /// <param name="Autostart">Automatically start the reflection process.</param>
  113. /// <param name="IdentificatorFunc">A transformation delegate to provide an unique identification for every matching class.</param>
  114. public AutoDiscovery(Boolean Autostart, Func<TClass, String> IdentificatorFunc = null)
  115. {
  116. _TypeLookup = new ConcurrentDictionary<String, Type>();
  117. _InstanceLookup = new ConcurrentDictionary<String, TClass>();
  118. if (Autostart)
  119. FindAndRegister(IdentificatorFunc: IdentificatorFunc);
  120. }
  121. #endregion
  122. #endregion
  123. #region FindAndRegister(ClearTypeDictionary = true, Paths = null, FileExtensions = null, IdentificatorFunc = null)
  124. /// <summary>
  125. /// Searches all matching files at the given paths for classes implementing the interface &lt;T&gt;.
  126. /// </summary>
  127. /// <param name="ClearTypeDictionary">Clears the TypeDictionary before adding new implementations.</param>
  128. /// <param name="Paths">An enumeration of paths to search for implementations.</param>
  129. /// <param name="FileExtensions">A enumeration of file extensions for filtering.</param>
  130. /// <param name="IdentificatorFunc">A transformation delegate to provide an unique identification for every matching class.</param>
  131. public void FindAndRegister(Boolean ClearTypeDictionary = true, IEnumerable<String> Paths = null, IEnumerable<String> FileExtensions = null, Func<TClass, String> IdentificatorFunc = null)
  132. {
  133. #region Get a list of interesting files
  134. var _ConcurrentBag = new ConcurrentBag<String>();
  135. if (Paths == null)
  136. Paths = new List<String> { "." };
  137. if (FileExtensions == null)
  138. FileExtensions = new List<String> { ".dll", ".exe" };
  139. foreach (var _Path in Paths)
  140. {
  141. Parallel.ForEach(Directory.GetFiles(_Path), _ActualFile =>
  142. {
  143. var _FileInfo = new FileInfo(_ActualFile);
  144. if (FileExtensions.Contains(_FileInfo.Extension))
  145. _ConcurrentBag.Add(_FileInfo.FullName);
  146. });
  147. }
  148. if (ClearTypeDictionary)
  149. _TypeLookup.Clear();
  150. #endregion
  151. #region Scan files of implementations of T
  152. Parallel.ForEach(_ConcurrentBag, _File =>
  153. {
  154. // Seems to be a mono bug!
  155. if (_File != null)
  156. {
  157. Console.WriteLine(_File);
  158. try
  159. {
  160. if (_File != null)
  161. foreach (var _ActualType in Assembly.LoadFrom(_File).GetTypes())
  162. {
  163. if (!_ActualType.IsAbstract &&
  164. _ActualType.IsPublic &&
  165. _ActualType.IsVisible)
  166. {
  167. var _ActualTypeGetInterfaces = _ActualType.GetInterfaces();
  168. if (_ActualTypeGetInterfaces != null)
  169. {
  170. foreach (var _Interface in _ActualTypeGetInterfaces)
  171. {
  172. if (_Interface == typeof(TClass))
  173. {
  174. try
  175. {
  176. var __Id = _ActualType.Name;
  177. if (IdentificatorFunc != null)
  178. {
  179. var _T = Activator.CreateInstance(_ActualType) as TClass;
  180. if (_T != null)
  181. __Id = IdentificatorFunc(_T);
  182. }
  183. if (__Id != null && __Id != String.Empty)
  184. _TypeLookup.TryAdd(__Id, _ActualType);
  185. }
  186. catch (Exception e)
  187. {
  188. throw new AutoDiscoveryException("Could not activate or register " + typeof(TClass).Name + "-instance '" + _ActualType.Name + "'!", e);
  189. }
  190. }
  191. }
  192. }
  193. }
  194. }
  195. }
  196. catch (Exception e)
  197. {
  198. throw new AutoDiscoveryException("Autodiscovering implementations of interface '" + typeof(TClass).Name + "' within file '" + _File + "' failed!", e);
  199. }
  200. }
  201. });
  202. #endregion
  203. }
  204. #endregion
  205. #region TryGetInstance(Identificator, out Instance)
  206. /// <summary>
  207. /// Attempts to get an instance associated with the identificator.
  208. /// </summary>
  209. public Boolean TryGetInstance(String Identificator, out TClass Instance)
  210. {
  211. if (_InstanceLookup.TryGetValue(Identificator, out Instance))
  212. return true;
  213. Type _Type = null;
  214. if (_TypeLookup.TryGetValue(Identificator, out _Type))
  215. {
  216. try
  217. {
  218. Instance = (TClass) Activator.CreateInstance(_Type);
  219. // If it fails because of concurrency it does not matter!
  220. _InstanceLookup.TryAdd(Identificator, Instance);
  221. return true;
  222. }
  223. catch (Exception e)
  224. {
  225. throw new AutoDiscoveryException("An instance of " + typeof(TClass).Name + " with identificator '" + Identificator + "' could not be activated!", e);
  226. }
  227. }
  228. return false;
  229. }
  230. #endregion
  231. #region GetEnumerator()
  232. public IEnumerator<TClass> GetEnumerator()
  233. {
  234. TClass Instance;
  235. foreach (var Identificator in _TypeLookup.Keys)
  236. if (TryGetInstance(Identificator, out Instance))
  237. yield return Instance;
  238. }
  239. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  240. {
  241. TClass Instance;
  242. foreach (var Identificator in _TypeLookup.Keys)
  243. if (TryGetInstance(Identificator, out Instance))
  244. yield return Instance;
  245. }
  246. #endregion
  247. }
  248. }