2012-08-13 13:26:06 -01:00
using System ;
2012-11-07 09:30:40 +05:00
using System.Collections.Concurrent ;
2012-08-13 13:26:06 -01:00
using System.Collections.Generic ;
using System.Configuration ;
using System.Linq ;
2012-11-07 09:30:40 +05:00
using System.Reflection ;
2012-08-13 13:26:06 -01:00
using System.Text ;
2012-11-08 08:27:38 +05:00
using Umbraco.Core.CodeAnnotations ;
2012-08-13 13:26:06 -01:00
using Umbraco.Core.Configuration ;
namespace Umbraco.Core.IO
2013-05-14 14:18:41 -10:00
{
public class FileSystemProviderManager
2012-08-13 13:26:06 -01:00
{
private readonly FileSystemProvidersSection _config ;
#region Singleton
private static readonly FileSystemProviderManager Instance = new FileSystemProviderManager ( ) ;
public static FileSystemProviderManager Current
{
get { return Instance ; }
}
#endregion
#region Constructors
2013-05-14 14:18:41 -10:00
internal FileSystemProviderManager ( )
2012-08-13 13:26:06 -01:00
{
2013-09-16 17:39:45 +10:00
_config = ( FileSystemProvidersSection ) ConfigurationManager . GetSection ( "umbracoConfiguration/FileSystemProviders" ) ;
2012-08-13 13:26:06 -01:00
}
#endregion
2012-11-07 09:30:40 +05:00
/// <summary>
/// used to cache the lookup of how to construct this object so we don't have to reflect each time.
/// </summary>
private class ProviderConstructionInfo
{
public object [ ] Parameters { get ; set ; }
public ConstructorInfo Constructor { get ; set ; }
public string ProviderAlias { get ; set ; }
}
2012-08-13 13:26:06 -01:00
2012-11-07 09:30:40 +05:00
private readonly ConcurrentDictionary < string , ProviderConstructionInfo > _providerLookup = new ConcurrentDictionary < string , ProviderConstructionInfo > ( ) ;
private readonly ConcurrentDictionary < Type , string > _wrappedProviderLookup = new ConcurrentDictionary < Type , string > ( ) ;
2012-08-13 13:26:06 -01:00
2013-05-14 14:18:41 -10:00
/// <summary>
/// Returns the underlying (non-typed) file system provider for the alias specified
/// </summary>
/// <param name="alias"></param>
/// <returns></returns>
/// <remarks>
/// It is recommended to use the typed GetFileSystemProvider method instead to get a strongly typed provider instance.
/// </remarks>
public IFileSystem GetUnderlyingFileSystemProvider ( string alias )
2012-11-07 09:30:40 +05:00
{
//either get the constructor info from cache or create it and add to cache
var ctorInfo = _providerLookup . GetOrAdd ( alias , s = >
{
var providerConfig = _config . Providers [ s ] ;
if ( providerConfig = = null )
throw new ArgumentException ( string . Format ( "No provider found with the alias '{0}'" , s ) ) ;
var providerType = Type . GetType ( providerConfig . Type ) ;
if ( providerType = = null )
throw new InvalidOperationException ( string . Format ( "Could not find type '{0}'" , providerConfig . Type ) ) ;
if ( providerType . IsAssignableFrom ( typeof ( IFileSystem ) ) )
throw new InvalidOperationException ( string . Format ( "The type '{0}' does not implement IFileSystem" , providerConfig . Type ) ) ;
var paramCount = providerConfig . Parameters ! = null ? providerConfig . Parameters . Count : 0 ;
var constructor = providerType . GetConstructors ( )
. SingleOrDefault ( x = > x . GetParameters ( ) . Count ( ) = = paramCount
& & x . GetParameters ( ) . All ( y = > providerConfig . Parameters . AllKeys . Contains ( y . Name ) ) ) ;
if ( constructor = = null )
throw new InvalidOperationException ( string . Format ( "Could not find constructor for type '{0}' which accepts {1} parameters" , providerConfig . Type , paramCount ) ) ;
var parameters = new object [ paramCount ] ;
for ( var i = 0 ; i < paramCount ; i + + )
parameters [ i ] = providerConfig . Parameters [ providerConfig . Parameters . AllKeys [ i ] ] . Value ;
//return the new constructor info class to cache so we don't have to do this again.
return new ProviderConstructionInfo ( )
{
Constructor = constructor ,
Parameters = parameters ,
ProviderAlias = s
} ;
} ) ;
var fs = ( IFileSystem ) ctorInfo . Constructor . Invoke ( ctorInfo . Parameters ) ;
return fs ;
2012-08-13 13:26:06 -01:00
}
2012-08-20 09:42:32 -01:00
2013-05-14 14:18:41 -10:00
/// <summary>
/// Returns the strongly typed file system provider
/// </summary>
/// <typeparam name="TProviderTypeFilter"></typeparam>
/// <returns></returns>
2012-08-20 09:42:32 -01:00
public TProviderTypeFilter GetFileSystemProvider < TProviderTypeFilter > ( )
2012-11-07 09:30:40 +05:00
where TProviderTypeFilter : FileSystemWrapper
2012-08-20 09:42:32 -01:00
{
2012-11-07 09:30:40 +05:00
//get the alias for the type from cache or look it up and add it to the cache, then we don't have to reflect each time
var alias = _wrappedProviderLookup . GetOrAdd ( typeof ( TProviderTypeFilter ) , fsType = >
{
//validate the ctor
var constructor = fsType . GetConstructors ( )
. SingleOrDefault ( x = >
x . GetParameters ( ) . Count ( ) = = 1 & & TypeHelper . IsTypeAssignableFrom < IFileSystem > ( x . GetParameters ( ) . Single ( ) . ParameterType ) ) ;
if ( constructor = = null )
throw new InvalidOperationException ( "The type of " + fsType + " must inherit from FileSystemWrapper and must have a constructor that accepts one parameter of type " + typeof ( IFileSystem ) ) ;
var attr =
( FileSystemProviderAttribute ) fsType . GetCustomAttributes ( typeof ( FileSystemProviderAttribute ) , false ) .
SingleOrDefault ( ) ;
if ( attr = = null )
throw new InvalidOperationException ( string . Format ( "The provider type filter '{0}' is missing the required FileSystemProviderAttribute" , typeof ( FileSystemProviderAttribute ) . FullName ) ) ;
return attr . Alias ;
} ) ;
2013-05-14 14:18:41 -10:00
var innerFs = GetUnderlyingFileSystemProvider ( alias ) ;
2012-11-07 09:30:40 +05:00
var outputFs = Activator . CreateInstance ( typeof ( TProviderTypeFilter ) , innerFs ) ;
return ( TProviderTypeFilter ) outputFs ;
2012-08-20 09:42:32 -01:00
}
2012-08-13 13:26:06 -01:00
}
}