From 8fb07891a706845836bafef37b647c7a850cc268 Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Thu, 2 Apr 2009 16:03:43 -0500 Subject: [PATCH] Extracting the ViewDescriptorBuilder functionality into it's own service class Seperates that concern, offers a new extensibility point for controlling view location, and helper prepare for some FindView optimization git-svn-id: http://dev.dejardin.org/svn/spark/branches/spike-1.1@315 0f556331-6e29-4ecb-911f-9ab3f335dbaa Conflicts: src/Spark.Web.Mvc/JavascriptViewResult.cs src/Spark/CompiledViewEntry.cs src/Spark/CompositeViewEntry.cs src/Spark/SparkViewEngine.cs --- .../SparkViewFactoryTests.cs | 2 +- src/Spark.Tests/BatchCompilationTester.cs | 8 +- src/Spark.Tests/CompiledViewHolderTester.cs | 17 +- src/Spark.Tests/SparkExtensionTester.cs | 4 +- .../PdfViewResultTests.cs | 13 +- src/Spark.Web.Mvc.Pdf/PdfViewResult.cs | 3 + .../DescriptorBuildingTester.cs | 61 ++++++ .../SparkViewFactoryTester.cs | 51 ++--- src/Spark.Web.Mvc/DefaultDescriptorBuilder.cs | 164 ++++++++++++++++ src/Spark.Web.Mvc/IDescriptorBuilder.cs | 59 ++++++ src/Spark.Web.Mvc/JavascriptViewResult.cs | 7 +- src/Spark.Web.Mvc/Spark.Web.Mvc.csproj | 4 +- src/Spark.Web.Mvc/SparkEngineStarter.cs | 1 + src/Spark.Web.Mvc/SparkViewFactory.cs | 182 ++++-------------- src/Spark/CompiledViewEntry.cs | 46 +++++ src/Spark/CompiledViewHolder.cs | 92 +-------- src/Spark/ISparkViewEngine.cs | 1 + src/Spark/ISparkViewEntry.cs | 4 + src/Spark/Spark.csproj | 1 + src/Spark/SparkViewDescriptor.cs | 39 ++++ src/Spark/SparkViewEngine.cs | 60 ++---- 21 files changed, 494 insertions(+), 325 deletions(-) create mode 100644 src/Spark.Web.Mvc/DefaultDescriptorBuilder.cs create mode 100644 src/Spark.Web.Mvc/IDescriptorBuilder.cs create mode 100644 src/Spark/CompiledViewEntry.cs diff --git a/src/Castle.MonoRail.Views.Spark.Tests/SparkViewFactoryTests.cs b/src/Castle.MonoRail.Views.Spark.Tests/SparkViewFactoryTests.cs index 48a8ce92..04b328c2 100644 --- a/src/Castle.MonoRail.Views.Spark.Tests/SparkViewFactoryTests.cs +++ b/src/Castle.MonoRail.Views.Spark.Tests/SparkViewFactoryTests.cs @@ -156,7 +156,7 @@ public void Rescue404Rendering() Assert.AreEqual("

404 message rendered

