Implementing shape binding abstractions and built-in strategies

--HG--
branch : mvc3p1
This commit is contained in:
Louis DeJardin
2010-08-27 17:47:27 -07:00
parent 12a6b63840
commit 7e10718e12
32 changed files with 545 additions and 190 deletions

View File

@@ -1,39 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autofac;
using NUnit.Framework;
using Orchard.DisplayManagement;
namespace Orchard.Tests.DisplayManagement {
[TestFixture]
public class DefaultShapeTableFactoryTests {
static IShapeTableFactory CreateShapeTableFactory(Action<ContainerBuilder> config) {
var builder = new ContainerBuilder();
builder.RegisterType<DefaultShapeTableFactory>().As<IShapeTableFactory>();
config(builder);
var container = builder.Build();
return container.Resolve<IShapeTableFactory>();
}
[Test]
public void ShapeTableRecognizesMethodNames() {
var stf = CreateShapeTableFactory(cfg => cfg.RegisterType<Test>().As<IShapeDriver>());
var shapeTable = stf.CreateShapeTable();
Assert.That(shapeTable.Entries.Count(), Is.EqualTo(2));
Assert.That(shapeTable.Entries.ContainsKey("Pager"));
Assert.That(shapeTable.Entries.ContainsKey("Email"));
}
public class Test : IShapeDriver {
public void Pager() {
}
public void Email(string text, string address) {
}
}
}
}

View File

@@ -0,0 +1,22 @@
using Autofac;
using NUnit.Framework;
using Orchard.Tests.Utility;
namespace Orchard.Tests.DisplayManagement.Descriptors {
public class ContainerTestBase {
protected IContainer _container;
[SetUp]
public virtual void Init() {
var builder = new ContainerBuilder();
builder.RegisterAutoMocking();
Register(builder);
_container = builder.Build();
Resolve(_container);
}
protected virtual void Register(ContainerBuilder builder) { }
protected virtual void Resolve(IContainer container) { }
}
}

View File

@@ -0,0 +1,24 @@
using System;
using Autofac;
using NUnit.Framework;
using Orchard.DisplayManagement.Descriptors;
namespace Orchard.Tests.DisplayManagement.Descriptors {
[TestFixture]
public class DefaultShapeTableFactoryTests : ContainerTestBase {
private IShapeTableFactory _factory;
protected override void Register(ContainerBuilder builder) {
builder.RegisterType<DefaultShapeTableFactory>().As<IShapeTableFactory>();
}
protected override void Resolve(IContainer container) {
_factory = container.Resolve<IShapeTableFactory>();
}
[Test]
public void FactoryIsResolved() {
Assert.That(_factory, Is.Not.Null);
}
}
}

View File

@@ -0,0 +1,8 @@
using NUnit.Framework;
namespace Orchard.Tests.DisplayManagement.Descriptors {
[TestFixture]
public class DefaultShapeTableManagerTests {
}
}

View File

