using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Umbraco.Core.Models.PublishedContent
{
///
/// Implements a strongly typed content model factory
///
public class PublishedContentModelFactory : IPublishedContentModelFactory
{
//private readonly Dictionary _constructors
// = new Dictionary();
private readonly Dictionary> _constructors;
///
/// Initializes a new instance of the class with types.
///
/// The model types.
///
/// Types must implement IPublishedContent and have a unique constructor that
/// accepts one IPublishedContent as a parameter.
/// To activate,
///
/// var types = TypeLoader.Current.GetTypes{PublishedContentModel}();
/// var factory = new PublishedContentModelFactoryImpl(types);
/// PublishedContentModelFactoryResolver.Current.SetFactory(factory);
///
///
public PublishedContentModelFactory(IEnumerable types)
{
var ctorArgTypes = new[] { typeof(IPublishedContent) };
var constructors = new Dictionary>(StringComparer.InvariantCultureIgnoreCase);
foreach (var type in types)
{
var constructor = type.GetConstructor(ctorArgTypes);
if (constructor == null)
throw new InvalidOperationException($"Type {type.FullName} is missing a public constructor with one argument of type IPublishedContent.");
var attribute = type.GetCustomAttribute(false);
var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias;
if (constructors.ContainsKey(typeName))
throw new InvalidOperationException($"More that one type want to be a model for content type {typeName}.");
// should work everywhere, but slow
//_constructors[typeName] = constructor;
// much faster with a dynamic method but potential MediumTrust issues
// here http://stackoverflow.com/questions/16363838/how-do-you-call-a-constructor-via-an-expression-tree-on-an-existing-object
// fast enough and works in MediumTrust
// read http://boxbinary.com/2011/10/how-to-run-a-unit-test-in-medium-trust-with-nunitpart-three-umbraco-framework-testing/
var exprArg = Expression.Parameter(typeof(IPublishedContent), "content");
var exprNew = Expression.New(constructor, exprArg);
var expr = Expression.Lambda>(exprNew, exprArg);
var func = expr.Compile();
constructors[typeName] = func;
}
_constructors = constructors.Count > 0 ? constructors : null;
}
public IPublishedContent CreateModel(IPublishedContent content)
{
// fail fast
if (_constructors == null)
return content;
// be case-insensitive
var contentTypeAlias = content.DocumentTypeAlias;
//ConstructorInfo constructor;
//return _constructors.TryGetValue(contentTypeAlias, out constructor)
// ? (IPublishedContent) constructor.Invoke(new object[] { content })
// : content;
Func constructor;
return _constructors.TryGetValue(contentTypeAlias, out constructor)
? constructor(content)
: content;
}
public T CreateModel(IPublishedFragment content)
{
throw new NotImplementedException();
}
}
}