using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Umbraco.Core.ObjectResolution
{
///
/// The base class for old legacy factories such as the DataTypeFactory or CacheResolverFactory.
///
/// The type of the concrete resolver class.
/// The type of the resolved objects.
///
/// This class contains basic functionality to mimic the functionality in these old factories since they all return
/// transient objects (though this should be changed) and the method GetById needs to lookup a type to an ID and since
/// these old classes don't contain metadata, the objects need to be instantiated first to get their metadata, we then store this
/// for use in the GetById method.
///
internal abstract class LegacyTransientObjectsResolver : LazyManyObjectsResolverBase
where TResolved : class
where TResolver : ResolverBase
{
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private ConcurrentDictionary _id2type;
#region Constructors
///
/// Initializes a new instance of the class with an initial list of object types.
///
/// A function returning the list of object types.
///
/// We are creating Transient instances (new instances each time) because this is how the legacy code worked and
/// I don't want to muck anything up by changing them to application based instances.
/// TODO: However, it would make much more sense to do this and would speed up the application plus this would make the GetById method much easier.
///
protected LegacyTransientObjectsResolver(Func> value)
: base(value, ObjectLifetimeScope.Transient) // new objects every time
{ }
#endregion
///
/// Returns the unique identifier of the type of a specified object.
///
/// The object.
/// The unique identifier of the type of .
protected abstract Guid GetUniqueIdentifier(TResolved value);
///
/// Returns a new instance for the type identified by its unique type identifier.
///
/// The type identifier.
/// The value of the type uniquely identified by .
public TResolved GetById(Guid id)
{
EnsureIsInitialized();
return !_id2type.ContainsKey(id)
? null
: PluginManager.Current.CreateInstance(_id2type[id]);
}
///
/// Populates the identifiers-to-types dictionnary.
///
///
/// This allow us to instantiate a type by ID since these legacy types doesn't contain any metadata.
/// We instanciate all types once to get their unique identifier, then build the dictionary so that
/// when GetById is called, we can instanciate a single object.
///
protected void EnsureIsInitialized()
{
using (var l = new UpgradeableReadLock(_lock))
{
if (_id2type == null)
{
l.UpgradeToWriteLock();
_id2type = new ConcurrentDictionary();
foreach (var value in Values)
{
_id2type.TryAdd(GetUniqueIdentifier(value), value.GetType());
}
}
}
}
}
}