Removed old MacroControlFactory - marked as internal FTW ! moved it to new MacroFieldEditorsResolver

using the new framework. Also moved PersistableMacroProperty to new assembly (it was also marked as internal).
Updated unit tests to work with Resolution and resetting resolvers.
This commit is contained in:
shannon@ShandemVaio
2012-08-01 22:46:13 +06:00
parent 646e96ab15
commit 2115146fdb
18 changed files with 192 additions and 454 deletions

View File

@@ -6,54 +6,6 @@ using umbraco.interfaces;
namespace Umbraco.Core
{
///// <summary>
///// A resolver to return all ICacheRefresher objects
///// </summary>
//internal sealed class CacheRefreshersResolver : LegacyTransientObjectsResolver<ICacheRefresher>
//{
// #region Singleton
// private static readonly CacheRefreshersResolver Instance = new CacheRefreshersResolver(PluginTypeResolver.Current.ResolveCacheRefreshers());
// public static CacheRefreshersResolver Current
// {
// get { return Instance; }
// }
// #endregion
// #region Constructors
// static CacheRefreshersResolver() { }
// /// <summary>
// /// Constructor
// /// </summary>
// /// <param name="refreshers"></param>
// internal CacheRefreshersResolver(IEnumerable<Type> refreshers)
// : base(refreshers)
// {
// }
// #endregion
// /// <summary>
// /// Gets the <see cref="ICacheRefresher"/> implementations.
// /// </summary>
// public IEnumerable<ICacheRefresher> CacheResolvers
// {
// get
// {
// EnsureRefreshersList();
// return Values;
// }
// }
// protected override Guid GetUniqueIdentifier(ICacheRefresher obj)
// {
// return obj.UniqueIdentifier;
// }
//}
/// <summary>
/// A resolver to return all ICacheRefresher objects
/// </summary>

View File

@@ -40,7 +40,10 @@ namespace Umbraco.Core
/// <returns></returns>
public virtual IBootManager Startup(Action<ApplicationContext> afterStartup)
{
afterStartup(ApplicationContext.Current);
if (afterStartup != null)
{
afterStartup(ApplicationContext.Current);
}
return this;
}
@@ -57,7 +60,11 @@ namespace Umbraco.Core
//stop the timer and log the output
_timer.Dispose();
afterComplete(ApplicationContext.Current);
if (afterComplete != null)
{
afterComplete(ApplicationContext.Current);
}
return this;
}
@@ -71,6 +78,9 @@ namespace Umbraco.Core
DataTypesResolver.Current = new DataTypesResolver(
PluginManager.Current.ResolveDataTypes());
MacroFieldEditorsResolver.Current = new MacroFieldEditorsResolver(
PluginManager.Current.ResolveMacroRenderings());
}
}
}

View File

@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Web.UI;
using Umbraco.Core.Macros;
using Umbraco.Core.Resolving;
using umbraco.interfaces;
namespace Umbraco.Core
{
/// <summary>
/// A resolver to return all IMacroGuiRendering objects
/// </summary>
/// <remarks>
/// Much of this classes methods are based on legacy code from umbraco.editorControls.macrocontainer.MacroControlFactory
/// this code should probably be reviewed and cleaned up if necessary.
/// </remarks>
internal sealed class MacroFieldEditorsResolver : ManyObjectsResolverBase<MacroFieldEditorsResolver, IMacroGuiRendering>
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="macroEditors"></param>
internal MacroFieldEditorsResolver(IEnumerable<Type> macroEditors)
: base(macroEditors, ObjectLifetimeScope.Transient)
{
}
/// <summary>
/// Gets the <see cref="IMacroGuiRendering"/> implementations.
/// </summary>
public IEnumerable<IMacroGuiRendering> MacroFieldEditors
{
get
{
return Values;
}
}
/// <summary>
/// Gets the value based on the type of control
/// </summary>
/// <param name="macroControl"></param>
/// <returns></returns>
/// <remarks>
/// This is legacy code migrated from umbraco.editorControls.macrocontainer.MacroControlFactory
/// </remarks>
internal string GetValueFromMacroControl(Control macroControl)
{
return ((IMacroGuiRendering)macroControl).Value;
}
/// <remarks>
/// This is legacy code migrated from umbraco.editorControls.macrocontainer.MacroControlFactory
/// </remarks>
internal List<Type> MacroControlTypes
{
get { return InstanceTypes; }
}
/// <summary>
/// Create an instance of a Macro control and return it.
/// Because the macro control uses inline client script whichs is not generated after postback
/// That's why we use the Page Picker instead of the content picker of the macro.
/// </summary>
/// <remarks>
/// This is legacy code migrated from umbraco.editorControls.macrocontainer.MacroControlFactory
/// </remarks>
internal Control GetMacroRenderControlByType(PersistableMacroProperty prop, string uniqueId)
{
var m = MacroControlTypes.FindLast(macroGuiCcontrol => macroGuiCcontrol.ToString() == string.Format("{0}.{1}", prop.AssemblyName, prop.TypeName));
var instance = PluginManager.Current.CreateInstance<IMacroGuiRendering>(m);
if (instance != null)
{
if (!string.IsNullOrEmpty(prop.Value))
{
instance.Value = prop.Value;
}
var macroControl = instance as Control;
if (macroControl != null)
{
macroControl.ID = uniqueId;
return macroControl;
}
}
return null;
}
}
}

