PageRenderTime 194ms CodeModel.GetById 81ms app.highlight 9ms RepoModel.GetById 101ms app.codeStats 0ms

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