#region CPL License /* Nuclex Framework Copyright (C) 2002-2010 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections.Generic; using System.Reflection; #if ENABLE_SERVICEMANAGER namespace Nuclex.Support.Services { /// Lists all types in a changing set of assemblies public abstract class MultiAssemblyTypeLister : ITypeLister { #region class AssemblyTypes /// Caches the list of types types for an assembly private class AssemblyTypes { /// Initializes a new cached assembly types list /// Assembly the types are found in /// Types defined in the assembly public AssemblyTypes(Assembly assembly, Type[] types) { this.Assembly = assembly; this.Types = types; } /// Assembly the types are found in public Assembly Assembly; /// Types defined in the assembly public Type[] Types; } #endregion // class AssemblyTypes /// Initializes a new assembly type lister public MultiAssemblyTypeLister() { this.assemblyTypes = new LinkedList(); } /// Enumerates all types in the lister's assembly set /// An enumerator over all types in the lister's assembly set public IEnumerable GetTypes() { // Make sure the assembly list is filled and up-to-date if(this.assemblyTypes.Count == 0) { enlistAssembliesFirstTime(); } else { updateAssemblyList(); } // Iterate over all types in all assemblies LinkedListNode node = this.assemblyTypes.First; while(node != null) { Type[] types = node.Value.Types; for(int index = 0; index < types.Length; ++index) { yield return types[index]; } node = node.Next; } } /// Called when the assemblies set is queried for the first time private void enlistAssembliesFirstTime() { foreach(Assembly assembly in GetAssemblies()) { this.assemblyTypes.AddLast(new AssemblyTypes(assembly, assembly.GetTypes())); } } /// Called to update the assembly list if it has changed private void updateAssemblyList() { LinkedListNode node = this.assemblyTypes.First; foreach(Assembly assembly in GetAssemblies()) { // If we reached the end of the cache, this automatically becomes a new entry if(node == null) { this.assemblyTypes.AddLast(new AssemblyTypes(assembly, assembly.GetTypes())); } else { // Otherwise, figure out whether the assembly list has changed // Try to locate the cached entry for this assembly. If we have to skip entries, // we know that an assembly might have been removed from the set. This will be // handled by moved all matched assemblies to the beginning, so that when we // finish, the assemblies after the last checked one automatically become those // which are no longer in the set. LinkedListNode existingNode = node; while(existingNode.Value.Assembly != assembly) { existingNode = existingNode.Next; if(existingNode == null) { break; } } // Is this assembly not yet in the cache? if(existingNode == null) { // If this assembly wasn't found in the cache, add it in the same order // it was returned by the enumerator. This will improve efficiency later // since the update algorithm is designed to perform optimally if the order // remains the same between calls. this.assemblyTypes.AddBefore( node, new AssemblyTypes(assembly, assembly.GetTypes()) ); } else if(existingNode != node) { // Did we skip other cached assemblies? // If other cached assemblies had to be skipped, this indicates that // the set of assemblies has changed. Move the list nodes to the same order // in which the assemblies are returned by the enumerator. Any cached // assemblies that have been completely removed from the set will therefore // end up at the bottom of the list after the update has completed. this.assemblyTypes.Remove(existingNode); this.assemblyTypes.AddBefore(node, existingNode); } else { // Assembly was found in the expected place // If the assembly was found in the same place as it was found during // the last check, the assembly list is unchanged at this entry. node = node.Next; } } } // Any nodes behind the last checked node contain cached type lists for assemblies // that are no longer in the set, so these will be removed. while(node != null) { LinkedListNode nextNode = node.Next; this.assemblyTypes.Remove(node); node = nextNode; } } /// Obtains a list of any assemblies whose types should be listed /// A list of any assemblies whose types to list protected abstract IEnumerable GetAssemblies(); /// Cached assembly type lists private LinkedList assemblyTypes; } } // namespace Nuclex.Support.Services #endif // ENABLE_SERVICEMANAGER