View File

@@ -1,10 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace umbraco.editorControls.macrocontainer
namespace Umbraco.Core.Macros
{
/// <summary>
/// NOTE: This is legacy code, might require a cleanup
/// </summary>
[Serializable]
internal class PersistableMacroProperty
{

View File

@@ -76,6 +76,15 @@ namespace Umbraco.Core
return ResolveTypes<IDataType>();
}
/// <summary>
/// Returns all available IMacroGuiRendering in application
/// </summary>
/// <returns></returns>
internal IEnumerable<Type> ResolveMacroRenderings()
{
return ResolveTypes<IMacroGuiRendering>();
}
/// <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.

View File

@@ -1,268 +0,0 @@
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
{
/// <summary>
/// Used to resolve all plugin types and cache them
/// </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
///
/// </remarks>
internal class PluginTypeResolver
{
internal PluginTypeResolver()
{
}
static PluginTypeResolver _resolver;
static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
/// <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 PluginTypeResolver Current
{
get
{
using (var l = new UpgradeableReadLock(Lock))
{
if (_resolver == null)
{
l.UpgradeToWriteLock();
_resolver = new PluginTypeResolver();
}
return _resolver;
}
}
set { _resolver = value; }
}
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>();
}
/// <summary>
/// Returns all available IDataType in application
/// </summary>
/// <returns></returns>
internal IEnumerable<Type> ResolveDataTypes()
{
return ResolveTypes<IDataType>();
}
/// <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
{
get { return _assemblies ?? (_assemblies = TypeFinder2.GetAssembliesWithKnownExclusions()); }
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)
{
var typesAsArray = types.ToArray();
using (DisposableTimer.DebugDuration<PluginTypeResolver>(
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<T>();
foreach (var t in typesAsArray)
{
try
{
var typeInstance = (T)Activator.CreateInstance(t);
instances.Add(typeInstance);
}
catch (Exception ex)
{
LogHelper.Error<PluginTypeResolver>(String.Format("Error creating type {0}", t.FullName), ex);
if (throwException)
{
throw ex;
}
}
}
return instances;
}
}
/// <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();
}
private IEnumerable<Type> ResolveTypes<T>(Func<IEnumerable<Type>> finder, bool typeIsAttribute = false)
{
using (var readLock = new UpgradeableReadLock(_lock))
{
using (DisposableTimer.TraceDuration<PluginTypeResolver>(
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<T>());
if (typeList == null)
{
//upgrade to a write lock since we're adding to the collection
readLock.UpgradeToWriteLock();
typeList = new TypeList<T>(typeIsAttribute);
foreach (var t in finder())
{
typeList.AddType(t);
}
//add the type list to the collection
_types.Add(typeList);
}
return typeList.GetTypes();
}
}
}
/// <summary>
/// Generic method to find the specified type and cache the result
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
internal IEnumerable<Type> ResolveTypes<T>()
{
return ResolveTypes<T>(() => TypeFinder2.FindClassesOfType<T>(AssembliesToScan));
}
/// <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>
internal IEnumerable<Type> ResolveTypesWithAttribute<T, TAttribute>()
where TAttribute : Attribute
{
return ResolveTypes<T>(() => TypeFinder2.FindClassesOfTypeWithAttribute<T, TAttribute>(AssembliesToScan));
}
/// <summary>
/// Generic method to find any type that has the specified attribute
/// </summary>
/// <typeparam name="TAttribute"></typeparam>
/// <returns></returns>
internal IEnumerable<Type> ResolveAttributedTypes<TAttribute>()
where TAttribute : Attribute
{
return ResolveTypes<TAttribute>(
() => TypeFinder2.FindClassesWithAttribute<TAttribute>(AssembliesToScan),
true);
}
/// <summary>
/// Used for unit tests
/// </summary>
/// <returns></returns>
internal HashSet<TypeList> GetTypeLists()
{
return _types;
}
#region Private classes
internal abstract class TypeList
{
public abstract void AddType(Type t);
public abstract Type GetListType();
public abstract IEnumerable<Type> GetTypes();
}
internal class TypeList<T> : TypeList
{
private readonly bool _typeIsAttribute;
public TypeList(bool typeIsAttribute = false)
{
_typeIsAttribute = typeIsAttribute;
}
private readonly List<Type> _types = new List<Type>();
public override void AddType(Type t)
{
//if the type is an attribute type we won't do the type check
if (_typeIsAttribute || t.IsType<T>())
{
_types.Add(t);
}
}
public override Type GetListType()
{
return typeof(T);
}
public override IEnumerable<Type> GetTypes()
{
return _types;
}
}
#endregion
}
}

