/src/LinFu.Reflection/Loader.cs

http://github.com/philiplaureano/LinFu · C# · 259 lines · 123 code · 37 blank · 99 comment · 19 complexity · 84ada82791c47c9236c2c327b26561d0 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. namespace LinFu.Reflection
  7. {
  8. /// <summary>
  9. /// Represents a generic loader class that can
  10. /// load multiple <see cref="Action{T}" /> delegates from multiple files and
  11. /// apply them to a particular <typeparamref name="TTarget" /> instance.
  12. /// </summary>
  13. /// <typeparam name="TTarget"></typeparam>
  14. public class Loader<TTarget> : Loader<TTarget, Assembly, Type>
  15. {
  16. /// <summary>
  17. /// Initializes the target with the default settings.
  18. /// </summary>
  19. public Loader()
  20. : base(new TypeExtractor(), new AssemblyLoader(), new PluginLoader<TTarget, LoaderPluginAttribute>())
  21. {
  22. DirectoryLister = new DefaultDirectoryLister();
  23. }
  24. }
  25. /// <summary>
  26. /// Represents a generic loader class that can
  27. /// load multiple <see cref="Action{T}" /> delegates from multiple files and
  28. /// apply them to a particular <typeparamref name="TTarget" /> instance.
  29. /// </summary>
  30. /// <typeparam name="TTarget">The target type to configure.</typeparam>
  31. /// <typeparam name="TAssembly">The assembly type.</typeparam>
  32. /// <typeparam name="TType">The target type.</typeparam>
  33. public class Loader<TTarget, TAssembly, TType> : ILoader<TTarget>
  34. {
  35. private readonly List<Action<TTarget>> _actions = new List<Action<TTarget>>();
  36. private readonly HashSet<string> _loadedFiles = new HashSet<string>();
  37. private readonly List<Action<ILoader<TTarget>>> _loaderActions = new List<Action<ILoader<TTarget>>>();
  38. private readonly List<IActionLoader<TTarget, string>> _loaders = new List<IActionLoader<TTarget, string>>();
  39. private readonly AssemblyTargetLoader<ILoader<TTarget>, TAssembly, TType> _pluginLoader;
  40. private readonly List<ILoaderPlugin<TTarget>> _plugins = new List<ILoaderPlugin<TTarget>>();
  41. /// <summary>
  42. /// Initializes the target with the default settings.
  43. /// </summary>
  44. public Loader(ITypeExtractor<TAssembly, TType> typeExtractor, IAssemblyLoader<TAssembly> assemblyLoader)
  45. : this(typeExtractor, assemblyLoader, null)
  46. {
  47. }
  48. /// <summary>
  49. /// Initializes the target with the default settings.
  50. /// </summary>
  51. public Loader(ITypeExtractor<TAssembly, TType> typeExtractor, IAssemblyLoader<TAssembly> assemblyLoader,
  52. IActionLoader<ILoader<TTarget>, TType> pluginTypeLoader)
  53. {
  54. _pluginLoader = new AssemblyTargetLoader<ILoader<TTarget>, TAssembly, TType>(typeExtractor, assemblyLoader);
  55. DirectoryLister = new DefaultDirectoryLister();
  56. // Make sure that loader plugins are loaded
  57. // on every LoadDirectory() call
  58. _pluginLoader.TypeLoaders.Add(pluginTypeLoader);
  59. }
  60. /// <summary>
  61. /// The list of actions that will execute
  62. /// every time a target instance is configured.
  63. /// </summary>
  64. public IList<Action<TTarget>> QueuedActions => _actions;
  65. /// <summary>
  66. /// The custom list of actions that will be
  67. /// performed prior to the beginning of the first load operation.
  68. /// </summary>
  69. /// <remarks>
  70. /// These actions will be performed only once per reset.
  71. /// </remarks>
  72. public IList<Action<ILoader<TTarget>>> CustomLoaderActions => _loaderActions;
  73. /// <summary>
  74. /// The list of <see cref="ILoaderPlugin{TTarget}" />
  75. /// instances that will be used to
  76. /// signal the beginning and end of the
  77. /// load sequence.
  78. /// </summary>
  79. public IList<ILoaderPlugin<TTarget>> Plugins => _plugins;
  80. /// <summary>
  81. /// The list of <see cref="IActionLoader{TTarget, TInput}" />
  82. /// instances responsible for configuring the target instance.
  83. /// </summary>
  84. public IList<IActionLoader<TTarget, string>> FileLoaders => _loaders;
  85. /// <summary>
  86. /// Gets or sets the <see cref="IDirectoryListing" /> instance
  87. /// responsible for returning a list of filenames
  88. /// to the loader for processing.
  89. /// </summary>
  90. public IDirectoryListing DirectoryLister { get; set; }
  91. /// <summary>
  92. /// Loads the container with the files listed in
  93. /// the target <paramref name="directory" /> that match
  94. /// the given <paramref name="filespec">file pattern</paramref>.
  95. /// </summary>
  96. /// <param name="directory">The full path of the location to scan.</param>
  97. /// <param name="filespec">The wildcard file pattern string to use when specifying the target files.</param>
  98. public void LoadDirectory(string directory, string filespec)
  99. {
  100. // Determine which files currently exist
  101. var files = DirectoryLister.GetFiles(directory, filespec);
  102. foreach (var currentFile in files)
  103. {
  104. // Make sure the file is loaded only once
  105. if (_loadedFiles.Contains(currentFile))
  106. continue;
  107. // HACK: Manually load any loader plugins
  108. // into the loader
  109. if (_pluginLoader != null && _pluginLoader.CanLoad(currentFile))
  110. {
  111. // Immediately execute any custom loader actions
  112. // embedded in the file itself
  113. var customActions = _pluginLoader.Load(currentFile);
  114. foreach (var customAction in customActions) customAction(this);
  115. }
  116. LoadFile(currentFile);
  117. _loadedFiles.Add(currentFile);
  118. }
  119. }
  120. /// <summary>
  121. /// Loads the current configuration into the <paramref name="target" />
  122. /// instance.
  123. /// </summary>
  124. /// <param name="target"></param>
  125. public void LoadInto(TTarget target)
  126. {
  127. // Abort the load if the container
  128. // is invalid
  129. if (ReferenceEquals(target, null))
  130. return;
  131. // Avoid duplicate actions by making
  132. // sure that the loader executes
  133. // the list of custom actions once
  134. foreach (var customAction in CustomLoaderActions) customAction(this);
  135. CustomLoaderActions.Clear();
  136. // Signal the beginning of the load
  137. foreach (var plugin in Plugins)
  138. {
  139. if (plugin == null || !ShouldLoad(plugin))
  140. continue;
  141. plugin.BeginLoad(target);
  142. }
  143. // Configure the container
  144. foreach (var action in QueuedActions)
  145. {
  146. if (action == null)
  147. continue;
  148. action(target);
  149. }
  150. // Signal the end of the load
  151. foreach (var plugin in Plugins)
  152. {
  153. if (plugin == null || !ShouldLoad(plugin))
  154. continue;
  155. plugin.EndLoad(target);
  156. }
  157. }
  158. /// <summary>
  159. /// Clears the currently loaded configuration
  160. /// and resets the loader back to its defaults.
  161. /// </summary>
  162. public void Reset()
  163. {
  164. _loaders.Clear();
  165. _plugins.Clear();
  166. _actions.Clear();
  167. _loaderActions.Clear();
  168. _loadedFiles.Clear();
  169. }
  170. /// <summary>
  171. /// Monitors the given <paramref name="directory" /> for any file changes and
  172. /// updates the current loader whenever the files that match the given <paramref name="fileSpec" />
  173. /// are loaded into memory
  174. /// </summary>
  175. /// <param name="directory">The full path of the location to scan.</param>
  176. /// <param name="fileSpec">The wildcard file pattern string to use when specifying the target files.</param>
  177. /// <param name="target">The target that will be loaded using the current loader.</param>
  178. public void AutoLoadFrom(string directory, string fileSpec, TTarget target)
  179. {
  180. if (!Directory.Exists(directory))
  181. throw new DirectoryNotFoundException(directory);
  182. FileSystemEventHandler handler = (source, e) =>
  183. {
  184. LoadDirectory(directory, fileSpec);
  185. LoadInto(target);
  186. };
  187. var fullPath = Path.GetFullPath(directory);
  188. var watcher = new FileSystemWatcher(fullPath, fileSpec);
  189. watcher.Created += handler;
  190. watcher.Changed += handler;
  191. watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
  192. | NotifyFilters.FileName | NotifyFilters.DirectoryName;
  193. watcher.EnableRaisingEvents = true;
  194. }
  195. /// <summary>
  196. /// Determines whether or not a specific plugin should be loaded.
  197. /// </summary>
  198. /// <param name="plugin">The target plugin to be loaded.</param>
  199. /// <returns><c>true</c> if the plugin should be loaded; otherwise, it will return <c>false</c>.</returns>
  200. protected virtual bool ShouldLoad(ILoaderPlugin<TTarget> plugin)
  201. {
  202. return true;
  203. }
  204. /// <summary>
  205. /// Loads the <paramref name="currentFile">current file</paramref>
  206. /// using the list of associated <see cref="FileLoaders" />.
  207. /// </summary>
  208. /// <param name="currentFile">The full path and filename being loaded.</param>
  209. public void LoadFile(string currentFile)
  210. {
  211. foreach (var loader in FileLoaders)
  212. {
  213. if (loader == null || !loader.CanLoad(currentFile))
  214. continue;
  215. var actions = loader.Load(currentFile);
  216. if (actions.Count() == 0)
  217. continue;
  218. _actions.AddRange(actions);
  219. }
  220. }
  221. }
  222. }