\r\n", output.ToString()); } - [Test] + [Test, Ignore("Controller Type effects are no longer supported in 1.1")] public void ControllerHelperAttributeCanBeUsed() { controller = new Helpers.HomeController(); diff --git a/src/Spark.Tests/BatchCompilationTester.cs b/src/Spark.Tests/BatchCompilationTester.cs index 44c6f10a..6a749077 100644 --- a/src/Spark.Tests/BatchCompilationTester.cs +++ b/src/Spark.Tests/BatchCompilationTester.cs @@ -86,8 +86,8 @@ public void DescriptorsAreEqual() var attribs = types[0].GetCustomAttributes(typeof(SparkViewAttribute), false); var sparkViewAttrib = (SparkViewAttribute)attribs[0]; - var key0 = new CompiledViewHolder.Key { Descriptor = descriptor }; - var key1 = new CompiledViewHolder.Key { Descriptor = sparkViewAttrib.BuildDescriptor() }; + var key0 = descriptor; + var key1 = sparkViewAttrib.BuildDescriptor(); Assert.AreEqual(key0, key1); } @@ -105,8 +105,8 @@ public void DescriptorsWithNoTargetNamespace() var attribs = types[0].GetCustomAttributes(typeof(SparkViewAttribute), false); var sparkViewAttrib = (SparkViewAttribute)attribs[0]; - var key0 = new CompiledViewHolder.Key { Descriptor = descriptor }; - var key1 = new CompiledViewHolder.Key { Descriptor = sparkViewAttrib.BuildDescriptor() }; + var key0 = descriptor; + var key1 = sparkViewAttrib.BuildDescriptor(); Assert.AreEqual(key0, key1); } diff --git a/src/Spark.Tests/CompiledViewHolderTester.cs b/src/Spark.Tests/CompiledViewHolderTester.cs index 6f94224b..9946b79f 100644 --- a/src/Spark.Tests/CompiledViewHolderTester.cs +++ b/src/Spark.Tests/CompiledViewHolderTester.cs @@ -30,15 +30,12 @@ public void Init() holder = new CompiledViewHolder(); } - private CompiledViewHolder.Key BuildKey(params string[] templates) + private SparkViewDescriptor BuildKey(params string[] templates) { - return new CompiledViewHolder.Key - { - Descriptor = new SparkViewDescriptor - { - Templates = templates - } - }; + return new SparkViewDescriptor + { + Templates = templates + }; } [Test] @@ -53,7 +50,7 @@ public void LookupNonExistantReturnsNull() public void LookupReturnsStoredInstance() { var key = BuildKey("c\\v", "shared\\m"); - var entry = new CompiledViewHolder.Entry { Key = key, Loader = new ViewLoader() }; + var entry = new CompiledViewEntry { Descriptor = key, Loader = new ViewLoader() }; Assert.IsNull(holder.Lookup(key)); holder.Store(entry); Assert.AreSame(entry, holder.Lookup(key)); @@ -91,7 +88,7 @@ public void ExpiredEntryReturnsNull() loader.Stub(x => x.IsCurrent()).Do(foo); var key = BuildKey("c\\v", "shared\\m"); - var entry = new CompiledViewHolder.Entry { Key = key, Loader = loader }; + var entry = new CompiledViewEntry { Descriptor = key, Loader = loader }; holder.Store(entry); Assert.AreSame(entry, holder.Lookup(key)); isCurrent = false; diff --git a/src/Spark.Tests/SparkExtensionTester.cs b/src/Spark.Tests/SparkExtensionTester.cs index 6d97fd7f..9cc57581 100644 --- a/src/Spark.Tests/SparkExtensionTester.cs +++ b/src/Spark.Tests/SparkExtensionTester.cs @@ -41,8 +41,8 @@ public void TestExtensions() { var descriptor = new SparkViewDescriptor(); descriptor.Templates.Add("Home\\extensionelements.spark"); - var entry = engine.CreateEntry(engine.CreateKey(descriptor)); - Assert.That(entry.Compiler.SourceCode.Contains("//this was a test")); + var entry = engine.CreateEntry(descriptor); + Assert.That(entry.SourceCode.Contains("//this was a test")); } } diff --git a/src/Spark.Web.Mvc.Pdf.Tests/PdfViewResultTests.cs b/src/Spark.Web.Mvc.Pdf.Tests/PdfViewResultTests.cs index ed02a71b..fcc72d03 100644 --- a/src/Spark.Web.Mvc.Pdf.Tests/PdfViewResultTests.cs +++ b/src/Spark.Web.Mvc.Pdf.Tests/PdfViewResultTests.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Web; using System.Web.Mvc; using System.Web.Routing; using NUnit.Framework; @@ -33,8 +29,7 @@ public void PdfResultShouldFindPartialViewAndRenderIt() var controllerContext = GetControllerContext(stream); IView view; - var viewEngine = MockViewEngine(controllerContext, out view); - + var viewEngine = MockViewEngine(controllerContext, out view); var result = new PdfViewResult { @@ -48,13 +43,13 @@ public void PdfResultShouldFindPartialViewAndRenderIt() view.VerifyAllExpectations(); } - private IViewEngine MockViewEngine(ControllerContext controllerContext, out IView view) + private static IViewEngine MockViewEngine(ControllerContext controllerContext, out IView view) { var viewEngine = MockRepository.GenerateMock(); view = MockRepository.GenerateMock(); - + viewEngine - .Expect(x => x.FindPartialView(controllerContext, "quux", true)) + .Expect(x => x.FindView(controllerContext, "quux", "", true)) .Return(new ViewEngineResult(view, viewEngine)); view diff --git a/src/Spark.Web.Mvc.Pdf/PdfViewResult.cs b/src/Spark.Web.Mvc.Pdf/PdfViewResult.cs index c9512bc7..f9a7a71a 100644 --- a/src/Spark.Web.Mvc.Pdf/PdfViewResult.cs +++ b/src/Spark.Web.Mvc.Pdf/PdfViewResult.cs @@ -17,6 +17,9 @@ public class PdfViewResult : ViewResult protected override ViewEngineResult FindView(ControllerContext context) { var result = base.FindView(context); + if (result.View == null) + return result; + var pdfView = new PdfView(result); return new ViewEngineResult(pdfView, pdfView); } diff --git a/src/Spark.Web.Mvc.Tests/DescriptorBuildingTester.cs b/src/Spark.Web.Mvc.Tests/DescriptorBuildingTester.cs index a4bd5343..969d9c99 100644 --- a/src/Spark.Web.Mvc.Tests/DescriptorBuildingTester.cs +++ b/src/Spark.Web.Mvc.Tests/DescriptorBuildingTester.cs @@ -36,6 +36,8 @@ public class DescriptorBuildingTester [SetUp] public void Init() { + CompiledViewHolder.Current = null; + _factory = new SparkViewFactory(); _viewFolder = new InMemoryViewFolder(); _factory.ViewFolder = _viewFolder; @@ -225,5 +227,64 @@ public void PartialViewFromAreaIgnoresLayout() result, searchedLocations, @"Admin\Home\Index.spark"); } + + [Test] + public void UseMasterCreatesTemplateChain() + { + _routeData.Values.Add("controller", "Home"); + _viewFolder.Add(@"Home\Index.spark", ""); + _viewFolder.Add(@"Layouts\Red.spark", ""); + _viewFolder.Add(@"Layouts\Green.spark", ""); + _viewFolder.Add(@"Layouts\Blue.spark", ""); + _viewFolder.Add(@"Layouts\Application.spark", ""); + _viewFolder.Add(@"Layouts\Home.spark", ""); + + var searchedLocations = new List(); + var result = _factory.CreateDescriptor(_controllerContext, "Index", null, true, searchedLocations); + AssertDescriptorTemplates( + result, searchedLocations, + @"Home\Index.spark", + @"Layouts\Green.spark", + @"Layouts\Red.spark", + @"Layouts\Blue.spark"); + } + + [Test] + public void NamedMasterOverridesViewMaster() + { + _routeData.Values.Add("controller", "Home"); + _viewFolder.Add(@"Home\Index.spark", ""); + _viewFolder.Add(@"Layouts\Red.spark", ""); + _viewFolder.Add(@"Layouts\Green.spark", ""); + _viewFolder.Add(@"Layouts\Blue.spark", ""); + _viewFolder.Add(@"Layouts\Application.spark", ""); + _viewFolder.Add(@"Layouts\Home.spark", ""); + + var searchedLocations = new List(); + var result = _factory.CreateDescriptor(_controllerContext, "Index", "Red", true, searchedLocations); + AssertDescriptorTemplates( + result, searchedLocations, + @"Home\Index.spark", + @"Layouts\Red.spark", + @"Layouts\Blue.spark"); + } + + [Test] + public void PartialViewIgnoresUseMasterAndDefault() + { + _routeData.Values.Add("controller", "Home"); + _viewFolder.Add(@"Home\Index.spark", ""); + _viewFolder.Add(@"Layouts\Red.spark", ""); + _viewFolder.Add(@"Layouts\Green.spark", ""); + _viewFolder.Add(@"Layouts\Blue.spark", ""); + _viewFolder.Add(@"Layouts\Application.spark", ""); + _viewFolder.Add(@"Layouts\Home.spark", ""); + + var searchedLocations = new List(); + var result = _factory.CreateDescriptor(_controllerContext, "Index", null, false, searchedLocations); + AssertDescriptorTemplates( + result, searchedLocations, + @"Home\Index.spark"); + } } } diff --git a/src/Spark.Web.Mvc.Tests/SparkViewFactoryTester.cs b/src/Spark.Web.Mvc.Tests/SparkViewFactoryTester.cs index 72168d56..703097d3 100644 --- a/src/Spark.Web.Mvc.Tests/SparkViewFactoryTester.cs +++ b/src/Spark.Web.Mvc.Tests/SparkViewFactoryTester.cs @@ -239,14 +239,11 @@ public void HtmlHelperWorksOnItsOwn() [Test] public void MasterApplicationIfPresent() { - var viewFolder = MockRepository.GenerateMock(); - viewFolder.Expect(x => x.HasView("Foo\\Baaz.spark")).Return(true); - viewFolder.Expect(x => x.HasView("Layouts\\Foo.spark")).Return(false); - viewFolder.Expect(x => x.HasView("Shared\\Foo.spark")).Return(false); - viewFolder.Expect(x => x.HasView("Layouts\\Application.spark")).Return(false); - viewFolder.Expect(x => x.HasView("Shared\\Application.spark")).Return(true); - - factory.ViewFolder = viewFolder; + factory.ViewFolder = new InMemoryViewFolder + { + {"Foo\\Baaz.spark", ""}, + {"Shared\\Application.spark", ""} + }; @@ -266,17 +263,10 @@ public void MasterApplicationIfPresent() [Test] public void MasterEmptyByDefault() { - var viewSourceLoader = MockRepository.GenerateMock(); - viewSourceLoader.Expect(x => x.HasView("Foo\\Baaz.spark")).Return(true); - viewSourceLoader.Expect(x => x.HasView("Layouts\\Foo.spark")).Return(false); - viewSourceLoader.Expect(x => x.HasView("Shared\\Foo.spark")).Return(false); - viewSourceLoader.Expect(x => x.HasView("Layouts\\Application.spark")).Return(false); - viewSourceLoader.Expect(x => x.HasView("Shared\\Application.spark")).Return(false); - - factory.ViewFolder = viewSourceLoader; - - - + factory.ViewFolder = new InMemoryViewFolder + { + {"Foo\\Baaz.spark", ""} + }; routeData.Values["controller"] = "Foo"; routeData.Values["action"] = "NotBaaz"; @@ -284,7 +274,6 @@ public void MasterEmptyByDefault() var descriptor = factory.CreateDescriptor(controllerContext, "Baaz", null, true, null); - //mocks.VerifyAll(); Assert.AreEqual(1, descriptor.Templates.Count); Assert.AreEqual("Foo\\Baaz.spark", descriptor.Templates[0]); @@ -293,12 +282,11 @@ public void MasterEmptyByDefault() [Test] public void MasterForControllerIfPresent() { - var viewSourceLoader = MockRepository.GenerateMock(); - viewSourceLoader.Expect(x => x.HasView("Foo\\Baaz.spark")).Return(true); - viewSourceLoader.Expect(x => x.HasView("Layouts\\Foo.spark")).Return(false); - viewSourceLoader.Expect(x => x.HasView("Shared\\Foo.spark")).Return(true); - - factory.ViewFolder = viewSourceLoader; + factory.ViewFolder = new InMemoryViewFolder + { + {"Foo\\Baaz.spark", ""}, + {"Shared\\Foo.spark",""} + }; @@ -308,8 +296,6 @@ public void MasterForControllerIfPresent() var descriptor = factory.CreateDescriptor(controllerContext, "Baaz", null, true, null); - //mocks.VerifyAll(); - Assert.AreEqual(2, descriptor.Templates.Count); Assert.AreEqual("Foo\\Baaz.spark", descriptor.Templates[0]); Assert.AreEqual("Shared\\Foo.spark", descriptor.Templates[1]); @@ -370,10 +356,11 @@ public void RenderPlainView() [Test] public void TargetNamespaceFromController() { - var viewSourceLoader = MockRepository.GenerateMock(); - viewSourceLoader.Expect(x => x.HasView("Home\\Baaz.spark")).Return(true); - viewSourceLoader.Expect(x => x.HasView("Layouts\\Home.spark")).Return(true); - factory.ViewFolder = viewSourceLoader; + factory.ViewFolder = new InMemoryViewFolder + { + {"Home\\Baaz.spark", ""}, + {"Layouts\\Home.spark",""} + }; controller = new StubController(); controllerContext = new ControllerContext(httpContext, routeData, controller); diff --git a/src/Spark.Web.Mvc/DefaultDescriptorBuilder.cs b/src/Spark.Web.Mvc/DefaultDescriptorBuilder.cs new file mode 100644 index 00000000..9074177a --- /dev/null +++ b/src/Spark.Web.Mvc/DefaultDescriptorBuilder.cs @@ -0,0 +1,164 @@ +using System.Collections.Generic; +using System.Linq; +using Spark.Compiler; +using Spark.Compiler.NodeVisitors; + +namespace Spark.Web.Mvc +{ + public class DefaultDescriptorBuilder : IDescriptorBuilder, ISparkServiceInitialize + { + private ISparkViewEngine _engine; + + public DefaultDescriptorBuilder() + { + } + + public DefaultDescriptorBuilder(ISparkViewEngine engine) + { + _engine = engine; + } + + public virtual void Initialize(ISparkServiceContainer container) + { + _engine = container.GetService(); + } + + public virtual SparkViewDescriptor BuildDescriptor(BuildDescriptorParams buildDescriptorParams, ICollection searchedLocations) + { + var descriptor = new SparkViewDescriptor + { + TargetNamespace = buildDescriptorParams.TargetNamespace + }; + + if (!LocatePotentialTemplate( + PotentialViewLocations(buildDescriptorParams.AreaName, buildDescriptorParams.ControllerName, buildDescriptorParams.ViewName), + descriptor.Templates, + searchedLocations)) + { + return null; + } + + if (!string.IsNullOrEmpty(buildDescriptorParams.MasterName)) + { + if (!LocatePotentialTemplate( + PotentialMasterLocations(buildDescriptorParams.AreaName, buildDescriptorParams.MasterName), + descriptor.Templates, + searchedLocations)) + { + return null; + } + } + else if (buildDescriptorParams.FindDefaultMaster && string.IsNullOrEmpty(TrailingUseMasterName(descriptor))) + { + LocatePotentialTemplate( + PotentialDefaultMasterLocations(buildDescriptorParams.AreaName, buildDescriptorParams.ControllerName), + descriptor.Templates, + null); + } + + var trailingUseMaster = TrailingUseMasterName(descriptor); + while (buildDescriptorParams.FindDefaultMaster && !string.IsNullOrEmpty(trailingUseMaster)) + { + if (!LocatePotentialTemplate( + PotentialMasterLocations(buildDescriptorParams.AreaName, trailingUseMaster), + descriptor.Templates, + searchedLocations)) + { + return null; + } + trailingUseMaster = TrailingUseMasterName(descriptor); + } + + return descriptor; + } + + + public string TrailingUseMasterName(SparkViewDescriptor descriptor) + { + var context = new VisitorContext + { + ViewFolder = _engine.ViewFolder, + SyntaxProvider = _engine.SyntaxProvider, + ViewPath = descriptor.Templates.Last() + }; + var chunks = _engine.SyntaxProvider.GetChunks(context, context.ViewPath); + var useMasterChunks = chunks.OfType(); + return useMasterChunks.Any() ? useMasterChunks.First().Name : null; + } + + private bool LocatePotentialTemplate( + IEnumerable potentialTemplates, + ICollection descriptorTemplates, + ICollection searchedLocations) + { + var template = potentialTemplates.FirstOrDefault(t => _engine.ViewFolder.HasView(t)); + if (template != null) + { + descriptorTemplates.Add(template); + return true; + } + if (searchedLocations != null) + { + foreach (var potentialTemplate in potentialTemplates) + searchedLocations.Add(potentialTemplate); + } + return false; + } + + protected virtual IEnumerable PotentialViewLocations(string areaName, string controllerName, string viewName) + { + return string.IsNullOrEmpty(areaName) + ? new[] + { + controllerName + "\\" + viewName + ".spark", + "Shared\\" + viewName + ".spark" + } + : new[] + { + areaName + "\\" + controllerName + "\\" + viewName + ".spark", + controllerName + "\\" + viewName + ".spark", + "Shared\\" + viewName + ".spark" + }; + } + + protected virtual IEnumerable PotentialMasterLocations(string areaName, string masterName) + { + return string.IsNullOrEmpty(areaName) + ? new[] + { + "Layouts\\" + masterName + ".spark", + "Shared\\" + masterName + ".spark" + } + : new[] + { + areaName + "\\Layouts\\" + masterName + ".spark", + areaName + "\\Shared\\" + masterName + ".spark", + "Layouts\\" + masterName + ".spark", + "Shared\\" + masterName + ".spark" + }; + } + + protected virtual IEnumerable PotentialDefaultMasterLocations(string areaName, string controllerName) + { + return string.IsNullOrEmpty(areaName) + ? new[] + { + "Layouts\\" + controllerName + ".spark", + "Shared\\" + controllerName + ".spark", + "Layouts\\Application.spark", + "Shared\\Application.spark" + } + : new[] + { + areaName + "\\Layouts\\" + controllerName + ".spark", + areaName + "\\Shared\\" + controllerName + ".spark", + areaName + "\\Layouts\\Application.spark", + areaName + "\\Shared\\Application.spark", + "Layouts\\" + controllerName + ".spark", + "Shared\\" + controllerName + ".spark", + "Layouts\\Application.spark", + "Shared\\Application.spark" + }; + } + } +} diff --git a/src/Spark.Web.Mvc/IDescriptorBuilder.cs b/src/Spark.Web.Mvc/IDescriptorBuilder.cs new file mode 100644 index 00000000..2ace170e --- /dev/null +++ b/src/Spark.Web.Mvc/IDescriptorBuilder.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; + +namespace Spark.Web.Mvc +{ + public interface IDescriptorBuilder + { + SparkViewDescriptor BuildDescriptor(BuildDescriptorParams buildDescriptorParams, ICollection searchedLocations); + } + + public class BuildDescriptorParams + { + private readonly string _targetNamespace; + private readonly string _areaName; + private readonly string _controllerName; + private readonly string _viewName; + private readonly string _masterName; + private readonly bool _findDefaultMaster; + + public BuildDescriptorParams(string targetNamespace, string areaName, string controllerName, string viewName, string masterName, bool findDefaultMaster) + { + _targetNamespace = targetNamespace; + _areaName = areaName; + _controllerName = controllerName; + _viewName = viewName; + _masterName = masterName; + _findDefaultMaster = findDefaultMaster; + } + + public string TargetNamespace + { + get { return _targetNamespace; } + } + + public string AreaName + { + get { return _areaName; } + } + + public string ControllerName + { + get { return _controllerName; } + } + + public string ViewName + { + get { return _viewName; } + } + + public string MasterName + { + get { return _masterName; } + } + + public bool FindDefaultMaster + { + get { return _findDefaultMaster; } + } + } +} \ No newline at end of file diff --git a/src/Spark.Web.Mvc/JavascriptViewResult.cs b/src/Spark.Web.Mvc/JavascriptViewResult.cs index 800928c3..ed71defd 100644 --- a/src/Spark.Web.Mvc/JavascriptViewResult.cs +++ b/src/Spark.Web.Mvc/JavascriptViewResult.cs @@ -41,12 +41,13 @@ public override void ExecuteResult(ControllerContext context) var searchedLocations = new List(); var factories = ViewEngines.Engines.OfType(); - if (factories.Count() == 0) + if (!factories.Any()) throw new CompilerException("No SparkViewFactory instances are registered"); - foreach(var factory in factories) + foreach (var factory in factories) { - var descriptor = factory.CreateDescriptorInternal("", AreaName, controllerName, ViewName, MasterName, false, searchedLocations); + var descriptor = factory.DescriptorBuilder.BuildDescriptor( + new BuildDescriptorParams("", AreaName, controllerName, ViewName, MasterName, false), searchedLocations); descriptor.Language = LanguageType.Javascript; var entry = factory.Engine.CreateEntry(descriptor); context.HttpContext.Response.ContentType = "text/javascript"; diff --git a/src/Spark.Web.Mvc/Spark.Web.Mvc.csproj b/src/Spark.Web.Mvc/Spark.Web.Mvc.csproj index 6b33d2a1..773cb43a 100644 --- a/src/Spark.Web.Mvc/Spark.Web.Mvc.csproj +++ b/src/Spark.Web.Mvc/Spark.Web.Mvc.csproj @@ -1,4 +1,4 @@ - + Debug @@ -64,6 +64,7 @@ Properties\CommonAssemblyInfo.cs + @@ -77,6 +78,7 @@ + diff --git a/src/Spark.Web.Mvc/SparkEngineStarter.cs b/src/Spark.Web.Mvc/SparkEngineStarter.cs index 0d71e7c6..02d2c8f8 100644 --- a/src/Spark.Web.Mvc/SparkEngineStarter.cs +++ b/src/Spark.Web.Mvc/SparkEngineStarter.cs @@ -29,6 +29,7 @@ public static class SparkEngineStarter public static void ConfigureContainer(ISparkServiceContainer container) { container.SetServiceBuilder(c => new SparkViewFactory(c.GetService())); + container.SetServiceBuilder(c => new DefaultDescriptorBuilder()); } /// diff --git a/src/Spark.Web.Mvc/SparkViewFactory.cs b/src/Spark.Web.Mvc/SparkViewFactory.cs index 480c2e3f..833a935e 100644 --- a/src/Spark.Web.Mvc/SparkViewFactory.cs +++ b/src/Spark.Web.Mvc/SparkViewFactory.cs @@ -18,7 +18,7 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Web; +using System.Threading; using System.Web.Mvc; using Spark.Compiler; using Spark.FileSystem; @@ -29,6 +29,7 @@ namespace Spark.Web.Mvc public class SparkViewFactory : IViewEngine, IViewFolderContainer, ISparkServiceInitialize { private ISparkViewEngine _engine; + private IDescriptorBuilder _descriptorBuilder; public SparkViewFactory() @@ -46,6 +47,7 @@ public virtual void Initialize(ISparkServiceContainer container) { Settings = container.GetService(); Engine = container.GetService(); + DescriptorBuilder = container.GetService(); } public ISparkSettings Settings { get; set; } @@ -67,6 +69,7 @@ public ISparkViewEngine Engine public void SetEngine(ISparkViewEngine engine) { + _descriptorBuilder = null; _engine = engine; if (_engine != null) { @@ -86,6 +89,17 @@ public IViewFolder ViewFolder set { Engine.ViewFolder = value; } } + public IDescriptorBuilder DescriptorBuilder + { + get + { + return _descriptorBuilder ?? + Interlocked.CompareExchange(ref _descriptorBuilder, new DefaultDescriptorBuilder(Engine), null) ?? + _descriptorBuilder; + } + set { _descriptorBuilder = value; } + } + public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName) { @@ -102,14 +116,6 @@ public virtual void ReleaseView(ControllerContext controllerContext, IView view) Engine.ReleaseInstance((ISparkView)view); } - private string GetAreaName(ControllerContext controllerContext) - { - object areaName; - return controllerContext.RouteData.Values.TryGetValue("area", out areaName) - ? Convert.ToString(areaName) - : null; - } - private ViewEngineResult FindViewInternal(ControllerContext controllerContext, string viewName, string masterName, bool findDefaultMaster) { var searchedLocations = new List(); @@ -129,23 +135,29 @@ private ViewEngineResult FindViewInternal(ControllerContext controllerContext, s } public SparkViewDescriptor CreateDescriptor( - ControllerContext controllerContext, + ControllerContext controllerContext, string viewName, - string masterName, + string masterName, bool findDefaultMaster, ICollection searchedLocations) { var targetNamespace = controllerContext.Controller.GetType().Namespace; - var areaName = GetAreaName(controllerContext); + + object areaValue; + var areaName = controllerContext.RouteData.Values.TryGetValue("area", out areaValue) + ? Convert.ToString(areaValue) + : null; + var controllerName = controllerContext.RouteData.GetRequiredString("controller"); - return CreateDescriptorInternal( - targetNamespace, - areaName, - controllerName, - viewName, - masterName, - findDefaultMaster, + return DescriptorBuilder.BuildDescriptor( + new BuildDescriptorParams( + targetNamespace, + areaName, + controllerName, + viewName, + masterName, + findDefaultMaster), searchedLocations); } @@ -153,9 +165,16 @@ private ViewEngineResult FindViewInternal(ControllerContext controllerContext, s string masterName, bool findDefaultMaster) { var searchedLocations = new List(); - var descriptor = CreateDescriptorInternal(targetNamespace, null, controllerName, viewName, masterName, - findDefaultMaster, - searchedLocations); + var descriptor = DescriptorBuilder.BuildDescriptor( + new BuildDescriptorParams( + targetNamespace, + null /*areaName*/, + controllerName, + viewName, + masterName, + findDefaultMaster), + searchedLocations); + if (descriptor == null) { throw new CompilerException("Unable to find templates at " + @@ -164,125 +183,6 @@ private ViewEngineResult FindViewInternal(ControllerContext controllerContext, s return descriptor; } - internal SparkViewDescriptor CreateDescriptorInternal(string targetNamespace, string areaName, string controllerName, string viewName, string masterName, bool findDefaultMaster, ICollection searchedLocations) - { - var descriptor = new SparkViewDescriptor - { - TargetNamespace = targetNamespace - }; - - if (!LocatePotentialTemplate( - PotentialViewLocations(areaName, controllerName, viewName), - descriptor.Templates, - searchedLocations)) - { - return null; - } - - if (!string.IsNullOrEmpty(masterName)) - { - if (!LocatePotentialTemplate( - PotentialMasterLocations(areaName, masterName), - descriptor.Templates, - searchedLocations)) - { - return null; - } - } - else if (findDefaultMaster) - { - LocatePotentialTemplate( - PotentialDefaultMasterLocations(areaName, controllerName), - descriptor.Templates, - null); - } - - return descriptor; - } - - private bool LocatePotentialTemplate( - IEnumerable potentialTemplates, - ICollection descriptorTemplates, - ICollection searchedLocations) - { - var template = potentialTemplates.FirstOrDefault(t => ViewFolder.HasView(t)); - if (template != null) - { - descriptorTemplates.Add(template); - return true; - } - if (searchedLocations != null) - { - foreach (var potentialTemplate in potentialTemplates) - searchedLocations.Add(potentialTemplate); - } - return false; - } - - protected virtual IEnumerable PotentialViewLocations(string areaName, string controllerName, string viewName) - { - if (string.IsNullOrEmpty(areaName)) - { - return new[] - { - controllerName + "\\" + viewName + ".spark", - "Shared\\" + viewName + ".spark" - }; - - } - - return new[] - { - areaName + "\\" + controllerName + "\\" + viewName + ".spark", - controllerName + "\\" + viewName + ".spark", - "Shared\\" + viewName + ".spark" - }; - } - - protected virtual IEnumerable PotentialMasterLocations(string areaName, string masterName) - { - if (string.IsNullOrEmpty(areaName)) - { - return new[] - { - "Layouts\\" + masterName + ".spark", - "Shared\\" + masterName + ".spark" - }; - } - return new[] - { - areaName + "\\Layouts\\" + masterName + ".spark", - areaName + "\\Shared\\" + masterName + ".spark", - "Layouts\\" + masterName + ".spark", - "Shared\\" + masterName + ".spark" - }; - } - - protected virtual IEnumerable PotentialDefaultMasterLocations(string areaName, string controllerName) - { - if (string.IsNullOrEmpty(areaName)) - { - return new[] - { - "Layouts\\" + controllerName + ".spark", - "Shared\\" + controllerName + ".spark", - "Layouts\\Application.spark", - "Shared\\Application.spark" - }; - - } - return new[] - { - areaName + "\\Layouts\\" + controllerName + ".spark", - areaName + "\\Shared\\" + controllerName + ".spark", - areaName + "\\Layouts\\Application.spark", - areaName + "\\Shared\\Application.spark", - "Layouts\\" + controllerName + ".spark", - "Shared\\" + controllerName + ".spark", - "Layouts\\Application.spark", - "Shared\\Application.spark" - }; - } public Assembly Precompile(SparkBatchDescriptor batch) { diff --git a/src/Spark/CompiledViewEntry.cs b/src/Spark/CompiledViewEntry.cs new file mode 100644 index 00000000..5dcab51d --- /dev/null +++ b/src/Spark/CompiledViewEntry.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using Spark.Compiler; +using Spark.Parser; + +namespace Spark +{ + public class CompiledViewEntry : ISparkViewEntry + { + public Guid ViewId { get { return Compiler.GeneratedViewId; } } + + public SparkViewDescriptor Descriptor { get; set; } + public ViewLoader Loader { get; set; } + public ViewCompiler Compiler { get; set; } + public IViewActivator Activator { get; set; } + public ISparkLanguageFactory LanguageFactory { get; set; } + + public string SourceCode + { + get { return Compiler.SourceCode; } + } + + public IList SourceMappings + { + get { return Compiler.SourceMappings; } + } + + public ISparkView CreateInstance() + { + var view = Activator.Activate(Compiler.CompiledType); + if (LanguageFactory != null) + LanguageFactory.InstanceCreated(Compiler, view); + return view; + } + + public void ReleaseInstance(ISparkView view) + { + if (LanguageFactory != null) + LanguageFactory.InstanceReleased(Compiler, view); + Activator.Release(Compiler.CompiledType, view); + } + + //TODO: refactor see #82 + public bool IsCurrent() { return Loader.IsCurrent(); } + } +} \ No newline at end of file diff --git a/src/Spark/CompiledViewHolder.cs b/src/Spark/CompiledViewHolder.cs index 2a528629..1df6995a 100644 --- a/src/Spark/CompiledViewHolder.cs +++ b/src/Spark/CompiledViewHolder.cs @@ -25,7 +25,7 @@ public class CompiledViewHolder { static private CompiledViewHolder _current; - readonly Dictionary _cache = new Dictionary(); + readonly Dictionary _cache = new Dictionary(); public static CompiledViewHolder Current { @@ -38,109 +38,35 @@ public static CompiledViewHolder Current set { _current = value; } } - public Entry Lookup(Key key) + public ISparkViewEntry Lookup(SparkViewDescriptor descriptor) { - Entry entry; + ISparkViewEntry entry; lock (_cache) { - if (!_cache.TryGetValue(key, out entry)) + if (!_cache.TryGetValue(descriptor, out entry)) return null; } - return entry.Loader.IsCurrent() ? entry : null; + return entry.IsCurrent() ? entry : null; } - public Entry Lookup(Guid viewId) + public ISparkViewEntry Lookup(Guid viewId) { lock (_cache) { - return _cache.Values.FirstOrDefault(e => e.Compiler.GeneratedViewId == viewId); + return _cache.Values.FirstOrDefault(e => e.ViewId == viewId); } } - public void Store(Entry entry) + public void Store(ISparkViewEntry entry) { lock (_cache) { - _cache[entry.Key] = entry; + _cache[entry.Descriptor] = entry; } } - public class Key - { - public SparkViewDescriptor Descriptor { get; set; } - - public override int GetHashCode() - { - int hashCode = 0; - - hashCode ^= (Descriptor.TargetNamespace ?? "").GetHashCode(); - - foreach (var template in Descriptor.Templates) - hashCode ^= template.ToLowerInvariant().GetHashCode(); - - return hashCode; - } - - public override bool Equals(object obj) - { - var that = obj as Key; - if (that == null || GetType() != that.GetType()) - return false; - if (!string.Equals(Descriptor.TargetNamespace, that.Descriptor.TargetNamespace)) - return false; - if (Descriptor.Templates.Count != that.Descriptor.Templates.Count) - return false; - for (int index = 0; index != Descriptor.Templates.Count; ++index) - { - if (!string.Equals(Descriptor.Templates[index], that.Descriptor.Templates[index], StringComparison.InvariantCultureIgnoreCase)) - { - return false; - } - } - return true; - } - } - - public class Entry : ISparkViewEntry - { - public Key Key { get; set; } - public ViewLoader Loader { get; set; } - public ViewCompiler Compiler { get; set; } - public IViewActivator Activator { get; set; } - public ISparkLanguageFactory LanguageFactory { get; set; } - - public SparkViewDescriptor Descriptor - { - get { return Key.Descriptor; } - } - - public string SourceCode - { - get { return Compiler.SourceCode; } - } - - public IList SourceMappings - { - get { return Compiler.SourceMappings; } - } - - public ISparkView CreateInstance() - { - var view = Activator.Activate(Compiler.CompiledType); - if (LanguageFactory != null) - LanguageFactory.InstanceCreated(Compiler, view); - return view; - } - - public void ReleaseInstance(ISparkView view) - { - if (LanguageFactory != null) - LanguageFactory.InstanceReleased(Compiler, view); - Activator.Release(Compiler.CompiledType, view); - } - } } } diff --git a/src/Spark/ISparkViewEngine.cs b/src/Spark/ISparkViewEngine.cs index db002e08..c077f782 100644 --- a/src/Spark/ISparkViewEngine.cs +++ b/src/Spark/ISparkViewEngine.cs @@ -27,6 +27,7 @@ public interface ISparkViewEngine ISparkExtensionFactory ExtensionFactory { get; set; } IViewActivatorFactory ViewActivatorFactory { get; set; } string DefaultPageBaseType { get; set; } + ISparkSyntaxProvider SyntaxProvider { get; set; } ISparkViewEntry GetEntry(SparkViewDescriptor descriptor); ISparkViewEntry CreateEntry(SparkViewDescriptor descriptor); diff --git a/src/Spark/ISparkViewEntry.cs b/src/Spark/ISparkViewEntry.cs index 512c84ab..9fbe926a 100644 --- a/src/Spark/ISparkViewEntry.cs +++ b/src/Spark/ISparkViewEntry.cs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // +using System; using System.Collections.Generic; using Spark.Compiler; @@ -25,5 +26,8 @@ public interface ISparkViewEntry string SourceCode { get; } IList SourceMappings { get; } + + Guid ViewId { get; } + bool IsCurrent(); } } diff --git a/src/Spark/Spark.csproj b/src/Spark/Spark.csproj index 895a2b49..4fbbdd66 100644 --- a/src/Spark/Spark.csproj +++ b/src/Spark/Spark.csproj @@ -61,6 +61,7 @@ Properties\CommonAssemblyInfo.cs + diff --git a/src/Spark/SparkViewDescriptor.cs b/src/Spark/SparkViewDescriptor.cs index ec99996c..eea3a0ff 100644 --- a/src/Spark/SparkViewDescriptor.cs +++ b/src/Spark/SparkViewDescriptor.cs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // +using System; using System.Collections.Generic; namespace Spark @@ -69,5 +70,43 @@ public SparkViewDescriptor AddAccessor(string property, string getValue) Accessors.Add(new Accessor { Property = property, GetValue = getValue }); return this; } + + public override int GetHashCode() + { + int hashCode = 0; + + hashCode ^= (TargetNamespace ?? "").GetHashCode(); + + foreach (var template in Templates) + hashCode ^= template.ToLowerInvariant().GetHashCode(); + + return hashCode; + } + + public override bool Equals(object obj) + { + var that = obj as SparkViewDescriptor; + + if (that == null || GetType() != that.GetType()) + return false; + + if (!string.Equals(TargetNamespace, that.TargetNamespace)) + return false; + + if (Templates.Count != that.Templates.Count) + return false; + + for (var index = 0; index != Templates.Count; ++index) + { + if (!string.Equals(Templates[index], that.Templates[index], StringComparison.InvariantCultureIgnoreCase)) + { + return false; + } + } + return true; + } + + + } } diff --git a/src/Spark/SparkViewEngine.cs b/src/Spark/SparkViewEngine.cs index 21819764..c8e09cde 100644 --- a/src/Spark/SparkViewEngine.cs +++ b/src/Spark/SparkViewEngine.cs @@ -48,8 +48,6 @@ public SparkViewEngine(ISparkSettings settings) public void Initialize(ISparkServiceContainer container) { - _container = container; - Settings = container.GetService(); SyntaxProvider = container.GetService(); ViewActivatorFactory = container.GetService(); @@ -59,8 +57,6 @@ public void Initialize(ISparkServiceContainer container) SetViewFolder(container.GetService()); } - private ISparkServiceContainer _container; - private IViewFolder _viewFolder; public IViewFolder ViewFolder { @@ -189,20 +185,7 @@ public ITemplateLocator TemplateLocator public ISparkViewEntry GetEntry(SparkViewDescriptor descriptor) { - var key = CreateKey(descriptor); - return CompiledViewHolder.Current.Lookup(key); - } - - public ISparkViewEntry CreateEntry(SparkViewDescriptor descriptor) - { - var key = CreateKey(descriptor); - var entry = CompiledViewHolder.Current.Lookup(key); - if (entry == null) - { - entry = CreateEntry(key); - CompiledViewHolder.Current.Store(entry); - } - return entry; + return CompiledViewHolder.Current.Lookup(descriptor); } public ISparkView CreateInstance(SparkViewDescriptor descriptor) @@ -219,33 +202,32 @@ public void ReleaseInstance(ISparkView view) entry.ReleaseInstance(view); } - public CompiledViewHolder.Key CreateKey(SparkViewDescriptor descriptor) + public ISparkViewEntry CreateEntry(SparkViewDescriptor descriptor) { - return new CompiledViewHolder.Key - { - Descriptor = descriptor - }; + var entry = CompiledViewHolder.Current.Lookup(descriptor); + if (entry == null) + { + entry = CreateEntryInternal(descriptor, true); + CompiledViewHolder.Current.Store(entry); + } + return entry; } - public CompiledViewHolder.Entry CreateEntry(CompiledViewHolder.Key key) - { - return CreateEntry(key, true); - } - - public CompiledViewHolder.Entry CreateEntry(CompiledViewHolder.Key key, bool compile) + + public ISparkViewEntry CreateEntryInternal(SparkViewDescriptor descriptor, bool compile) { - var entry = new CompiledViewHolder.Entry + var entry = new CompiledViewEntry { - Key = key, + Descriptor = descriptor, Loader = CreateViewLoader(), - Compiler = LanguageFactory.CreateViewCompiler(this, key.Descriptor), + Compiler = LanguageFactory.CreateViewCompiler(this, descriptor), LanguageFactory = LanguageFactory }; var chunksLoaded = new List>(); var templatesLoaded = new List(); - LoadTemplates(entry.Loader, key.Descriptor.Templates, chunksLoaded, templatesLoaded); + LoadTemplates(entry.Loader, entry.Descriptor.Templates, chunksLoaded, templatesLoaded); if (compile) { @@ -261,7 +243,7 @@ public CompiledViewHolder.Entry CreateEntry(CompiledViewHolder.Key key, bool com return entry; } - void LoadTemplates(ViewLoader loader, IList templates, IList> chunksLoaded, IList templatesLoaded) + void LoadTemplates(ViewLoader loader, IEnumerable templates, ICollection> chunksLoaded, ICollection templatesLoaded) { foreach (var template in templates) { @@ -332,14 +314,14 @@ public Assembly BatchCompilation(IList descriptors) public Assembly BatchCompilation(string outputAssembly, IList descriptors) { - var batch = new List(); + var batch = new List(); var sourceCode = new List(); foreach (var descriptor in descriptors) { - var entry = new CompiledViewHolder.Entry + var entry = new CompiledViewEntry { - Key = CreateKey(descriptor), + Descriptor = descriptor, Loader = CreateViewLoader(), Compiler = LanguageFactory.CreateViewCompiler(this, descriptor) }; @@ -381,9 +363,9 @@ public IList LoadBatchCompilation(Assembly assembly) var descriptor = ((SparkViewAttribute)attributes[0]).BuildDescriptor(); - var entry = new CompiledViewHolder.Entry + var entry = new CompiledViewEntry { - Key = new CompiledViewHolder.Key { Descriptor = descriptor }, + Descriptor = descriptor, Loader = new ViewLoader(), Compiler = new DefaultViewCompiler { CompiledType = type }, Activator = ViewActivatorFactory.Register(type)