View File

@@ -14,7 +14,13 @@ namespace Umbraco.Core.Resolving
{
public static event EventHandler Frozen;
public static bool IsFrozen { get; private set; }
/// <summary>
/// Gets a value indicating that resolution is frozen
/// </summary>
/// <remarks>
/// The internal setter is normally used for unit tests
/// </remarks>
public static bool IsFrozen { get; internal set; }
public static void EnsureNotFrozen()
{

View File

@@ -11,9 +11,14 @@ namespace Umbraco.Core.Resolving
where TResolver : class
{
static TResolver _resolver;
//TODO: This is not correct, this will be the same lock for all ResolverBase classes!!
// this will work i suppose but not really ideal
/// <summary>
/// The lock for the singleton
/// </summary>
/// <remarks>
/// Though resharper says this is in error, it is actually correct. We want a different lock object for each generic type.
/// See this for details: http://confluence.jetbrains.net/display/ReSharper/Static+field+in+generic+type
/// </remarks>
static readonly ReaderWriterLockSlim ResolversLock = new ReaderWriterLockSlim();
public static TResolver Current
@@ -40,5 +45,13 @@ namespace Umbraco.Core.Resolving
}
}
}
/// <summary>
/// used in unit tests to reset current to null
/// </summary>
internal static void Reset()
{
_resolver = null;
}
}
}

View File

@@ -70,6 +70,8 @@
<Compile Include="Logging\AsynchronousRollingFileAppender.cs" />
<Compile Include="Logging\LoggingTaskExtension.cs" />
<Compile Include="Logging\LogHelper.cs" />
<Compile Include="MacroFieldEditorsResolver.cs" />
<Compile Include="Macros\PersistableMacroProperty.cs" />
<Compile Include="ObjectExtensions.cs" />
<Compile Include="RazorDataTypeModelStaticMappingItem.cs" />
<Compile Include="Resolving\ManyObjectsResolverBase.cs" />

View File

@@ -2,6 +2,7 @@ using System;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Resolving;
using Umbraco.Tests.TestHelpers;
using umbraco.interfaces;
@@ -23,6 +24,18 @@ namespace Umbraco.Tests
{
this.GetType().Assembly
};
CacheRefreshersResolver.Current = new CacheRefreshersResolver(
PluginManager.Current.ResolveCacheRefreshers());
Resolution.Freeze();
}
[TearDown]
public void TearDown()
{
CacheRefreshersResolver.Reset();
Resolution.IsFrozen = false;
}
[Test]