@@ -0,0 +1,111 @@
using System.Collections.Generic;
using System.Linq;
using Autofac;
using Moq;
using NUnit.Framework;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Descriptors.ShapeAttributeStrategy;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment;
using Orchard.Environment.Extensions.Models;
namespace Orchard.Tests.DisplayManagement.Descriptors {
[TestFixture]
public class ShapeAttributeBindingStrategyTests : ContainerTestBase {
private FeatureDescriptor _testFeature;
protected override void Register(Autofac.ContainerBuilder builder) {
_testFeature = new FeatureDescriptor { Name = "Testing", Extension = new ExtensionDescriptor { Name = "Testing" } };
builder.RegisterType<ShapeAttributeBindingStrategy>().As<IShapeDescriptorBindingStrategy>();
builder.RegisterInstance(new TestProvider()).WithMetadata("Feature", _testFeature);
builder.RegisterModule(new ShapeAttributeBindingModule());
}
protected override void Resolve(IContainer container) {
// implementation resorts to orchard host to resolve "current scope" services
container.Resolve<Mock<IOrchardHostContainer>>()
.Setup(x => x.Resolve<IComponentContext>())
.Returns(container);
}
class TestProvider {
[Shape]
public string Simple() {
return "Simple";
}
[Shape("Renamed")]
public string RenamedMethod() {
return "Renamed";
}
}
private IEnumerable<ShapeDescriptorAlteration> GetInitializers() {
var strategy = _container.Resolve<IShapeDescriptorBindingStrategy>();
var builder = new ShapeTableBuilder();
strategy.Discover(builder);
return builder.Build();
}
[Test]
public void ShapeAttributeOccurrencesAreDetected() {
var occurrences = _container.Resolve<IEnumerable<ShapeAttributeOccurrence>>();
Assert.That(occurrences.Any(o => o.MethodInfo == typeof(TestProvider).GetMethod("Simple")));
}
[Test]
public void InitializersHaveExpectedShapeTypeNames() {
var strategy = _container.Resolve<IShapeDescriptorBindingStrategy>();
var builder = new ShapeTableBuilder();
strategy.Discover(builder);
var initializers = builder.Build();
Assert.That(initializers.Any(i => i.ShapeType == "Simple"));
Assert.That(initializers.Any(i => i.ShapeType == "Renamed"));
Assert.That(initializers.Any(i => i.ShapeType == "RenamedMethod"), Is.False);
}
[Test]
public void FeatureMetadataIsDetected() {
var strategy = _container.Resolve<IShapeDescriptorBindingStrategy>();
var builder = new ShapeTableBuilder();
strategy.Discover(builder);
var initializers = builder.Build();
Assert.That(initializers.All(i => i.Feature == _testFeature));
}
[Test]
public void LifetimeScopeContainersHaveMetadata() {
var strategy = _container.Resolve<IShapeDescriptorBindingStrategy>();
var builder = new ShapeTableBuilder();
strategy.Discover(builder);
var initializers = builder.Build();
Assert.That(initializers.Any(i => i.ShapeType == "Simple"));
var childContainer = _container.BeginLifetimeScope();
var strategy2 = childContainer.Resolve<IShapeDescriptorBindingStrategy>();
var builder2 = new ShapeTableBuilder();
strategy2.Discover(builder2);
var initializers2 = builder2.Build();
Assert.That(initializers2.Any(i => i.ShapeType == "Simple"));
Assert.That(strategy, Is.Not.SameAs(strategy2));
}
[Test]
public void BindingProvidedByStrategyInvokesMethod() {
var initializers = GetInitializers();
var shapeDescriptor = initializers.Where(i => i.ShapeType == "Simple")
.Aggregate(new ShapeDescriptor { ShapeType = "Simple" }, (d, i) => { i.Alter(d); return d; });
var displayContext = new DisplayContext();
var result = shapeDescriptor.Binding(displayContext);
var result2 = shapeDescriptor.Binding.Invoke(displayContext);
Assert.That(result.ToString(), Is.StringContaining("Simple"));
Assert.That(result2.ToString(), Is.StringContaining("Simple"));
}
}
}

View File

