using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using Umbraco.Core.Logging;
using umbraco.interfaces;
namespace Umbraco.Core
{
///
/// Used to resolve all plugin types and cache them and is also used to instantiate plugin types
///
///
///
/// 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
///
///
internal class PluginManager
{
internal PluginManager()
{
}
static PluginManager _resolver;
static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
///
/// We will ensure that no matter what, only one of these is created, this is to ensure that caching always takes place
///
///
/// The setter is generally only used for unit tests
///
internal static PluginManager Current
{
get
{
using (var l = new UpgradeableReadLock(Lock))
{
if (_resolver == null)
{
l.UpgradeToWriteLock();
_resolver = new PluginManager();
}
return _resolver;
}
}
set { _resolver = value; }
}
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private readonly HashSet _types = new HashSet();
private IEnumerable _assemblies;
///
/// Returns all classes attributed with XsltExtensionAttribute attribute
///
///
internal IEnumerable ResolveCacheRefreshers()
{
return ResolveTypes();
}
///
/// Returns all available IDataType in application
///
///
internal IEnumerable ResolveDataTypes()
{
return ResolveTypes();
}
///
/// Returns all available IMacroGuiRendering in application
///
///
internal IEnumerable ResolveMacroRenderings()
{
return ResolveTypes();
}
///
/// Returns all available IPackageAction in application
///
///
internal IEnumerable ResolvePackageActions()
{
return ResolveTypes();
}
///
/// Returns all available IAction in application
///
///
internal IEnumerable ResolveActions()
{
return ResolveTypes();
}
///
/// 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.
///
internal IEnumerable AssembliesToScan
{
get { return _assemblies ?? (_assemblies = TypeFinder.GetAssembliesWithKnownExclusions()); }
set { _assemblies = value; }
}
///
/// Used to resolve and create instances of the specified type based on the resolved/cached plugin types
///
///
/// set to true if an exception is to be thrown if there is an error during instantiation
///
internal IEnumerable FindAndCreateInstances(bool throwException = false)
{
var types = ResolveTypes();
return CreateInstances(types, throwException);
}
///
/// Used to create instances of the specified type based on the resolved/cached plugin types
///
///
///
/// set to true if an exception is to be thrown if there is an error during instantiation
///
internal IEnumerable CreateInstances(IEnumerable types, bool throwException = false)
{
var typesAsArray = types.ToArray();
using (DisposableTimer.DebugDuration(
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)))
{
var instances = new List();
foreach (var t in typesAsArray)
{
try
{
var typeInstance = (T)Activator.CreateInstance(t);
instances.Add(typeInstance);
}
catch (Exception ex)
{
LogHelper.Error(String.Format("Error creating type {0}", t.FullName), ex);
if (throwException)
{
throw ex;
}
}
}
return instances;
}
}
///
/// Used to create an instance of the specified type based on the resolved/cached plugin types
///
///
///
///
///
internal T CreateInstance(Type type, bool throwException = false)
{
var instances = CreateInstances(new[] { type }, throwException);
return instances.FirstOrDefault();
}
private IEnumerable ResolveTypes(Func> finder, bool typeIsAttribute = false)
{
using (var readLock = new UpgradeableReadLock(_lock))
{
using (DisposableTimer.TraceDuration(
String.Format("Starting resolution types of {0}", typeof(T).FullName),
String.Format("Completed resolution of types of {0}", typeof(T).FullName)))
{
//check if the TypeList already exists, if so return it, if not we'll create it
var typeList = _types.SingleOrDefault(x => x.GetListType().IsType());
if (typeList == null)
{
//upgrade to a write lock since we're adding to the collection
readLock.UpgradeToWriteLock();
typeList = new TypeList(typeIsAttribute);
foreach (var t in finder())
{
typeList.AddType(t);
}
//add the type list to the collection
_types.Add(typeList);
}
return typeList.GetTypes();
}
}
}
///
/// Generic method to find the specified type and cache the result
///
///
///
internal IEnumerable ResolveTypes()
{
return ResolveTypes(() => TypeFinder.FindClassesOfType(AssembliesToScan));
}
///
/// Generic method to find the specified type that has an attribute and cache the result
///
///
///
///
internal IEnumerable ResolveTypesWithAttribute()
where TAttribute : Attribute
{
return ResolveTypes(() => TypeFinder.FindClassesOfTypeWithAttribute(AssembliesToScan));
}
///
/// Generic method to find any type that has the specified attribute
///
///
///
internal IEnumerable ResolveAttributedTypes()
where TAttribute : Attribute
{
return ResolveTypes(
() => TypeFinder.FindClassesWithAttribute(AssembliesToScan),
true);
}
///
/// Used for unit tests
///
///
internal HashSet GetTypeLists()
{
return _types;
}
#region Private classes
internal abstract class TypeList
{
public abstract void AddType(Type t);
public abstract Type GetListType();
public abstract IEnumerable GetTypes();
}
internal class TypeList : TypeList
{
private readonly bool _typeIsAttribute;
public TypeList(bool typeIsAttribute = false)
{
_typeIsAttribute = typeIsAttribute;
}
private readonly List _types = new List();
public override void AddType(Type t)
{
//if the type is an attribute type we won't do the type check
if (_typeIsAttribute || t.IsType())
{
_types.Add(t);
}
}
public override Type GetListType()
{
return typeof(T);
}
public override IEnumerable GetTypes()
{
return _types;
}
}
#endregion
}
}