View File

@@ -14,7 +14,14 @@ namespace Umbraco.Tests
{
[TestFixture]
public class ManyObjectResolverTests
{
{
[SetUp]
public void Initialize()
{
//reset each test
Resolution.IsFrozen = false;
}
[Test]
public void Ensure_Transient_Object_Creation()
@@ -22,6 +29,8 @@ namespace Umbraco.Tests
var resolver = new TransientObjectsResolver();
resolver.AddType<TransientObject>();
Resolution.Freeze();
var instances1 = resolver.Objects;
var instances2 = resolver.Objects;
@@ -34,6 +43,8 @@ namespace Umbraco.Tests
var resolver = new ApplicationObjectsResolver();
resolver.AddType<TransientObject>();
Resolution.Freeze();
var instances1 = resolver.Objects;
var instances2 = resolver.Objects;
@@ -48,6 +59,8 @@ namespace Umbraco.Tests
var resolver = new HttpRequestObjectsResolver(httpContextFactory.HttpContext);
resolver.AddType<TransientObject>();
Resolution.Freeze();
var instances1 = resolver.Objects;
var instances2 = resolver.Objects;

View File

@@ -1,6 +1,7 @@
using System;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Resolving;
using Umbraco.Tests.TestHelpers;
using umbraco.cms.businesslogic.datatype;
using umbraco.interfaces;
@@ -24,6 +25,18 @@ namespace Umbraco.Tests
{
this.GetType().Assembly
};
DataTypesResolver.Current = new DataTypesResolver(
PluginManager.Current.ResolveDataTypes());
Resolution.Freeze();
}
[TearDown]
public void TearDown()
{
DataTypesResolver.Reset();
Resolution.IsFrozen = false;
}
[Test]

View File

@@ -10,7 +10,7 @@ using umbraco.interfaces;
namespace Umbraco.Tests
{
[TestFixture]
public class MacroControlFactoryTests
public class MacroFieldEditorsResolverTests
{
[SetUp]
public void Initialize()
@@ -25,12 +25,15 @@ namespace Umbraco.Tests
{
this.GetType().Assembly
};
MacroFieldEditorsResolver.Current = new MacroFieldEditorsResolver(
PluginManager.Current.ResolveMacroRenderings());
}
[Test]
public void Find_Types()
{
var found = MacroControlFactory.MacroControlTypes;
var found = MacroFieldEditorsResolver.Current.MacroControlTypes;
Assert.AreEqual(2, found.Count());
}

View File

@@ -59,7 +59,7 @@
<Compile Include="CacheRefresherFactoryTests.cs" />
<Compile Include="ContentStoreTests.cs" />
<Compile Include="DataTypeFactoryTests.cs" />
<Compile Include="MacroControlFactoryTests.cs" />
<Compile Include="MacroFieldEditorsResolverTests.cs" />
<Compile Include="MacroEngineFactoryTests.cs" />
<Compile Include="MediaFactoryTests.cs" />
<Compile Include="PackageActionFactoryTests.cs" />

View File

@@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using Umbraco.Core;
using umbraco.BusinessLogic.Actions;
using umbraco.cms.businesslogic.macro;
using umbraco.cms.businesslogic.media;
using umbraco.interfaces;
namespace umbraco.editorControls
{
/// <summary>
/// Extension methods for the PluginTypeResolver
/// </summary>
public static class PluginManagerExtensions
{
/// <summary>
/// Returns all available IMacroGuiRendering in application
/// </summary>
/// <param name="resolver"></param>
/// <returns></returns>
internal static IEnumerable<Type> ResolveMacroRenderings(this PluginManager resolver)
{
return resolver.ResolveTypes<IMacroGuiRendering>();
}
}
}

View File

@@ -1,90 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web.UI;
using Umbraco.Core;
using umbraco.BusinessLogic.Utils;
using umbraco.interfaces;
using umbraco.editorControls;
namespace umbraco.editorControls.macrocontainer
{
internal class MacroControlFactory
{
/// <summary>
/// All Possible Macro types
/// </summary>
private static List<Type> _macroControlTypes = null;
private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
#region Methods
/// <summary>
/// Create an instance of a Macro control and return it.
/// Because the macro control uses inline client script whichs is not generated after postback
/// That's why we use the Page Picker instead of the content picker of the macro.
/// </summary>
internal static Control GetMacroRenderControlByType(PersistableMacroProperty prop, string uniqueID)
{
var m = MacroControlTypes.FindLast(macroGuiCcontrol => macroGuiCcontrol.ToString() == string.Format("{0}.{1}", prop.AssemblyName, prop.TypeName));
var instance = PluginManager.Current.CreateInstance<IMacroGuiRendering>(m);
if (instance != null)
{
if (!string.IsNullOrEmpty(prop.Value))
{
instance.Value = prop.Value;
}
var macroControl = instance as Control;
if (macroControl != null)
{
macroControl.ID = uniqueID;
return macroControl;
}
}
return null;
}
/// <summary>
/// Gets the value based on the type of control
/// </summary>
/// <param name="macroControl"></param>
/// <returns></returns>
internal static string GetValueFromMacroControl(Control macroControl)
{
return ((IMacroGuiRendering)macroControl).Value;
}
#endregion
#region Properties
/// <summary>
/// All Possible Macro types
/// </summary>
internal static List<Type> MacroControlTypes
{
get
{
using (var readLock = new UpgradeableReadLock(Lock))
{
if (_macroControlTypes == null || !_macroControlTypes.Any())
{
readLock.UpgradeToWriteLock();
_macroControlTypes = new List<Type>(PluginManager.Current.ResolveMacroRenderings());
}
return _macroControlTypes;
}
}
}
#endregion
}
}

View File

@@ -4,6 +4,8 @@ using System.Linq;
using System.Text;
using System.Web.UI.WebControls;
using System.Web.UI;
using Umbraco.Core;
using Umbraco.Core.Macros;
using umbraco.cms.businesslogic.macro;
using System.Collections;
using umbraco.presentation;
@@ -236,7 +238,7 @@ namespace umbraco.editorControls.macrocontainer
caption.Text = prop.Name;
//Get the MacroControl
Control macroControl = MacroControlFactory.GetMacroRenderControlByType(prop, ID + "_" + prop.Alias);
Control macroControl = MacroFieldEditorsResolver.Current.GetMacroRenderControlByType(prop, ID + "_" + prop.Alias);
AddFormRow(caption, macroControl);
@@ -295,7 +297,7 @@ namespace umbraco.editorControls.macrocontainer
{
//Make sure we find the correct Unique ID
string ControlIdToFind = ID + "_" + prop.Alias;
string value = MacroControlFactory.GetValueFromMacroControl(_formTable.FindControl(ControlIdToFind));
string value = MacroFieldEditorsResolver.Current.GetValueFromMacroControl(_formTable.FindControl(ControlIdToFind));
sb.AppendFormat(" {0}=\"{1}\" ", prop.Alias, value);
}
sb.Append(" />");

View File

@@ -176,7 +176,6 @@
<Compile Include="AbstractJsonPrevalueEditor.cs" />
<Compile Include="AbstractOptions.cs" />
<Compile Include="AbstractPrevalueEditor.cs" />
<Compile Include="PluginManagerExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
@@ -267,9 +266,7 @@
<Compile Include="macrocontainer\DataType.cs" />
<Compile Include="macrocontainer\Editor.cs" />
<Compile Include="macrocontainer\MacroContainerEvent.cs" />
<Compile Include="macrocontainer\MacroControlFactory.cs" />
<Compile Include="macrocontainer\MacroEditor.cs" />
<Compile Include="macrocontainer\PersistableMacroProperty.cs" />
<Compile Include="macrocontainer\PrevalueEditor.cs" />
<Compile Include="mediapicker\mediaChooser.cs">
<SubType>Code</SubType>