@@ -56,7 +56,7 @@ namespace Orchard.Tests.DisplayManagement {
var displayHelperFactory = new DisplayHelperFactory(displayManager.Object, shapeFactory.Object);
var display = (dynamic)displayHelperFactory.CreateHelper(viewContext, null);
var outline = new Shape { Attributes = new ShapeAttributes { Type = "Outline" } };
var outline = new Shape { Metadata = new ShapeMetadata { Type = "Outline" } };
display(outline);
//displayManager.Verify(dm => dm.Execute(outline, viewContext, null));

View File

@@ -25,8 +25,8 @@ namespace Orchard.Tests.DisplayManagement {
public void ShapeHasAttributesType() {
var factory = _container.Resolve<IShapeFactory>();
dynamic foo = factory.Create("Foo", ArgsUtility.Empty());
ShapeAttributes attributes = foo.Attributes;
Assert.That(attributes.Type, Is.EqualTo("Foo"));
ShapeMetadata metadata = foo.Metadata;
Assert.That(metadata.Type, Is.EqualTo("Foo"));
}
[Test]

View File

@@ -28,7 +28,7 @@ namespace Orchard.Tests.DisplayManagement {
var alpha = shape.Alpha();
Assert.That(alpha.Attributes.Type, Is.EqualTo("Alpha"));
Assert.That(alpha.Metadata.Type, Is.EqualTo("Alpha"));
}
[Test]
@@ -37,7 +37,7 @@ namespace Orchard.Tests.DisplayManagement {
var alpha = shape.Alpha(one: 1, two: "dos");
Assert.That(alpha.Attributes.Type, Is.EqualTo("Alpha"));
Assert.That(alpha.Metadata.Type, Is.EqualTo("Alpha"));
Assert.That(alpha.one, Is.EqualTo(1));
Assert.That(alpha.two, Is.EqualTo("dos"));
}
@@ -48,7 +48,7 @@ namespace Orchard.Tests.DisplayManagement {
var alpha = shape.Alpha(new { one = 1, two = "dos" });
Assert.That(alpha.Attributes.Type, Is.EqualTo("Alpha"));
Assert.That(alpha.Metadata.Type, Is.EqualTo("Alpha"));
Assert.That(alpha.one, Is.EqualTo(1));
Assert.That(alpha.two, Is.EqualTo("dos"));
}

View File

@@ -5,10 +5,15 @@ using System.Text;
using System.Web;
using System.Web.Mvc;
using Autofac;
using Moq;
using NUnit.Framework;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Descriptors.ShapeAttributeStrategy;
using Orchard.DisplayManagement.Implementation;
using Orchard.DisplayManagement.Shapes;
using Orchard.Environment;
using Orchard.Tests.Utility;
namespace Orchard.Tests.DisplayManagement {
[TestFixture]
@@ -18,20 +23,28 @@ namespace Orchard.Tests.DisplayManagement {
[SetUp]
public void Init() {
var builder = new ContainerBuilder();
builder.RegisterModule(new ShapeAttributeBindingModule());
builder.RegisterType<ShapeAttributeBindingStrategy>().As<IShapeDescriptorBindingStrategy>();
builder.RegisterType<DefaultDisplayManager>().As<IDisplayManager>();
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<DisplayHelperFactory>().As<IDisplayHelperFactory>();
builder.RegisterType<ShapeHelperFactory>().As<IShapeHelperFactory>();
builder.RegisterType<DefaultShapeTableFactory>().As<IShapeTableFactory>();
builder.RegisterType<SimpleShapes>().As<IShapeDriver>();
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<SimpleShapes>();
builder.RegisterAutoMocking(MockBehavior.Loose);
_container = builder.Build();
_container.Resolve<Mock<IOrchardHostContainer>>()
.Setup(x => x.Resolve<IComponentContext>())
.Returns(_container);
}
public class SimpleShapes : IShapeDriver {
public class SimpleShapes {
[Shape]
public IHtmlString Something() {
return new HtmlString("<br/>");
}
[Shape]
public IHtmlString Pager() {
return new HtmlString("<div>hello</div>");
}

View File

@@ -194,7 +194,10 @@
<Compile Include="Data\StubLocator.cs" />
<Compile Include="DisplayManagement\ArgsUtility.cs" />
<Compile Include="DisplayManagement\DefaultDisplayManagerTests.cs" />
<Compile Include="DisplayManagement\DefaultShapeTableFactoryTests.cs" />
<Compile Include="DisplayManagement\Descriptors\ContainerTestBase.cs" />
<Compile Include="DisplayManagement\Descriptors\DefaultShapeTableFactoryTests.cs" />
<Compile Include="DisplayManagement\Descriptors\DefaultShapeTableManagerTests.cs" />
<Compile Include="DisplayManagement\Descriptors\ShapeAttributeBindingStrategyTests.cs" />
<Compile Include="DisplayManagement\DisplayHelperTests.cs" />
<Compile Include="DisplayManagement\ShapeFactoryTests.cs" />
<Compile Include="DisplayManagement\SubsystemTests.cs" />

View File

@@ -6,19 +6,24 @@ using System.Web.Mvc;
using Orchard.DisplayManagement;
namespace Orchard.DevTools {
public class Shapes : IShapeDriver {
public class Shapes : IDependency {
[Shape]
public IHtmlString Title(dynamic text) {
return new HtmlString("<h2>" + text + "</h2>");
}
[Shape]
public IHtmlString Explosion(int? Height, int? Width) {
return new HtmlString(string.Format("<span>Boom {0}x{1}</span>", Height, Width));
}
[Shape]
public IHtmlString Page(dynamic Display, dynamic Shape) {
return Display(Shape.Sidebar, Shape.Messages);
}
[Shape]
public IHtmlString Zone(dynamic Display, dynamic Shape) {
var tag = new TagBuilder("div");
tag.GenerateId("zone-" + Shape.Name);
@@ -28,11 +33,11 @@ namespace Orchard.DevTools {
return new HtmlString(tag.ToString());
}
[Shape]
public IHtmlString Message(dynamic Display, object Content, string Severity) {
return Display(new HtmlString("<p class=\"message\">"), Severity ?? "Neutral", ": ", Content, new HtmlString("</p>"));
}
static IHtmlString Combine(IEnumerable<IHtmlString> contents) {
return new HtmlString(contents.Aggregate("", (a, b) => a + b));
}

View File

@@ -12,6 +12,7 @@ using Orchard.Data.Migration.Interpreters;
using Orchard.Data.Providers;
using Orchard.Data.Migration;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.DisplayManagement.Shapes;
using Orchard.Environment.Extensions;
@@ -65,7 +66,7 @@ namespace Orchard.Setup {
builder.RegisterType<DisplayHelperFactory>().As<IDisplayHelperFactory>();
builder.RegisterType<DefaultDisplayManager>().As<IDisplayManager>();
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<DefaultShapeTableFactory>().As<IShapeTableFactory>();
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableFactory>();
}

View File

@@ -29,11 +29,11 @@ namespace Orchard.ContentManagement.Drivers.Coordinators {
var fieldTypeName = partFieldDefinition.FieldDefinition.Name;
var fieldInfo = fieldInfos.FirstOrDefault(x => x.FieldTypeName == fieldTypeName);
if (fieldInfo != null) {
var storage = _fieldStorageProviderSelector
.GetProvider(partFieldDefinition)
.BindStorage(contentPart, partFieldDefinition);
var field = fieldInfo.Factory(partFieldDefinition, storage);
contentPart.Weld(field);
var storage = _fieldStorageProviderSelector
.GetProvider(partFieldDefinition)
.BindStorage(contentPart, partFieldDefinition);
var field = fieldInfo.Factory(partFieldDefinition, storage);
contentPart.Weld(field);
}
}
}

View File

@@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
namespace Orchard.DisplayManagement.Descriptors {
public class DefaultShapeTableFactory : IShapeTableFactory {
public IDictionary<string, ShapeTable> CreateShapeTables() {
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,37 @@
using System.Collections.Generic;
using System.Linq;
namespace Orchard.DisplayManagement.Descriptors {
public class DefaultShapeTableManager : IShapeTableManager {
private readonly IEnumerable<IShapeDescriptorBindingStrategy> _bindingStrategies;
public DefaultShapeTableManager(IEnumerable<IShapeDescriptorBindingStrategy> bindingStrategies) {
_bindingStrategies = bindingStrategies;
}
private ShapeTable _shapeTable;
public ShapeTable GetShapeTable(string themeName) {
if (_shapeTable == null) {
var builder = new ShapeTableBuilder();
foreach (var bindingStrategy in _bindingStrategies) {
bindingStrategy.Discover(builder);
}
// placeholder - alterations will need to be selective and in a particular order
_shapeTable = new ShapeTable {
Descriptors = builder.Build()
.GroupBy(alteration => alteration.ShapeType)
.Select(group => group.Aggregate(
new ShapeDescriptor { ShapeType = group.Key },
(d, a) => {
a.Alter(d);
return d;
}))
.ToDictionary(sd => sd.ShapeType)
};
}
return _shapeTable;
}
}
}

View File

@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment.Extensions.Models;
namespace Orchard.DisplayManagement.Descriptors {
public interface IShapeTableManager : IDependency {
ShapeTable GetShapeTable(string themeName);
}
public interface IShapeTableFactory : IDependency {
IDictionary<string, ShapeTable> CreateShapeTables();
}
public interface IShapeDescriptorBindingStrategy : IDependency {
void Discover(ShapeTableBuilder builder);
}
public class ShapeTable {
public IDictionary<string, ShapeDescriptor> Descriptors { get; set; }
}
public class ShapeDescriptor {
public string ShapeType { get; set; }
public ShapeBinding Binding { get; set; }
}
public delegate object ShapeBinding(DisplayContext displayContext);
public class ShapeTableBuilder {
readonly IList<ShapeDescriptorAlterationBuilderImpl> _descriptorBuilders = new List<ShapeDescriptorAlterationBuilderImpl>();
public ShapeDescriptorAlterationBuilder Describe {
get {
var db = new ShapeDescriptorAlterationBuilderImpl();
_descriptorBuilders.Add(db);
return db;
}
}
public IEnumerable<ShapeDescriptorAlteration> Build() {
return _descriptorBuilders.Select(b => b.Build());
}
class ShapeDescriptorAlterationBuilderImpl : ShapeDescriptorAlterationBuilder {
public ShapeDescriptorAlteration Build() {
return new ShapeDescriptorAlteration(_shapeType, _feature, _configurations.ToArray());
}
}
}
public class ShapeDescriptorAlteration {
private readonly IList<Action<ShapeDescriptor>> _configurations;
public ShapeDescriptorAlteration(string shapeType, FeatureDescriptor feature, IList<Action<ShapeDescriptor>> configurations) {
_configurations = configurations;
ShapeType = shapeType;
Feature = feature;
}
public string ShapeType { get; private set; }
public FeatureDescriptor Feature { get; private set; }
public void Alter(ShapeDescriptor descriptor) {
foreach (var configuration in _configurations) {
configuration(descriptor);
}
}
}
public class ShapeDescriptorAlterationBuilder {
protected FeatureDescriptor _feature;
protected string _shapeType;
protected readonly IList<Action<ShapeDescriptor>> _configurations = new List<Action<ShapeDescriptor>>();
public ShapeDescriptorAlterationBuilder Named(string shapeType) {
_shapeType = shapeType;
return this;
}
public ShapeDescriptorAlterationBuilder From(FeatureDescriptor feature) {
_feature = feature;
return this;
}
public ShapeDescriptorAlterationBuilder Configure(Action<ShapeDescriptor> action) {
_configurations.Add(action);
return this;
}
public ShapeDescriptorAlterationBuilder BoundAs(Func<ShapeDescriptor, ShapeBinding> binder) {
// schedule the configuration
return Configure(descriptor => {
ShapeBinding target = null;
// announce the binding, which may be reconfigured before it's used
descriptor.Binding = displayContext => {
// when used, first realize the actual target once
if (target == null)
target = binder(descriptor);
// and execute the re
return target(displayContext);
};
});
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Linq;
using Autofac;
using Autofac.Core;
using Orchard.Environment.Extensions.Models;
namespace Orchard.DisplayManagement.Descriptors.ShapeAttributeStrategy {
public class ShapeAttributeBindingModule : Module {
readonly List<ShapeAttributeOccurrence> _occurrences = new List<ShapeAttributeOccurrence>();
protected override void Load(ContainerBuilder builder) {
builder.RegisterInstance(_occurrences).As<IEnumerable<ShapeAttributeOccurrence>>();
}
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) {
var occurrences = registration.Activator.LimitType.GetMethods()
.SelectMany(mi => mi.GetCustomAttributes(typeof(ShapeAttribute), false).OfType<ShapeAttribute>()
.Select(sa => new ShapeAttributeOccurrence(
sa,
mi,
registration,
() => GetFeatureDescriptor(registration))))
.ToArray();
if (occurrences.Any())
_occurrences.AddRange(occurrences);
}
private static FeatureDescriptor GetFeatureDescriptor(IComponentRegistration registration) {
object value; return registration.Metadata.TryGetValue("Feature", out value) ? value as FeatureDescriptor : null;
}
}
}

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Autofac;
using Autofac.Core;
using Microsoft.CSharp.RuntimeBinder;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment;
namespace Orchard.DisplayManagement.Descriptors.ShapeAttributeStrategy {
public class ShapeAttributeBindingStrategy : IShapeDescriptorBindingStrategy {
private readonly IEnumerable<ShapeAttributeOccurrence> _shapeAttributeOccurrences;
private readonly IOrchardHostContainer _orchardHostContainer;
public ShapeAttributeBindingStrategy(
IEnumerable<ShapeAttributeOccurrence> shapeAttributeOccurrences,
IOrchardHostContainer orchardHostContainer) {
_shapeAttributeOccurrences = shapeAttributeOccurrences;
_orchardHostContainer = orchardHostContainer;
}
public void Discover(ShapeTableBuilder builder) {
foreach (var iter in _shapeAttributeOccurrences) {
var occurrence = iter;
var shapeType = occurrence.ShapeAttribute.ShapeType ?? occurrence.MethodInfo.Name;
builder.Describe
.Named(shapeType)
.From(occurrence.Feature)
.BoundAs(descriptor => CreateDelegate(occurrence, descriptor));
}
}
private ShapeBinding CreateDelegate(
ShapeAttributeOccurrence attributeOccurrence,
ShapeDescriptor descriptor) {
return context => {
var componentContext = _orchardHostContainer.Resolve<IComponentContext>();
var serviceInstance = componentContext.Resolve(attributeOccurrence.Registration, Enumerable.Empty<Parameter>());
// oversimplification for the sake of evolving
return PerformInvoke(context, attributeOccurrence.MethodInfo, serviceInstance);
};
}
private object PerformInvoke(DisplayContext displayContext, MethodInfo methodInfo, object serviceInstance) {
var arguments = methodInfo.GetParameters()
.Select(parameter => BindParameter(displayContext, parameter));
return methodInfo.Invoke(serviceInstance, arguments.ToArray());
}
private object BindParameter(DisplayContext displayContext, ParameterInfo parameter) {
if (parameter.Name == "Shape")
return displayContext.Value;
if (parameter.Name == "Display")
return displayContext.Display;
var result = ((dynamic)(displayContext.Value))[parameter.Name];
var converter = _converters.GetOrAdd(parameter.ParameterType, CompileConverter);
return converter.Invoke((object)result);
}
static readonly ConcurrentDictionary<Type, Func<object, object>> _converters =
new ConcurrentDictionary<Type, Func<object, object>>();
static Func<object, object> CompileConverter(Type targetType) {
var valueParameter = Expression.Parameter(typeof(object), "value");
return Expression.Lambda<Func<object, object>>(
Expression.Convert(
Expression.Dynamic(
Microsoft.CSharp.RuntimeBinder.Binder.Convert(CSharpBinderFlags.ConvertExplicit, targetType, null),
targetType,
valueParameter),
typeof(object)),
valueParameter).Compile();
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Reflection;
using Autofac.Core;
using Orchard.Environment.Extensions.Models;
namespace Orchard.DisplayManagement.Descriptors.ShapeAttributeStrategy {
public class ShapeAttributeOccurrence {
private readonly Func<FeatureDescriptor> _feature;
public ShapeAttributeOccurrence(ShapeAttribute shapeAttribute, MethodInfo methodInfo, IComponentRegistration registration, Func<FeatureDescriptor> feature) {
ShapeAttribute = shapeAttribute;
MethodInfo = methodInfo;
Registration = registration;
_feature = feature;
}
public ShapeAttribute ShapeAttribute { get; private set; }
public MethodInfo MethodInfo { get; private set; }
public IComponentRegistration Registration { get; private set; }
public FeatureDescriptor Feature { get { return _feature(); } }
}
}

View File

@@ -5,6 +5,6 @@
/// Note: Anything on this interface is a reserved word for the purpose of shape properties
/// </summary>
public interface IShape {
IShapeAttributes Attributes { get; set; }
IShapeMetadata Metadata { get; set; }
}
}

View File

@@ -1,5 +1,5 @@
namespace Orchard.DisplayManagement {
public interface IShapeAttributes {
public interface IShapeMetadata {
string Type { get; set; }
string Position { get; set; }
}

View File

@@ -3,5 +3,5 @@
/// Base interface for module components which define new shape types and
/// optionally provide default implementation method
/// </summary>
public interface IShapeDriver : IDependency{}
public interface IShapeProvider : IDependency{}
}

View File

@@ -4,11 +4,13 @@ using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Web;
using Microsoft.CSharp.RuntimeBinder;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Shapes;
using Orchard.Localization;
namespace Orchard.DisplayManagement.Implementation {
public class DefaultDisplayManager : IDisplayManager {
private readonly IShapeTableManager _shapeTableManager;
private readonly IShapeTableFactory _shapeTableFactory;
// this need to be Shape instead of IShape - cast to interface throws error on clr types like HtmlString
@@ -19,8 +21,8 @@ namespace Orchard.DisplayManagement.Implementation {
typeof(Shape),
null/*typeof(DefaultDisplayManager)*/)));
public DefaultDisplayManager(IShapeTableFactory shapeTableFactory) {
_shapeTableFactory = shapeTableFactory;
public DefaultDisplayManager(IShapeTableManager shapeTableManager) {
_shapeTableManager = shapeTableManager;
T = NullLocalizer.Instance;
}
@@ -28,23 +30,24 @@ namespace Orchard.DisplayManagement.Implementation {
public IHtmlString Execute(DisplayContext context) {
var shape = _convertAsShapeCallsite.Target(_convertAsShapeCallsite, context.Value);
// non-shape arguements are returned as a no-op
if (shape == null)
return CoerceHtmlString(context.Value);
var shapeAttributes = shape.Attributes;
var shapeAttributes = shape.Metadata;
// can't really cope with a shape that has no type information
if (shapeAttributes == null || string.IsNullOrEmpty(shapeAttributes.Type))
return CoerceHtmlString(context.Value);
var shapeTable = _shapeTableFactory.CreateShapeTable();
var shapeTable = _shapeTableManager.GetShapeTable(null);
//preproc loop / event (alter shape, swapping type)
ShapeTable.Entry entry;
if (shapeTable.Entries.TryGetValue(shapeAttributes.Type, out entry)) {
return Process(entry, shape, context);
ShapeDescriptor shapeDescriptor;
if (shapeTable.Descriptors.TryGetValue(shapeAttributes.Type, out shapeDescriptor)) {
return Process(shapeDescriptor, shape, context);
}
//postproc / content html alteration/wrapping/etc
throw new OrchardException(T("Shape type {0} not found", shapeAttributes.Type));
@@ -61,8 +64,8 @@ namespace Orchard.DisplayManagement.Implementation {
return new HtmlString(HttpUtility.HtmlEncode(value));
}
private IHtmlString Process(ShapeTable.Entry entry, IShape shape, DisplayContext context) {
return CoerceHtmlString(entry.Target(context));
private IHtmlString Process(ShapeDescriptor shapeDescriptor, IShape shape, DisplayContext context) {
return CoerceHtmlString(shapeDescriptor.Binding(context));
}
class ForgivingConvertBinder : ConvertBinder {

View File

@@ -41,7 +41,7 @@ namespace Orchard.DisplayManagement.Implementation {
// consideration - types without default constructors could consume positional arguments?
var shape = ClayActivator.CreateInstance(baseType, behaviors);
shape.Attributes = new ShapeAttributes { Type = shapeType };
shape.Metadata = new ShapeMetadata { Type = shapeType };
// only one non-Type, non-named argument is allowed
var initializer = positional.SingleOrDefault();

View File

@@ -26,6 +26,8 @@ namespace Orchard.DisplayManagement.Implementation {
public override object InvokeMember(Func<object> proceed, object target, string name, INamedEnumerable<object> args) {
return ((DisplayHelper)target).Invoke(name, args);
}
}
}
}

View File

@@ -0,0 +1,10 @@
using System;
namespace Orchard.DisplayManagement {
public class ShapeAttribute : Attribute {
public ShapeAttribute() { }
public ShapeAttribute(string shapeType) { this.ShapeType = shapeType; }
public string ShapeType { get; private set; }
}
}

View File

@@ -1,5 +1,5 @@
namespace Orchard.DisplayManagement.Shapes {
public class Shape : IShape {
public virtual IShapeAttributes Attributes { get; set; }
public virtual IShapeMetadata Metadata { get; set; }
}
}

View File

@@ -1,5 +1,5 @@
namespace Orchard.DisplayManagement.Shapes {
public class ShapeAttributes : IShapeAttributes {
public class ShapeMetadata : IShapeMetadata {
public string Type { get; set; }
public string Position { get; set; }
}

View File

@@ -1,86 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Web;
using Microsoft.CSharp.RuntimeBinder;
using Orchard.DisplayManagement.Implementation;
using Binder = Microsoft.CSharp.RuntimeBinder.Binder;
namespace Orchard.DisplayManagement {
public class DefaultShapeTableFactory : IShapeTableFactory {
private readonly IEnumerable<IShapeDriver> _shapeProviders;
public DefaultShapeTableFactory(IEnumerable<IShapeDriver> shapeProviders) {
_shapeProviders = shapeProviders;
}
public ShapeTable CreateShapeTable() {
var table = new ShapeTable { Entries = GetEntries().ToDictionary(e => e.ShapeType) };
return table;
}
private IEnumerable<ShapeTable.Entry> GetEntries() {
foreach (var shapeProvider in _shapeProviders) {
foreach (var methodInfo in shapeProvider.GetType().GetMethods().Where(IsAcceptableMethod)) {
var info = methodInfo;
var provider = shapeProvider;
yield return new ShapeTable.Entry {
ShapeType = methodInfo.Name,
Target = ctx => PerformInvoke(ctx, info, provider)
};
}
}
}
private object PerformInvoke(DisplayContext displayContext, MethodInfo methodInfo, IShapeDriver shapeDriver) {
// oversimplification for the sake of evolving
dynamic shape = displayContext.Value;
var arguments = methodInfo.GetParameters()
.Select(parameter => BindParameter(displayContext, parameter));
return methodInfo.Invoke(shapeDriver, arguments.ToArray());
}
private object BindParameter(DisplayContext displayContext, ParameterInfo parameter) {
if (parameter.Name == "Shape")
return displayContext.Value;
if (parameter.Name == "Display")
return displayContext.Display;
var result = ((dynamic)(displayContext.Value))[parameter.Name];
var converter = _converters.GetOrAdd(
parameter.ParameterType,
CompileConverter);
return converter(result);
}
static Func<object, object> CompileConverter(Type targetType) {
var valueParameter = Expression.Parameter(typeof (object), "value");
return Expression.Lambda<Func<object, object>>(
Expression.Convert(
Expression.Dynamic(
Binder.Convert(CSharpBinderFlags.ConvertExplicit, targetType, null),
targetType,
valueParameter),
typeof (object)),
valueParameter).Compile();
}
static readonly ConcurrentDictionary<Type, Func<object, object>> _converters =
new ConcurrentDictionary<Type, Func<object, object>>();
static bool IsAcceptableMethod(MethodInfo methodInfo) {
if (methodInfo.IsSpecialName)
return false;
if (methodInfo.DeclaringType == typeof(object))
return false;
return true;
}
}
}

View File

@@ -1,10 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Orchard.DisplayManagement {
public interface IShapeTableFactory : IDependency {
ShapeTable CreateShapeTable();
}
}

View File

@@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Web;
using Orchard.DisplayManagement.Implementation;
namespace Orchard.DisplayManagement {
public class ShapeTable {
public IDictionary<string, Entry> Entries { get; set; }
public class Entry {
public string ShapeType { get; set; }
public Func<DisplayContext, object> Target { get; set; }
}
}
}

View File

@@ -377,25 +377,29 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="ContentManagement\DataMigrations\FrameworkDataMigration.cs" />
<Compile Include="DisplayManagement\Descriptors\DefaultShapeTableFactory.cs" />
<Compile Include="DisplayManagement\Descriptors\ShapeAttributeStrategy\ShapeAttributeBindingModule.cs" />
<Compile Include="DisplayManagement\Descriptors\ShapeAttributeStrategy\ShapeAttributeOccurrence.cs" />
<Compile Include="DisplayManagement\ShapeAttribute.cs" />
<Compile Include="DisplayManagement\Descriptors\ShapeAttributeStrategy\ShapeAttributeBindingStrategy.cs" />
<Compile Include="DisplayManagement\Implementation\DefaultDisplayManager.cs" />
<Compile Include="DisplayManagement\Implementation\DefaultShapeFactory.cs" />
<Compile Include="DisplayManagement\IShapeHelperFactory.cs" />
<Compile Include="DisplayManagement\IDisplayHelperFactory.cs" />
<Compile Include="DisplayManagement\IShape.cs" />
<Compile Include="DisplayManagement\IShapeAttributes.cs" />
<Compile Include="DisplayManagement\IShapeMetadata.cs" />
<Compile Include="DisplayManagement\Shapes\Shape.cs" />
<Compile Include="DisplayManagement\Shapes\ShapeAttributes.cs" />
<Compile Include="DisplayManagement\Speculation\DefaultShapeTableFactory.cs" />
<Compile Include="DisplayManagement\Shapes\ShapeMetadata.cs" />
<Compile Include="DisplayManagement\Descriptors\DefaultShapeTableManager.cs" />
<Compile Include="DisplayManagement\Implementation\DisplayContext.cs" />
<Compile Include="DisplayManagement\Implementation\DisplayHelper.cs" />
<Compile Include="DisplayManagement\Implementation\DisplayHelperFactory.cs" />
<Compile Include="DisplayManagement\Implementation\IDisplayManager.cs" />
<Compile Include="DisplayManagement\IShapeFactory.cs" />
<Compile Include="DisplayManagement\IShapeDriver.cs" />
<Compile Include="DisplayManagement\Speculation\IShapeTableFactory.cs" />
<Compile Include="DisplayManagement\IShapeProvider.cs" />
<Compile Include="DisplayManagement\Descriptors\Interfaces.cs" />
<Compile Include="DisplayManagement\Implementation\ShapeHelper.cs" />
<Compile Include="DisplayManagement\Implementation\ShapeHelperFactory.cs" />
<Compile Include="DisplayManagement\Speculation\ShapeTable.cs" />
<Compile Include="Environment\IContextualizable.cs" />
<Compile Include="Environment\IHostLocalRestart.cs" />
<Compile Include="Environment\IShellContainerRegistrations.cs" />