using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Reflection; using System.Text; using Umbraco.Core.CodeAnnotations; using Umbraco.Core.Configuration; namespace Umbraco.Core.IO { public 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 internal FileSystemProviderManager() { _config = (FileSystemProvidersSection)ConfigurationManager.GetSection("umbracoConfiguration/FileSystemProviders"); } #endregion /// /// used to cache the lookup of how to construct this object so we don't have to reflect each time. /// private class ProviderConstructionInfo { public object[] Parameters { get; set; } public ConstructorInfo Constructor { get; set; } public string ProviderAlias { get; set; } } private readonly ConcurrentDictionary _providerLookup = new ConcurrentDictionary(); private readonly ConcurrentDictionary _wrappedProviderLookup = new ConcurrentDictionary(); /// /// Returns the underlying (non-typed) file system provider for the alias specified /// /// /// /// /// It is recommended to use the typed GetFileSystemProvider method instead to get a strongly typed provider instance. /// public IFileSystem GetUnderlyingFileSystemProvider(string alias) { //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; } /// /// Returns the strongly typed file system provider /// /// /// public TProviderTypeFilter GetFileSystemProvider() where TProviderTypeFilter : FileSystemWrapper { //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(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; }); var innerFs = GetUnderlyingFileSystemProvider(alias); var outputFs = Activator.CreateInstance(typeof (TProviderTypeFilter), innerFs); return (TProviderTypeFilter)outputFs; } } }