2012-08-01 22:06:15 +06:00
using System ;
using System.Collections ;
using System.Collections.Generic ;
2012-11-11 09:09:06 +05:00
using System.Globalization ;
using System.IO ;
2012-08-01 22:06:15 +06:00
using System.Linq ;
using System.Reflection ;
using System.Text ;
using System.Threading ;
2012-11-12 08:10:12 +05:00
using System.Web.Caching ;
using System.Web.Compilation ;
2012-11-11 09:09:06 +05:00
using System.Xml ;
using System.Xml.Linq ;
using Umbraco.Core.Configuration ;
using Umbraco.Core.IO ;
2012-08-01 22:06:15 +06:00
using Umbraco.Core.Logging ;
2012-10-30 09:25:28 -01:00
using Umbraco.Core.Models ;
2012-11-05 06:14:44 +06:00
using Umbraco.Core.PropertyEditors ;
2012-08-01 22:06:15 +06:00
using umbraco.interfaces ;
2012-11-13 15:13:06 -01:00
using File = System . IO . File ;
2012-08-01 22:06:15 +06:00
namespace Umbraco.Core
{
/// <summary>
/// Used to resolve all plugin types and cache them and is also used to instantiate plugin types
/// </summary>
/// <remarks>
///
/// This class should be used to resolve all plugin types, the TypeFinder should not be used directly!
///
/// This class can expose extension methods to resolve custom plugins
///
2012-11-11 09:09:06 +05:00
/// Before this class resolves any plugins it checks if the hash has changed for the DLLs in the /bin folder, if it hasn't
/// it will use the cached resolved plugins that it has already found which means that no assembly scanning is necessary. This leads
/// to much faster startup times.
2012-08-01 22:06:15 +06:00
/// </remarks>
internal class PluginManager
{
2012-11-12 08:10:12 +05:00
private readonly ApplicationContext _appContext ;
2012-08-01 22:06:15 +06:00
2012-11-12 08:10:12 +05:00
/// <summary>
/// Creates a new PluginManager with an ApplicationContext instance which ensures that the plugin xml
/// file is cached temporarily until app startup completes.
/// </summary>
/// <param name="appContext"></param>
/// <param name="detectBinChanges"></param>
internal PluginManager ( ApplicationContext appContext , bool detectBinChanges = true )
: this ( detectBinChanges )
{
if ( appContext = = null ) throw new ArgumentNullException ( "appContext" ) ;
_appContext = appContext ;
}
/// <summary>
/// Creates a new PluginManager
/// </summary>
/// <param name="detectBinChanges">
/// If true will detect changes in the /bin folder and therefor load plugins from the
/// cached plugins file if one is found. If false will never use the cache file for plugins
/// </param>
internal PluginManager ( bool detectBinChanges = true )
2012-08-01 22:06:15 +06:00
{
2012-11-11 09:09:06 +05:00
_tempFolder = IOHelper . MapPath ( "~/App_Data/TEMP/PluginCache" ) ;
//create the folder if it doesn't exist
if ( ! Directory . Exists ( _tempFolder ) )
{
Directory . CreateDirectory ( _tempFolder ) ;
}
2012-11-12 08:10:12 +05:00
if ( detectBinChanges )
{
//first check if the cached hash is 0, if it is then we ne
//do the check if they've changed
HaveAssembliesChanged = ( CachedAssembliesHash ! = CurrentAssembliesHash ) | | CachedAssembliesHash = = 0 ;
//if they have changed, we need to write the new file
if ( HaveAssembliesChanged )
{
WriteCachePluginsHash ( ) ;
}
}
else
2012-11-11 09:09:06 +05:00
{
2012-11-12 08:10:12 +05:00
//always set to true if we're not detecting (generally only for testing)
HaveAssembliesChanged = true ;
2012-11-11 09:09:06 +05:00
}
2012-11-12 08:10:12 +05:00
2012-08-01 22:06:15 +06:00
}
static PluginManager _resolver ;
static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim ( ) ;
2012-11-11 09:09:06 +05:00
private readonly string _tempFolder ;
private long _cachedAssembliesHash = - 1 ;
private long _currentAssembliesHash = - 1 ;
2012-08-01 22:06:15 +06:00
/// <summary>
/// We will ensure that no matter what, only one of these is created, this is to ensure that caching always takes place
/// </summary>
/// <remarks>
/// The setter is generally only used for unit tests
/// </remarks>
internal static PluginManager Current
{
get
{
using ( var l = new UpgradeableReadLock ( Lock ) )
{
if ( _resolver = = null )
{
l . UpgradeToWriteLock ( ) ;
2012-11-12 08:10:12 +05:00
_resolver = ApplicationContext . Current = = null
? new PluginManager ( )
: new PluginManager ( ApplicationContext . Current ) ;
2012-08-01 22:06:15 +06:00
}
return _resolver ;
}
}
set { _resolver = value ; }
}
2012-11-11 09:09:06 +05:00
#region Hash checking methods
/// <summary>
/// Returns a bool if the assemblies in the /bin have changed since they were last hashed.
/// </summary>
internal bool HaveAssembliesChanged { get ; private set ; }
/// <summary>
/// Returns the currently cached hash value of the scanned assemblies in the /bin folder. Returns 0
/// if no cache is found.
/// </summary>
/// <value> </value>
internal long CachedAssembliesHash
{
get
{
if ( _cachedAssembliesHash ! = - 1 )
return _cachedAssembliesHash ;
var filePath = Path . Combine ( _tempFolder , "umbraco-plugins.hash" ) ;
if ( ! File . Exists ( filePath ) )
return 0 ;
var hash = File . ReadAllText ( filePath , Encoding . UTF8 ) ;
Int64 val ;
if ( Int64 . TryParse ( hash , out val ) )
{
_cachedAssembliesHash = val ;
return _cachedAssembliesHash ;
}
//it could not parse for some reason so we'll return 0.
return 0 ;
}
}
/// <summary>
/// Returns the current assemblies hash based on creating a hash from the assemblies in the /bin
/// </summary>
/// <value> </value>
internal long CurrentAssembliesHash
{
get
{
if ( _currentAssembliesHash ! = - 1 )
return _currentAssembliesHash ;
_currentAssembliesHash = GetAssembliesHash ( new DirectoryInfo ( IOHelper . MapPath ( SystemDirectories . Bin ) ) . GetFiles ( "*.dll" ) ) ;
return _currentAssembliesHash ;
}
}
/// <summary>
/// Writes the assembly hash file
/// </summary>
private void WriteCachePluginsHash ( )
{
var filePath = Path . Combine ( _tempFolder , "umbraco-plugins.hash" ) ;
File . WriteAllText ( filePath , CurrentAssembliesHash . ToString ( ) , Encoding . UTF8 ) ;
}
/// <summary>
/// Returns a unique hash for the combination of FileInfo objects passed in
/// </summary>
/// <param name="plugins"></param>
/// <returns></returns>
internal static long GetAssembliesHash ( IEnumerable < FileInfo > plugins )
{
using ( DisposableTimer . TraceDuration < PluginManager > ( "Determining hash of plugins on disk" , "Hash determined" ) )
{
var hashCombiner = new HashCodeCombiner ( ) ;
//add each unique folder to the hash
foreach ( var i in plugins . Select ( x = > x . Directory ) . DistinctBy ( x = > x . FullName ) )
{
hashCombiner . AddFolder ( i ) ;
}
return ConvertPluginsHashFromHex ( hashCombiner . GetCombinedHashCode ( ) ) ;
}
}
/// <summary>
/// Converts the hash value of current plugins to long from string
/// </summary>
/// <param name="val"></param>
/// <returns></returns>
internal static long ConvertPluginsHashFromHex ( string val )
{
long outVal ;
if ( Int64 . TryParse ( val , NumberStyles . AllowHexSpecifier , CultureInfo . InvariantCulture , out outVal ) )
{
return outVal ;
}
return 0 ;
}
/// <summary>
/// Attempts to resolve the list of plugin + assemblies found in the runtime for the base type 'T' passed in.
/// If the cache file doesn't exist, fails to load, is corrupt or the type 'T' element is not found then
/// a false attempt is returned.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
2012-11-12 08:10:12 +05:00
internal Attempt < IEnumerable < string > > TryGetCachedPluginsFromFile < T > ( )
2012-11-11 09:09:06 +05:00
{
var filePath = Path . Combine ( _tempFolder , "umbraco-plugins.list" ) ;
if ( ! File . Exists ( filePath ) )
2012-11-12 08:10:12 +05:00
return Attempt < IEnumerable < string > > . False ;
2012-11-11 09:09:06 +05:00
try
{
2012-11-12 08:10:12 +05:00
//we will load the xml document, if the app context exist, we will load it from the cache (which is only around for 5 minutes)
//while the app boots up, this should save some IO time on app startup when the app context is there (which is always unless in unit tests)
XDocument xml ;
if ( _appContext ! = null )
{
xml = _appContext . ApplicationCache . GetCacheItem ( "umbraco-plugins.list" ,
new TimeSpan ( 0 , 0 , 5 , 0 ) ,
( ) = > XDocument . Load ( filePath ) ) ;
}
else
{
xml = XDocument . Load ( filePath ) ;
}
2012-11-11 09:09:06 +05:00
if ( xml . Root = = null )
2012-11-12 08:10:12 +05:00
return Attempt < IEnumerable < string > > . False ;
2012-11-11 09:09:06 +05:00
var typeElement = xml . Root . Elements ( )
. SingleOrDefault ( x = >
x . Name . LocalName = = "baseType"
& & ( ( string ) x . Attribute ( "type" ) ) = = typeof ( T ) . FullName ) ;
if ( typeElement = = null )
2012-11-12 08:10:12 +05:00
return Attempt < IEnumerable < string > > . False ;
2012-11-11 09:09:06 +05:00
//return success
2012-11-12 08:10:12 +05:00
return new Attempt < IEnumerable < string > > (
2012-11-11 09:09:06 +05:00
true ,
typeElement . Elements ( "add" )
2012-11-12 08:10:12 +05:00
. Select ( x = > ( string ) x . Attribute ( "type" ) ) ) ;
2012-11-11 09:09:06 +05:00
}
catch ( Exception )
{
//if the file is corrupted, etc... return false
2012-11-12 08:10:12 +05:00
return Attempt < IEnumerable < string > > . False ;
2012-11-11 09:09:06 +05:00
}
}
/// <summary>
/// Adds/Updates the type list for the base type 'T' in the cached file
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="typesFound"></param>
/// <remarks>
/// THIS METHOD IS NOT THREAD SAFE
/// </remarks>
/// <example>
/// <![CDATA[
/// <plugins>
/// <baseType type="Test.Testing.Tester">
/// <add type="My.Assembly.MyTester" assembly="My.Assembly" />
/// <add type="Your.Assembly.YourTester" assembly="Your.Assembly" />
/// </baseType>
/// </plugins>
/// ]]>
/// </example>
internal void UpdateCachedPluginsFile < T > ( IEnumerable < Type > typesFound )
{
var filePath = Path . Combine ( _tempFolder , "umbraco-plugins.list" ) ;
XDocument xml ;
try
{
xml = XDocument . Load ( filePath ) ;
}
catch
{
//if there's an exception loading then this is somehow corrupt, we'll just replace it.
File . Delete ( filePath ) ;
//create the document and the root
xml = new XDocument ( new XElement ( "plugins" ) ) ;
}
if ( xml . Root = = null )
{
//if for some reason there is no root, create it
xml . Add ( new XElement ( "plugins" ) ) ;
}
//find the type 'T' element to add or update
var typeElement = xml . Root . Elements ( )
. SingleOrDefault ( x = >
x . Name . LocalName = = "baseType"
& & ( ( string ) x . Attribute ( "type" ) ) = = typeof ( T ) . FullName ) ;
if ( typeElement = = null )
{
//create the type element
typeElement = new XElement ( "baseType" , new XAttribute ( "type" , typeof ( T ) . FullName ) ) ;
//then add it to the root
xml . Root . Add ( typeElement ) ;
}
//now we have the type element, we need to clear any previous types as children and add/update it with new ones
typeElement . ReplaceNodes ( typesFound
2012-11-12 08:10:12 +05:00
. Select ( x = >
new XElement ( "add" ,
new XAttribute ( "type" , x . AssemblyQualifiedName ) ) ) ) ;
2012-11-11 09:09:06 +05:00
//save the xml file
xml . Save ( filePath ) ;
}
#endregion
2012-08-01 22:06:15 +06:00
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim ( ) ;
private readonly HashSet < TypeList > _types = new HashSet < TypeList > ( ) ;
private IEnumerable < Assembly > _assemblies ;
/// <summary>
/// Returns all classes attributed with XsltExtensionAttribute attribute
/// </summary>
/// <returns></returns>
internal IEnumerable < Type > ResolveCacheRefreshers ( )
{
return ResolveTypes < ICacheRefresher > ( ) ;
}
2012-11-05 06:14:44 +06:00
/// <summary>
/// Returns all available IPropertyEditorValueConverter
/// </summary>
/// <returns></returns>
internal IEnumerable < Type > ResolvePropertyEditorValueConverters ( )
{
return ResolveTypes < IPropertyEditorValueConverter > ( ) ;
}
2012-08-01 22:06:15 +06:00
/// <summary>
/// Returns all available IDataType in application
/// </summary>
/// <returns></returns>
internal IEnumerable < Type > ResolveDataTypes ( )
{
return ResolveTypes < IDataType > ( ) ;
}
2012-08-01 22:46:13 +06:00
/// <summary>
/// Returns all available IMacroGuiRendering in application
/// </summary>
/// <returns></returns>
internal IEnumerable < Type > ResolveMacroRenderings ( )
{
return ResolveTypes < IMacroGuiRendering > ( ) ;
}
2012-08-01 23:03:26 +06:00
/// <summary>
/// Returns all available IPackageAction in application
/// </summary>
/// <returns></returns>
internal IEnumerable < Type > ResolvePackageActions ( )
{
return ResolveTypes < IPackageAction > ( ) ;
}
2012-08-01 23:30:37 +06:00
/// <summary>
/// Returns all available IAction in application
/// </summary>
/// <returns></returns>
internal IEnumerable < Type > ResolveActions ( )
{
return ResolveTypes < IAction > ( ) ;
}
2012-10-30 09:25:28 -01:00
/// <summary>
/// Returns all available IMacroPropertyTypes in application
/// </summary>
/// <returns></returns>
internal IEnumerable < Type > ResolveMacroPropertyTypes ( )
{
return ResolveTypes < IMacroPropertyType > ( ) ;
}
2012-08-01 22:06:15 +06:00
/// <summary>
/// Gets/sets which assemblies to scan when type finding, generally used for unit testing, if not explicitly set
/// this will search all assemblies known to have plugins and exclude ones known to not have them.
/// </summary>
internal IEnumerable < Assembly > AssembliesToScan
{
2012-08-01 23:50:33 +06:00
get { return _assemblies ? ? ( _assemblies = TypeFinder . GetAssembliesWithKnownExclusions ( ) ) ; }
2012-08-01 22:06:15 +06:00
set { _assemblies = value ; }
}
/// <summary>
/// Used to resolve and create instances of the specified type based on the resolved/cached plugin types
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="throwException">set to true if an exception is to be thrown if there is an error during instantiation</param>
/// <returns></returns>
internal IEnumerable < T > FindAndCreateInstances < T > ( bool throwException = false )
{
var types = ResolveTypes < T > ( ) ;
return CreateInstances < T > ( types , throwException ) ;
}
/// <summary>
/// Used to create instances of the specified type based on the resolved/cached plugin types
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="types"></param>
/// <param name="throwException">set to true if an exception is to be thrown if there is an error during instantiation</param>
/// <returns></returns>
internal IEnumerable < T > CreateInstances < T > ( IEnumerable < Type > types , bool throwException = false )
{
2012-10-13 09:26:30 +05:00
//Have removed logging because it doesn't really need to be logged since the time taken is generally 0ms.
//we want to know if it fails ever, not how long it took if it is only 0.
2012-08-01 22:06:15 +06:00
var typesAsArray = types . ToArray ( ) ;
2012-10-13 09:26:30 +05:00
//using (DisposableTimer.DebugDuration<PluginManager>(
// String.Format("Starting instantiation of {0} objects of type {1}", typesAsArray.Length, typeof(T).FullName),
// String.Format("Completed instantiation of {0} objects of type {1}", typesAsArray.Length, typeof(T).FullName)))
//{
2012-08-01 22:06:15 +06:00
var instances = new List < T > ( ) ;
foreach ( var t in typesAsArray )
{
try
{
var typeInstance = ( T ) Activator . CreateInstance ( t ) ;
instances . Add ( typeInstance ) ;
}
catch ( Exception ex )
{
LogHelper . Error < PluginManager > ( String . Format ( "Error creating type {0}" , t . FullName ) , ex ) ;
if ( throwException )
{
throw ex ;
}
}
}
return instances ;
2012-10-13 09:26:30 +05:00
//}
2012-08-01 22:06:15 +06:00
}
/// <summary>
/// Used to create an instance of the specified type based on the resolved/cached plugin types
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="type"></param>
/// <param name="throwException"></param>
/// <returns></returns>
internal T CreateInstance < T > ( Type type , bool throwException = false )
{
var instances = CreateInstances < T > ( new [ ] { type } , throwException ) ;
return instances . FirstOrDefault ( ) ;
}
2012-10-17 08:44:32 +05:00
private IEnumerable < Type > ResolveTypes < T > (
Func < IEnumerable < Type > > finder ,
TypeResolutionKind resolutionType ,
bool cacheResult )
2012-08-01 22:06:15 +06:00
{
using ( var readLock = new UpgradeableReadLock ( _lock ) )
{
using ( DisposableTimer . TraceDuration < PluginManager > (
String . Format ( "Starting resolution types of {0}" , typeof ( T ) . FullName ) ,
String . Format ( "Completed resolution of types of {0}" , typeof ( T ) . FullName ) ) )
2012-11-11 09:09:06 +05:00
{
2012-08-01 22:06:15 +06:00
//check if the TypeList already exists, if so return it, if not we'll create it
2012-10-17 08:44:32 +05:00
var typeList = _types . SingleOrDefault ( x = > x . IsTypeList < T > ( resolutionType ) ) ;
//if we're not caching the result then proceed, or if the type list doesn't exist then proceed
if ( ! cacheResult | | typeList = = null )
2012-08-01 22:06:15 +06:00
{
//upgrade to a write lock since we're adding to the collection
readLock . UpgradeToWriteLock ( ) ;
2012-10-17 08:44:32 +05:00
typeList = new TypeList < T > ( resolutionType ) ;
2012-08-01 22:06:15 +06:00
2012-11-11 09:09:06 +05:00
//we first need to look into our cache file (this has nothing to do with the 'cacheResult' parameter which caches in memory).
if ( ! HaveAssembliesChanged )
2012-08-01 22:06:15 +06:00
{
2012-11-11 09:09:06 +05:00
var fileCacheResult = TryGetCachedPluginsFromFile < T > ( ) ;
if ( fileCacheResult . Success )
{
var successfullyLoadedFromCache = true ;
//we have a previous cache for this so we don't need to scan we just load what has been found in the file
foreach ( var t in fileCacheResult . Result )
{
try
{
2012-11-12 08:10:12 +05:00
//we use the build manager to ensure we get all types loaded, this is slightly slower than
//Type.GetType but if the types in the assembly aren't loaded yet then we have problems with that.
var type = BuildManager . GetType ( t , true ) ;
typeList . AddType ( type ) ;
2012-11-11 09:09:06 +05:00
}
catch ( Exception ex )
{
//if there are any exceptions loading types, we have to exist, this should never happen so
//we will need to revert to scanning for types.
successfullyLoadedFromCache = false ;
2012-11-12 08:10:12 +05:00
LogHelper . Error < PluginManager > ( "Could not load a cached plugin type: " + t + " now reverting to re-scanning assemblies for the base type: " + typeof ( T ) . FullName , ex ) ;
2012-11-11 09:09:06 +05:00
break ;
}
}
if ( ! successfullyLoadedFromCache )
{
//we need to manually load by scanning if loading from the file was not successful.
LoadViaScanningAndUpdateCacheFile < T > ( typeList , finder ) ;
}
2012-11-12 08:10:12 +05:00
else
{
LogHelper . Debug < PluginManager > ( "Loaded plugin types " + typeof ( T ) . FullName + " from persisted cache" ) ;
}
2012-11-11 09:09:06 +05:00
}
}
else
2012-08-01 22:06:15 +06:00
{
2012-11-11 09:09:06 +05:00
//we don't have a cache for this so proceed to look them up by scanning
LoadViaScanningAndUpdateCacheFile < T > ( typeList , finder ) ;
2012-08-01 22:06:15 +06:00
}
2012-10-17 08:44:32 +05:00
//only add the cache if we are to cache the results
if ( cacheResult )
{
//add the type list to the collection
_types . Add ( typeList ) ;
}
2012-08-01 22:06:15 +06:00
}
return typeList . GetTypes ( ) ;
}
}
}
2012-11-11 09:09:06 +05:00
/// <summary>
/// This method invokes the finder which scans the assemblies for the types and then loads the result into the type finder.
/// Once the results are loaded, we update the cached type xml file
/// </summary>
/// <param name="typeList"></param>
/// <param name="finder"></param>
/// <remarks>
/// THIS METHODS IS NOT THREAD SAFE
/// </remarks>
private void LoadViaScanningAndUpdateCacheFile < T > ( TypeList typeList , Func < IEnumerable < Type > > finder )
{
//we don't have a cache for this so proceed to look them up by scanning
foreach ( var t in finder ( ) )
{
typeList . AddType ( t ) ;
}
UpdateCachedPluginsFile < T > ( typeList . GetTypes ( ) ) ;
}
2012-08-01 22:06:15 +06:00
/// <summary>
/// Generic method to find the specified type and cache the result
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
2012-10-17 08:44:32 +05:00
internal IEnumerable < Type > ResolveTypes < T > ( bool cacheResult = true )
2012-08-01 22:06:15 +06:00
{
2012-10-17 08:44:32 +05:00
return ResolveTypes < T > (
( ) = > TypeFinder . FindClassesOfType < T > ( AssembliesToScan ) ,
TypeResolutionKind . FindAllTypes ,
cacheResult ) ;
2012-08-01 22:06:15 +06:00
}
/// <summary>
/// Generic method to find the specified type that has an attribute and cache the result
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TAttribute"></typeparam>
/// <returns></returns>
2012-10-17 08:44:32 +05:00
internal IEnumerable < Type > ResolveTypesWithAttribute < T , TAttribute > ( bool cacheResult = true )
2012-08-01 22:06:15 +06:00
where TAttribute : Attribute
{
2012-10-17 08:44:32 +05:00
return ResolveTypes < T > (
( ) = > TypeFinder . FindClassesOfTypeWithAttribute < T , TAttribute > ( AssembliesToScan ) ,
TypeResolutionKind . FindTypesWithAttribute ,
cacheResult ) ;
2012-08-01 22:06:15 +06:00
}
/// <summary>
/// Generic method to find any type that has the specified attribute
/// </summary>
/// <typeparam name="TAttribute"></typeparam>
/// <returns></returns>
2012-10-17 08:44:32 +05:00
internal IEnumerable < Type > ResolveAttributedTypes < TAttribute > ( bool cacheResult = true )
2012-08-01 22:06:15 +06:00
where TAttribute : Attribute
{
return ResolveTypes < TAttribute > (
2012-08-01 23:50:33 +06:00
( ) = > TypeFinder . FindClassesWithAttribute < TAttribute > ( AssembliesToScan ) ,
2012-10-17 08:44:32 +05:00
TypeResolutionKind . FindAttributedTypes ,
cacheResult ) ;
2012-08-01 22:06:15 +06:00
}
/// <summary>
/// Used for unit tests
/// </summary>
/// <returns></returns>
internal HashSet < TypeList > GetTypeLists ( )
{
return _types ;
}
2012-10-17 08:44:32 +05:00
#region Private classes / Enums
/// <summary>
/// The type of resolution being invoked
/// </summary>
internal enum TypeResolutionKind
{
FindAllTypes ,
FindAttributedTypes ,
FindTypesWithAttribute
}
2012-08-01 22:06:15 +06:00
internal abstract class TypeList
{
public abstract void AddType ( Type t ) ;
2012-10-17 08:44:32 +05:00
public abstract bool IsTypeList < TLookup > ( TypeResolutionKind resolutionType ) ;
2012-08-01 22:06:15 +06:00
public abstract IEnumerable < Type > GetTypes ( ) ;
}
internal class TypeList < T > : TypeList
{
2012-10-17 08:44:32 +05:00
private readonly TypeResolutionKind _resolutionType ;
2012-08-01 22:06:15 +06:00
2012-10-17 08:44:32 +05:00
public TypeList ( TypeResolutionKind resolutionType )
2012-08-01 22:06:15 +06:00
{
2012-10-17 08:44:32 +05:00
_resolutionType = resolutionType ;
2012-08-01 22:06:15 +06:00
}
private readonly List < Type > _types = new List < Type > ( ) ;
public override void AddType ( Type t )
{
2012-10-17 08:44:32 +05:00
//if the type is an attribute type we won't do the type check because typeof<T> is going to be the
//attribute type whereas the 't' type is the object type found with the attribute.
if ( _resolutionType = = TypeResolutionKind . FindAttributedTypes | | t . IsType < T > ( ) )
2012-08-01 22:06:15 +06:00
{
_types . Add ( t ) ;
}
}
2012-10-17 08:44:32 +05:00
/// <summary>
/// Returns true if the current TypeList is of the same type and of the same type
/// </summary>
/// <typeparam name="TLookup"></typeparam>
/// <param name="resolutionType"></param>
/// <returns></returns>
public override bool IsTypeList < TLookup > ( TypeResolutionKind resolutionType )
2012-08-01 22:06:15 +06:00
{
2012-10-17 08:44:32 +05:00
return _resolutionType = = resolutionType & & ( typeof ( T ) ) . IsType < TLookup > ( ) ;
2012-08-01 22:06:15 +06:00
}
public override IEnumerable < Type > GetTypes ( )
{
return _types ;
}
}
#endregion
}
}