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 ;
using Umbraco.Core.Configuration ;
namespace Umbraco.Core.IO
{
internal class FileSystemProviderManager
{
private readonly FileSystemProvidersSection _config ;
#region Singleton
private static readonly FileSystemProviderManager Instance = new FileSystemProviderManager ();
public static FileSystemProviderManager Current
{
get { return Instance ; }
}
#endregion
#region Constructors
public FileSystemProviderManager ()
{
_config = ( FileSystemProvidersSection ) ConfigurationManager . GetSection ( "FileSystemProviders" );
}
#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 ; }
}
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
public IFileSystem GetFileSystemProvider ( 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 ));
2012-08-13 13:26:06 -01:00
2012-11-07 09:30:40 +05:00
var providerType = Type . GetType ( providerConfig . Type );
if ( providerType == null )
throw new InvalidOperationException ( string . Format ( "Could not find type '{0}'" , providerConfig . Type ));
2012-08-13 13:26:06 -01:00
2012-11-07 09:30:40 +05:00
if ( providerType . IsAssignableFrom ( typeof ( IFileSystem )))
throw new InvalidOperationException ( string . Format ( "The type '{0}' does not implement IFileSystem" , providerConfig . Type ));
2012-08-13 13:26:06 -01:00
2012-11-07 09:30:40 +05:00
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 ));
2012-08-13 13:26:06 -01:00
2012-11-07 09:30:40 +05:00
var parameters = new object [ paramCount ];
for ( var i = 0 ; i < paramCount ; i ++)
parameters [ i ] = providerConfig . Parameters [ providerConfig . Parameters . AllKeys [ i ]]. Value ;
2012-08-13 13:26:06 -01:00
2012-11-07 09:30:40 +05:00
//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
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 ));
2012-08-20 09:42:32 -01:00
2012-11-07 09:30:40 +05:00
var attr =
( FileSystemProviderAttribute ) fsType . GetCustomAttributes ( typeof ( FileSystemProviderAttribute ), false ).
SingleOrDefault ();
2012-08-20 09:42:32 -01:00
2012-11-07 09:30:40 +05:00
if ( attr == null )
throw new InvalidOperationException ( string . Format ( "The provider type filter '{0}' is missing the required FileSystemProviderAttribute" , typeof ( FileSystemProviderAttribute ). FullName ));
return attr . Alias ;
});
var innerFs = GetFileSystemProvider ( alias );
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
}
}