├── .gitattributes ├── .gitignore ├── README.md └── Source ├── Xipton.Razor.Example ├── App.config ├── Embedded │ └── embeddedTemplate.cshtml ├── Models │ ├── MyProductList.cs │ └── Person.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Views │ ├── Other │ │ └── html.cshtml │ ├── Reports │ │ ├── Products.cshtml │ │ ├── _ViewStart.cshtml │ │ └── _productGrid.cshtml │ ├── Shared │ │ ├── _Layout1.cshtml │ │ └── _Layout2.cshtml │ └── _ViewStart.cshtml ├── Web.config ├── Xipton.Razor.Example.csproj └── packages.config ├── Xipton.Razor.UnitTest ├── App.config ├── ConfigTest.cs ├── LiteralStringTest.cs ├── Properties │ └── AssemblyInfo.cs ├── TemplateTest.cs ├── ViewBagTest.cs ├── VirtualPathBuilderTest.cs ├── Xipton.Razor.UnitTest.csproj ├── legacy │ └── web.config ├── packages.config └── unmanaged.dll ├── Xipton.Razor.sln └── Xipton.Razor ├── Config ├── ConfigElement.cs ├── IRazorConfigInitializer.cs ├── RazorConfig.cs ├── RootOperatorElement.cs ├── TemplateConfigurationException.cs ├── TemplatesElement.cs ├── XElementExtension.cs ├── XmlConfigurationSection.cs └── xipton.razor.config ├── Core ├── ContentManager.cs ├── ContentModifiedArgs.cs ├── ContentProvider │ ├── CompositeContentProvider.cs │ ├── EmbeddedResourceContentProvider.cs │ ├── FileContentProvider.cs │ └── MemoryContentProvider.cs ├── DynamicData.cs ├── Generator │ ├── CSharp │ │ ├── XiptonCSharpCodeLanguage.cs │ │ └── XiptonCSharpCodeParser.cs │ ├── IXiptonCodeLanguage.cs │ ├── SetModelCodeGenerator.cs │ ├── VB │ │ ├── XiptonVBCodeLanguage.cs │ │ └── XiptonVBCodeParser.cs │ └── XiptonEngineHost.cs ├── HelperResult.cs ├── IContentProvider.cs ├── ITemplateController.cs ├── TemplateBindingException.cs ├── TemplateCompileException.cs ├── TemplateException.cs ├── TemplateFactory.cs ├── TemplateParseException.cs ├── TemplateTreeException.cs └── VirtualPathBuilder.cs ├── Extension ├── AppDomainExtension.cs ├── AssemblyExtension.cs ├── ObjectExtension.cs ├── StringExtension.cs ├── TemplateExtension.cs └── TypeExtension.cs ├── ILiteralString.cs ├── IRazorMachine.cs ├── ITemplate.cs ├── ITemplate`1.cs ├── LiteralString.cs ├── Properties └── AssemblyInfo.cs ├── RazorContext.cs ├── RazorMachine.cs ├── TemplateBase.cs ├── TemplateBase`1.cs ├── Xipton.Razor.Overview.cd ├── Xipton.Razor.csproj ├── legacy └── web.config └── packages.config /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | bin 3 | obj 4 | 5 | # Resharper folder 6 | _* 7 | 8 | # mstest test results 9 | TestResults 10 | 11 | 12 | # User files # 13 | ################### 14 | *.user 15 | *.suo 16 | 17 | 18 | # Logs and databases # 19 | ###################### 20 | *.log 21 | *.sql 22 | *.sqlite 23 | 24 | # OS generated files # 25 | ###################### 26 | .DS_Store 27 | .DS_Store? 28 | ._* 29 | .Spotlight-V100 30 | .Trashes 31 | Icon? 32 | ehthumbs.db 33 | Thumbs.db 34 | 35 | [Rr]elease*/ 36 | _ReSharper*/ 37 | [Tt]est[Rr]esult* 38 | packages/ 39 | [Hh]elp/ 40 | *.scc 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## RazorMachine ### 2 | 3 | var rm = new RazorMachine(); 4 | var result = 5 | rm.Execute("Hello @Model.FirstName @Model.LastName", new {FirstName="John", LastName="Smith"}); 6 | Console.WriteLine(result); 7 | 8 | RazorMachine is a robust and easy to use .Net Razor v2/v3 template engine. The master branch uses Razor v3. This implementation supports layouts (masterpages) and a _viewStart construct, just like MVC does support these features. The RazorEngine works independently from MVC. It only needs the System.Web.Razor reference. It almost works exacly like Asp.Net MVC. Take a look at https://github.com/jlamfers/RazorMachine/wiki/Examples to see how easily this framework works. 9 | 10 | This RazorEngine originally was published at CodeProject 11 | 12 | ## Install ## 13 | 14 | There is a package available at NuGet. To install RazorMachine using NuGet, run the following command in the Package Manager Console 15 | ``` 16 | PM> Install-Package RazorMachine 17 | ``` 18 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/App.config: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Embedded/embeddedTemplate.cshtml: -------------------------------------------------------------------------------- 1 | @model Xipton.Razor.Example.Models.Person 2 | EMBEDDED RESOURCE TEMPLATE SAYS: Hello @Model.FirstName @Model.LastName 3 | @{ 4 | } 5 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Models/MyProductList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Xipton.Razor.Example.Models { 4 | public class MyProduct 5 | { 6 | public string Name { get; set; } 7 | public double Price { get; set; } 8 | } 9 | 10 | public class MyProductList : List { 11 | public MyProductList() 12 | { 13 | Add("Toyota ", 20.245) 14 | .Add("Opel ", 12.938) 15 | .Add("BMW ", 24.837) 16 | .Add("Skoda ", 19.298); 17 | } 18 | 19 | private MyProductList Add(string name, double price){ 20 | Add(new MyProduct{Name = name, Price = price}); 21 | return this; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Models/Person.cs: -------------------------------------------------------------------------------- 1 | namespace Xipton.Razor.Example.Models { 2 | public class Person { 3 | public string FirstName { get; set; } 4 | public string LastName { get; set; } 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Xipton.Razor.Example.Models; 4 | 5 | namespace Xipton.Razor.Example { 6 | class Program { 7 | 8 | // since the with the first examples we do not want to use any file content from the 9 | // ./Views folder so we remove all content providers and thus the file content provider as well 10 | private static RazorMachine _rm = CreateRazorMachineWithoutContentProviders(); 11 | 12 | static void Main(string[] args) { 13 | 14 | // Examples from the CodeProject article 15 | VerifyExample(1); 16 | Example1(); 17 | VerifyExample(2); 18 | Example2(); 19 | VerifyExample(3); 20 | Example3(); 21 | VerifyExample(4); 22 | Example4(); 23 | VerifyExample(5); 24 | Example5(); 25 | VerifyExample(6); 26 | Example6(); 27 | VerifyExample(7); 28 | Example7(); 29 | VerifyExample(8); 30 | Example8(); 31 | VerifyExample(9); 32 | Example9(); 33 | VerifyExample(10); 34 | Example10(); 35 | 36 | VerifyExample("RunEmbeddedTemplate"); 37 | RunEmbeddedTemplate(); 38 | 39 | VerifyExample("RunProductListTemplateFromFile"); 40 | RunProductListTemplateFromFile(); 41 | 42 | VerifyExample("RunHtmlTemplate"); 43 | RunHtmlTemplate(); 44 | 45 | 46 | Console.Write("Completed all examples. Press any key to exit..."); 47 | Console.ReadLine(); 48 | } 49 | 50 | private static void VerifyExample(int exampleNr) { 51 | VerifyExample(exampleNr.ToString()); 52 | } 53 | private static void VerifyExample(string name) { 54 | Console.WriteLine("\r\n***** Press [Enter] to run example {0} or [Esc] to exit.\r\n",name); 55 | if (Console.ReadKey().Key == ConsoleKey.Escape){ 56 | Environment.Exit(0); 57 | } 58 | 59 | } 60 | 61 | private static RazorMachine CreateRazorMachineWithoutContentProviders(bool includeGeneratedSourceCode=false, string rootOperatorPath = null, bool htmlEncode = true) { 62 | var rm = new RazorMachine(includeGeneratedSourceCode: includeGeneratedSourceCode, htmlEncode: htmlEncode, rootOperatorPath: rootOperatorPath); 63 | rm.Context.TemplateFactory.ContentManager.ClearAllContentProviders(); 64 | return rm; 65 | } 66 | 67 | // Example 1 - Executing a template 68 | public static void Example1() { 69 | ITemplate template = _rm.ExecuteContent("Razor says: Hello @Model.FirstName @Model.LastName", new { FirstName = "John", LastName = "Smith" }); 70 | Console.WriteLine(template.Result); 71 | } 72 | 73 | // Example 2 - Executing a template using a layout 74 | public static void Example2() { 75 | _rm.RegisterTemplate("~/shared/_layout.cshtml", "BEGIN TEMPLATE \r\n @RenderBody() \r\nEND TEMPLATE"); 76 | ITemplate template = _rm.ExecuteContent("@{Layout=\"_layout\";} Razor says: Hello @Model.FirstName @Model.LastName", new { FirstName = "John", LastName = "Smith" }); 77 | Console.WriteLine(template); // template.ToString() evaluates to template.Result 78 | } 79 | 80 | //Example 3 - Executing a template using a layout and _viewStart 81 | public static void Example3() { 82 | _rm.RegisterTemplate("~/shared/_layout.cshtml", "BEGIN TEMPLATE \r\n @RenderBody() \r\nEND TEMPLATE"); 83 | _rm.RegisterTemplate("~/_viewstart.cshtml", "@{Layout=\"_layout\";}"); 84 | ITemplate template = _rm.ExecuteContent("Razor says: Hello @Model.FirstName @Model.LastName", new { FirstName = "John", LastName = "Smith" }); 85 | Console.WriteLine(template); // same result as example 2 86 | } 87 | 88 | //Example 4 - Executing a template by a virtual path using a layout and _viewStart 89 | public static void Example4() { 90 | 91 | _rm.RegisterTemplate("~/shared/_layout.cshtml", "BEGIN TEMPLATE \r\n @RenderBody() \r\nEND TEMPLATE"); 92 | _rm.RegisterTemplate("~/_viewstart.cshtml", "@{Layout=\"_layout\";}"); 93 | _rm.RegisterTemplate("~/simpleTemplate.cshtml", "Razor says: Hello @Model.FirstName @Model.LastName"); 94 | 95 | ITemplate template = _rm.ExecuteUrl("~/simpleTemplate.cshtml", new { FirstName = "John", LastName = "Smith" }); 96 | Console.WriteLine(template); // same result as example 2 97 | 98 | } 99 | 100 | //Example 5 - Returning information from anywhere (even from a layout) to the caller using the ViewBag 101 | public static void Example5() { 102 | _rm.RegisterTemplate("~/shared/_layout.cshtml", "@{ViewBag.PiValue=3.1415927;}"); 103 | _rm.RegisterTemplate("~/_viewstart.cshtml", "@{Layout=\"_layout\";}"); 104 | ITemplate template = _rm.ExecuteContent("Anything"); 105 | Console.WriteLine(template.ViewBag.PiValue); // => writes 3.1415927 106 | } 107 | 108 | //Example 6 - Adding information from anywhere to a predefined ViewBag and returning it to the caller 109 | public static void Example6() { 110 | _rm.RegisterTemplate("~/shared/_layout.cshtml", "@{ViewBag.Values.Add(3.1415927);}"); 111 | _rm.RegisterTemplate("~/_viewstart.cshtml", "@{Layout=\"_layout\";}"); 112 | ITemplate template = _rm.ExecuteContent("Anything", viewbag: new { Values = new List { 0, 1, 2 } }); 113 | Console.WriteLine(template.ViewBag.Values[3]); // => writes 3.1415927 114 | } 115 | 116 | //Example 7 - Show generated code 117 | public static void Example7() { 118 | var rm = CreateRazorMachineWithoutContentProviders(includeGeneratedSourceCode: true); 119 | rm.RegisterTemplate("~/shared/_layout.cshtml", "BEGIN TEMPLATE \r\n @RenderBody() \r\nEND TEMPLATE"); 120 | rm.RegisterTemplate("~/_viewstart.cshtml", "@{Layout=\"_layout\";}"); 121 | ITemplate template = rm.ExecuteContent("Razor says: Hello @Model.FirstName @Model.LastName", new { FirstName = "John", LastName = "Smith" }); 122 | Console.WriteLine(template); // writes output result 123 | Console.WriteLine(template.GeneratedSourceCode); // writes generated source for template 124 | Console.WriteLine(template.Childs[0].GeneratedSourceCode); // writes generated source for layout 125 | } 126 | 127 | //Example 8 - HTML encoding 128 | public static void Example8() { 129 | _rm.RegisterTemplate("~/shared/_layout.cshtml", "@RenderBody()"); // replace _layout by "RenderBody only" template to ensure output 130 | 131 | // not encoded since all output is literal content 132 | Console.WriteLine(_rm.ExecuteContent("Tom & Jerry").Result); 133 | 134 | // encoded since the content is written as a string value 135 | // and by default HtmlEncode is on for written content 136 | Console.WriteLine(_rm.ExecuteContent("@Model.Text", new { Text = "Tom & Jerry" }).Result); 137 | 138 | // not encoded since content is a written as a raw string 139 | Console.WriteLine(_rm.ExecuteContent("@Raw(Model.Text)", new { Text = "Tom & Jerry" }).Result); 140 | 141 | // not encoded since HtmlEncoding is turend off in code 142 | Console.WriteLine(_rm.ExecuteContent("@{HtmlEncode=false;} @Model.Text", new { Text = "Tom & Jerry" }).Result); 143 | 144 | var rm = CreateRazorMachineWithoutContentProviders(htmlEncode: false); 145 | rm.Context.TemplateFactory.ContentManager.ClearAllContentProviders(); 146 | // not encoded since now html encoding if off by default, still you can set it on in code 147 | Console.WriteLine(rm.ExecuteContent("@Model.Text", new { Text = "Tom & Jerry" }).Result); 148 | } 149 | 150 | //Example 9 - Root operator is resolved directly 151 | public static void Example9() { 152 | var rm = CreateRazorMachineWithoutContentProviders(rootOperatorPath: "/MyAppName"); 153 | rm.RegisterTemplate("~/MyTemplate", "Some Link"); 154 | var template = rm.ExecuteUrl("/MyAppName/MyTemplate"); 155 | // same result as: 156 | template = rm.ExecuteUrl("~/MyTemplate"); 157 | Console.WriteLine(template); // writes: Some Link 158 | 159 | } 160 | 161 | //Example 10 - Razor 2: Attributes with null values are not rendered 162 | public static void Example10() { 163 | var template = _rm.ExecuteContent("Some Link", new { Brand = "Toyota", NullValue = (string)null }); 164 | Console.WriteLine(template); // writes: Some Link 165 | } 166 | 167 | 168 | // ********************* Other examples ********************************************************************************* // 169 | 170 | public static void RunEmbeddedTemplate() { 171 | // this example only works if the embedded content provider has been configured. Here it is configured at app.config, includeGeneratedSourceCode is forced to true 172 | var rm = new RazorMachine(includeGeneratedSourceCode:true); // => default configuration from app.config is loaded 173 | var t = rm.ExecuteUrl("~/embeddedTemplate"); 174 | Console.WriteLine("Generated source code:"); 175 | Console.WriteLine(t.GeneratedSourceCode); 176 | Console.WriteLine("Generated output:"); 177 | Console.WriteLine(t); // => writes the embedded template's result 178 | //NOTE: since now all content providers (file content providers as well) are loaded the ~/ViewStart.cshtml from the ./Views folder is executed as well 179 | } 180 | 181 | /// 182 | /// This example creates a product list using a simple product grid productGrid (at ./Views/Reports/). 183 | /// The grid implicitly "knows" the product model because the grid is a child of Products, which is executed together with the MyProductList(). 184 | /// The layout is _layout2 which is set in memory at "~/_ViewStart", thus override the file ./Views/_ViewStart.cshtml 185 | /// 186 | public static void RunProductListTemplateFromFile() { 187 | var rm = new RazorMachine(includeGeneratedSourceCode: true); // => default configuration from app.config is loaded, includeGeneratedSourceCode is forced to true 188 | rm.RegisterTemplate("~/_ViewStart", "@{Layout=\"_Layout2\";}"); // => change (override) layout in memory to _layout2 189 | 190 | var t = rm.ExecuteUrl("~/Reports/Products", new MyProductList()); 191 | 192 | Console.WriteLine("Generated template source code:"); 193 | Console.WriteLine(t.GeneratedSourceCode); 194 | 195 | Console.WriteLine("Generated layout source code:"); 196 | Console.WriteLine(t.Childs[t.Childs.Count - 1].GeneratedSourceCode); // layout always is the last child bacause it is rendered at last 197 | 198 | Console.WriteLine("Generated grid source code:"); 199 | Console.WriteLine(t.Childs[0].GeneratedSourceCode); 200 | 201 | Console.WriteLine("Generated output:"); 202 | Console.WriteLine(t); // => writes the embedded template's result 203 | } 204 | 205 | public static void RunHtmlTemplate(){ 206 | Console.WriteLine(new RazorMachine(includeGeneratedSourceCode: true).ExecuteUrl("/Other/Html",new{FirstName="Dick",LastName="Tracy"})); 207 | } 208 | 209 | 210 | 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Xipton.Razor.Example")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Xipton")] 12 | [assembly: AssemblyProduct("Xipton.Razor.Example")] 13 | [assembly: AssemblyCopyright("Copyright © 2013")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("ad0f2ba7-e72e-4a4c-b2dd-84557e785d80")] 24 | 25 | [assembly: AssemblyVersion("3.1.0.0")] 26 | [assembly: AssemblyFileVersion("3.1.0.0")] 27 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Views/Other/html.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | @{this.HtmlEncode = true; // default value can be configured} 4 | 5 | } 6 | @*Razor 2 feature: root operator is resolved directly *@ 7 | 8 | 9 | @*Razor 2 related feature: conditional attributes: attributes are not rendered if the correponding value is null or empty*@ 10 | @{ 11 | string nullValue = null; 12 | } 13 |
14 | 15 | Hello @Model.FirstName @Model.LastName 16 | 17 | Hello Tom & Jerry 18 | @("Hello Tom & Jerry") 19 | @Raw("Hello Tom & Jerry") 20 | 21 | The following C# code was generated for this template: 22 | @Raw(GeneratedSourceCode) 23 | 24 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Views/Reports/Products.cshtml: -------------------------------------------------------------------------------- 1 | @* intellisense works *@ 2 | Overview of products at @DateTime.Now 3 | @RenderPage("_productGrid",skipLayout:true) @* layout is skipped (as an example; it is disabled at _productGrid as well. *@ 4 | 5 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Views/Reports/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | var br = Environment.NewLine; 3 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Views/Reports/_productGrid.cshtml: -------------------------------------------------------------------------------- 1 | @using Xipton.Razor.Example.Models 2 | @model MyProductList 3 | 4 | @{Layout = null; // enforce to disable any layout setting 5 | } 6 | 7 | @* intellisense should work at this template *@ 8 | 9 | Product list: 10 | Name Price 11 | --------------- 12 | @foreach(var m in Model){ 13 | @m.Name @m.Price@br 14 | } 15 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Views/Shared/_Layout1.cshtml: -------------------------------------------------------------------------------- 1 | BEGIN LAYOUT @VirtualPath 2 | (at @RazorContext.TemplateFactory.ContentManager.TryGetResourceName(VirtualPath)) 3 | @RenderBody() 4 | END -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Views/Shared/_Layout2.cshtml: -------------------------------------------------------------------------------- 1 | BEGIN _Layout2 2 | @RenderBody() 3 | END _Layout2 -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | // content of ViewStart.cshtml at file system 3 | Layout = "_layout1"; @* layout1 is at the shared folder *@ 4 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/Xipton.Razor.Example.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {B6E046A6-4EBC-48EC-A454-8399AB3DD9AF} 9 | Exe 10 | Properties 11 | Xipton.Razor.Example 12 | Xipton.Razor.Example 13 | v4.5 14 | 15 | 16 | 512 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | false 28 | 29 | 30 | x86 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | false 38 | 39 | 40 | 41 | 42 | 43 | False 44 | ..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Code 54 | 55 | 56 | 57 | 58 | Designer 59 | 60 | 61 | 62 | 63 | PreserveNewest 64 | 65 | 66 | PreserveNewest 67 | 68 | 69 | PreserveNewest 70 | 71 | 72 | PreserveNewest 73 | 74 | 75 | Always 76 | 77 | 78 | PreserveNewest 79 | 80 | 81 | PreserveNewest 82 | 83 | 84 | 85 | 86 | Designer 87 | 88 | 89 | 90 | 91 | {3a7a2f2f-0b57-47f9-8e1a-d34ecb961831} 92 | Xipton.Razor 93 | 94 | 95 | 96 | 103 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.Example/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.UnitTest/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.UnitTest/ConfigTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using Xipton.Razor.Config; 7 | 8 | namespace Xipton.Razor.UnitTest { 9 | [TestClass] 10 | public class ConfigTest { 11 | 12 | [TestMethod] 13 | public void ConfigCanBeCreated(){ 14 | var config = new RazorConfig(); 15 | Assert.IsNotNull(config.ContentProviders); 16 | Assert.IsNotNull(config.Namespaces); 17 | Assert.IsNotNull(config.References); 18 | Assert.IsNotNull(config.RootOperator); 19 | Assert.IsNotNull(config.Templates); 20 | Assert.IsTrue(config.References.Any(r => r.Contains("*"))); 21 | } 22 | [TestMethod] 23 | public void ConfigCanBeCreatedWithoutWildcards() 24 | { 25 | var config = new RazorConfig(false); 26 | Assert.IsNotNull(config.ContentProviders); 27 | Assert.IsNotNull(config.Namespaces); 28 | Assert.IsNotNull(config.References); 29 | Assert.IsNotNull(config.RootOperator); 30 | Assert.IsNotNull(config.Templates); 31 | Assert.IsFalse(config.References.Any(r => r.Contains("*"))); 32 | } 33 | 34 | [TestMethod] 35 | public void ConfigIsLoadedFromConfig() { 36 | var config = new RazorConfig(); 37 | Assert.IsFalse(config.Templates.IncludeGeneratedSourceCode); 38 | config.Initializer.TryInitializeFromConfig(); 39 | Assert.IsTrue(config.Templates.IncludeGeneratedSourceCode); 40 | } 41 | 42 | [TestMethod] 43 | public void ConfigIsLoadedFromFile() { 44 | var config = new RazorConfig(); 45 | Assert.IsFalse(config.Templates.IncludeGeneratedSourceCode); 46 | config.Initializer.InitializeByXmlFileName("Xipton.Razor.UnitTest.dll.config"); 47 | Assert.IsTrue(config.Templates.IncludeGeneratedSourceCode); 48 | } 49 | 50 | [TestMethod] 51 | public void ConfigIsLoadedFromValues() { 52 | var config = new RazorConfig(); 53 | config.Initializer.InitializeByValues(defaultExtension: ".vb"); 54 | Assert.AreEqual(config.Templates.DefaultExtension,".vb"); 55 | } 56 | 57 | [TestMethod] 58 | public void ConfigIsLoadedFromXml() { 59 | var config = new RazorConfig(); 60 | config.Initializer.InitializeByXmlContent( 61 | @" 62 | 63 | 64 | 65 | " 66 | ); 67 | Assert.AreEqual(config.RootOperator.Path, "/foo"); 68 | } 69 | 70 | [TestMethod] 71 | [ExpectedException(typeof(InvalidOperationException))] 72 | public void ConfigCannotBeModifiedAfterRazorMachineInitialization(){ 73 | 74 | var rm = new RazorMachine(); 75 | rm.Context.Config.Initializer.InitializeByValues(rootOperatorPath: "/foo"); 76 | 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.UnitTest/LiteralStringTest.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable HeuristicUnreachableCode 2 | using System; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Xipton.Razor.UnitTest { 6 | [TestClass] 7 | public class LiteralStringTest { 8 | 9 | [TestMethod] 10 | public void LiteralStringCanBeInstantiated() { 11 | new LiteralString(null); 12 | new LiteralString(string.Empty); 13 | new LiteralString("foo"); 14 | } 15 | 16 | [TestMethod] 17 | public void LiteralStringCanBeComparedToSystemString() { 18 | Assert.IsTrue(new LiteralString(null).Equals(null)); 19 | new LiteralString(string.Empty).Equals(string.Empty); 20 | new LiteralString("foo").Equals("foo"); 21 | 22 | Assert.IsTrue(new LiteralString(string.Empty) == string.Empty); 23 | Assert.IsTrue(new LiteralString("foo") == "foo"); 24 | 25 | Assert.IsFalse(new LiteralString(null) != null); // ! 26 | Assert.IsFalse(new LiteralString(string.Empty) != string.Empty); 27 | Assert.IsFalse(new LiteralString("foo") != "foo"); 28 | } 29 | 30 | [TestMethod] 31 | public void LiteralStringCanBeAssignedFromSystemString() { 32 | LiteralString literalString = "foo"; 33 | Assert.AreEqual(literalString,"foo"); 34 | Assert.IsTrue(literalString == "foo"); 35 | 36 | string nullString = null; 37 | literalString = nullString; 38 | Assert.IsTrue(literalString.Equals(nullString)); //! => literalString object itself is not null 39 | Assert.IsTrue(literalString == nullString); 40 | Assert.IsTrue(literalString == null);//! => literalString object itself is not null 41 | } 42 | 43 | [TestMethod] 44 | public void StringCanBeAssignedFromLiteralString() { 45 | LiteralString literalString = "foo"; 46 | string s = literalString; 47 | Assert.AreEqual(literalString, s); 48 | Assert.IsTrue(s == "foo"); 49 | 50 | literalString = (string) null; 51 | s = literalString; 52 | Assert.IsTrue(literalString == s); 53 | Assert.IsTrue(s == null); 54 | } 55 | 56 | [TestMethod] 57 | public void NullStringBehaviorCheck(){ 58 | Assert.IsTrue(new LiteralString(null) == null); // !! 59 | Assert.IsTrue(new LiteralString(null).ToString() == string.Empty); // !! => ToString() never returns null; 60 | 61 | LiteralString s = (string)null; 62 | Assert.IsTrue(s == null); 63 | Assert.IsTrue(s.ToString().Length == 0); //!! 64 | 65 | s = null; // instead of (string)null => no implicit cast operator 66 | Assert.IsTrue(s == null); 67 | Exception caught = null; 68 | try{ 69 | Assert.IsTrue(s.ToString().Length == 0); 70 | } 71 | catch (NullReferenceException ex) { 72 | caught = ex; 73 | Console.WriteLine(ex.Message); 74 | } 75 | Assert.IsNotNull(caught); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.UnitTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Xipton.Razor.UnitTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Xipton")] 12 | [assembly: AssemblyProduct("Xipton.Razor.UnitTest")] 13 | [assembly: AssemblyCopyright("Copyright © 2013")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("f45d36c8-d0f8-4be6-a7ef-d3bda2692b74")] 24 | 25 | [assembly: AssemblyVersion("3.1.0.0")] 26 | [assembly: AssemblyFileVersion("3.1.0.0")] 27 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.UnitTest/ViewBagTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using Xipton.Razor.Core; 5 | using Xipton.Razor.Extension; 6 | 7 | namespace Xipton.Razor.UnitTest 8 | { 9 | [TestClass] 10 | public class ViewBagTest 11 | { 12 | 13 | private static RazorMachine NewEngine(string virtualRoot = null) { 14 | var setup = @" 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ".FormatWith(virtualRoot ?? "/"); 27 | 28 | return new RazorMachine(setup); 29 | } 30 | 31 | [TestMethod] 32 | public void ViewBagValueIsSet() 33 | { 34 | var engine = new RazorMachine() 35 | .RegisterTemplate("~/Test", "@{ViewBag.Foo = 1;} Hello World!"); 36 | var template = engine.ExecuteUrl("~/Test"); 37 | Debug.WriteLine(template.Result); // => prints: Hello World! 38 | Assert.AreEqual(1, template.ViewBag.Foo); 39 | } 40 | 41 | [TestMethod] 42 | public void EmptyViewBagCanBeAssignedTo() 43 | { 44 | var engine = NewEngine("/foo"); 45 | engine.RegisterTemplate("~/Test.cshtml", "@{ViewBag.Foo = 1;}"); 46 | var template = engine.ExecuteUrl("/foo/Test"); 47 | Assert.AreEqual(1,template.ViewBag.Foo); 48 | } 49 | 50 | [TestMethod] 51 | [ExpectedException(typeof(TemplateBindingException))] 52 | public void EmptyViewBagThrowsRuntimeBinderExceptionOnInvalidReference() 53 | { 54 | var engine = NewEngine(); 55 | engine.RegisterTemplate("/Test", "@{int i = (int)ViewBag.Foo;}"); 56 | engine.ExecuteUrl("/Test"); 57 | } 58 | 59 | [TestMethod] 60 | public void ViewBagCanBe_AnonymousInstance() 61 | { 62 | var engine = NewEngine(); 63 | engine.RegisterTemplate("~/Test/Foo", "@{var i = (int)ViewBag.Foo;Assert.AreEqual(10,i);}"); 64 | engine.ExecuteUrl("/Test/Foo",viewbag:new{Foo = 10}); 65 | } 66 | [TestMethod] 67 | public void ViewBagCanBe_KeyValueDictionary() 68 | { 69 | var engine = NewEngine(); 70 | engine.RegisterTemplate("~/Test.cshtml", "@{var i = (int)ViewBag.Foo;Assert.AreEqual(10,i);}"); 71 | engine.ExecuteUrl("~/Test", viewbag: new Dictionary{{"Foo",10}}); 72 | 73 | } 74 | class FooHolder 75 | { 76 | // ReSharper disable UnusedMember.Local 77 | public int Foo { get { return 10; } } 78 | // ReSharper restore UnusedMember.Local 79 | } 80 | [TestMethod] 81 | public void ViewBagCanBe_Class() 82 | { 83 | var engine = NewEngine(); 84 | engine.RegisterTemplate("~/Test.cshtml", "@{var i = (int)ViewBag.Foo;Assert.AreEqual(10,i);}"); 85 | engine.ExecuteUrl("~/Test", viewbag: new FooHolder()); 86 | 87 | } 88 | [TestMethod] 89 | public void ViewBagFromParentIsReferencedImplicitly() 90 | { 91 | var engine = NewEngine(); 92 | engine.RegisterTemplate("~/Parent.cshtml", "@{ViewBag.Title=10;} @RenderPage(\"Child\")"); 93 | engine.RegisterTemplate("~/Child.cshtml", "@{var i = (int)ViewBag.Title;Assert.AreEqual(10,i);}"); 94 | engine.ExecuteUrl("~/Parent"); 95 | 96 | } 97 | 98 | [TestMethod] 99 | public void ViewBagAtChildInstantiatesRootViewBagIfChildHasNoViewBag() 100 | { 101 | var engine = NewEngine(); 102 | engine.RegisterTemplate("~/Parent.cshtml", "@RenderPage(\"Child\") @{var i = (int)ViewBag.Title;Assert.AreEqual(10,i);}"); 103 | engine.RegisterTemplate("~/Child.cshtml", "@RenderPage(\"Child2\")"); 104 | engine.RegisterTemplate("~/Child2.cshtml", "@{ViewBag.Title=10;}"); 105 | var template = engine.ExecuteUrl("~/Parent"); 106 | Assert.AreEqual("~/Child2.cshtml", template.Childs[0].Childs[0].VirtualPath); 107 | 108 | } 109 | 110 | 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.UnitTest/Xipton.Razor.UnitTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {C7DA7CA0-86F9-4CA1-89EA-D1A23A4C7A2A} 9 | Library 10 | Properties 11 | Xipton.Razor.UnitTest 12 | Xipton.Razor.UnitTest 13 | v4.5 14 | 512 15 | 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | false 27 | 28 | 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | False 44 | ..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Always 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Designer 64 | 65 | 66 | 67 | Always 68 | 69 | 70 | 71 | 72 | {3A7A2F2F-0B57-47F9-8E1A-D34ECB961831} 73 | Xipton.Razor 74 | 75 | 76 | 77 | 78 | 79 | 80 | 87 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.UnitTest/legacy/web.config: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.UnitTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.UnitTest/unmanaged.dll: -------------------------------------------------------------------------------- 1 | this represents an unmanaged dll, for testing the handling of unmanged dlls 2 | -------------------------------------------------------------------------------- /Source/Xipton.Razor.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xipton.Razor.UnitTest", "Xipton.Razor.UnitTest\Xipton.Razor.UnitTest.csproj", "{C7DA7CA0-86F9-4CA1-89EA-D1A23A4C7A2A}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xipton.Razor.Example", "Xipton.Razor.Example\Xipton.Razor.Example.csproj", "{B6E046A6-4EBC-48EC-A454-8399AB3DD9AF}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xipton.Razor", "Xipton.Razor\Xipton.Razor.csproj", "{3A7A2F2F-0B57-47F9-8E1A-D34ECB961831}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Debug|Mixed Platforms = Debug|Mixed Platforms 16 | Debug|x86 = Debug|x86 17 | Release|Any CPU = Release|Any CPU 18 | Release|Mixed Platforms = Release|Mixed Platforms 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {C7DA7CA0-86F9-4CA1-89EA-D1A23A4C7A2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {C7DA7CA0-86F9-4CA1-89EA-D1A23A4C7A2A}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {C7DA7CA0-86F9-4CA1-89EA-D1A23A4C7A2A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 25 | {C7DA7CA0-86F9-4CA1-89EA-D1A23A4C7A2A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 26 | {C7DA7CA0-86F9-4CA1-89EA-D1A23A4C7A2A}.Debug|x86.ActiveCfg = Debug|Any CPU 27 | {C7DA7CA0-86F9-4CA1-89EA-D1A23A4C7A2A}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {C7DA7CA0-86F9-4CA1-89EA-D1A23A4C7A2A}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {C7DA7CA0-86F9-4CA1-89EA-D1A23A4C7A2A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 30 | {C7DA7CA0-86F9-4CA1-89EA-D1A23A4C7A2A}.Release|Mixed Platforms.Build.0 = Release|Any CPU 31 | {C7DA7CA0-86F9-4CA1-89EA-D1A23A4C7A2A}.Release|x86.ActiveCfg = Release|Any CPU 32 | {B6E046A6-4EBC-48EC-A454-8399AB3DD9AF}.Debug|Any CPU.ActiveCfg = Debug|x86 33 | {B6E046A6-4EBC-48EC-A454-8399AB3DD9AF}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 34 | {B6E046A6-4EBC-48EC-A454-8399AB3DD9AF}.Debug|Mixed Platforms.Build.0 = Debug|x86 35 | {B6E046A6-4EBC-48EC-A454-8399AB3DD9AF}.Debug|x86.ActiveCfg = Debug|x86 36 | {B6E046A6-4EBC-48EC-A454-8399AB3DD9AF}.Debug|x86.Build.0 = Debug|x86 37 | {B6E046A6-4EBC-48EC-A454-8399AB3DD9AF}.Release|Any CPU.ActiveCfg = Release|x86 38 | {B6E046A6-4EBC-48EC-A454-8399AB3DD9AF}.Release|Mixed Platforms.ActiveCfg = Release|x86 39 | {B6E046A6-4EBC-48EC-A454-8399AB3DD9AF}.Release|Mixed Platforms.Build.0 = Release|x86 40 | {B6E046A6-4EBC-48EC-A454-8399AB3DD9AF}.Release|x86.ActiveCfg = Release|x86 41 | {B6E046A6-4EBC-48EC-A454-8399AB3DD9AF}.Release|x86.Build.0 = Release|x86 42 | {3A7A2F2F-0B57-47F9-8E1A-D34ECB961831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {3A7A2F2F-0B57-47F9-8E1A-D34ECB961831}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {3A7A2F2F-0B57-47F9-8E1A-D34ECB961831}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 45 | {3A7A2F2F-0B57-47F9-8E1A-D34ECB961831}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 46 | {3A7A2F2F-0B57-47F9-8E1A-D34ECB961831}.Debug|x86.ActiveCfg = Debug|Any CPU 47 | {3A7A2F2F-0B57-47F9-8E1A-D34ECB961831}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {3A7A2F2F-0B57-47F9-8E1A-D34ECB961831}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {3A7A2F2F-0B57-47F9-8E1A-D34ECB961831}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 50 | {3A7A2F2F-0B57-47F9-8E1A-D34ECB961831}.Release|Mixed Platforms.Build.0 = Release|Any CPU 51 | {3A7A2F2F-0B57-47F9-8E1A-D34ECB961831}.Release|x86.ActiveCfg = Release|Any CPU 52 | EndGlobalSection 53 | GlobalSection(SolutionProperties) = preSolution 54 | HideSolutionNode = FALSE 55 | EndGlobalSection 56 | EndGlobal 57 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Config/ConfigElement.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System.Xml.Linq; 9 | 10 | namespace Xipton.Razor.Config 11 | { 12 | public abstract class ConfigElement { 13 | 14 | public static TElement Create() 15 | where TElement : ConfigElement, new() { 16 | var e = new TElement(); 17 | e.SetDefaults(); 18 | return e; 19 | } 20 | 21 | internal ConfigElement TryLoadElement(XElement e) { 22 | if (e != null) 23 | Load(e); 24 | return this; 25 | } 26 | 27 | protected abstract void Load(XElement e); 28 | protected internal abstract void SetDefaults(); 29 | 30 | } 31 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Config/IRazorConfigInitializer.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Web.Razor; 11 | using Xipton.Razor.Core; 12 | 13 | namespace Xipton.Razor.Config 14 | { 15 | public interface IRazorConfigInitializer 16 | { 17 | IRazorConfigInitializer InitializeByValues( 18 | Type baseType = null, 19 | string rootOperatorPath = null, 20 | RazorCodeLanguage language = null, 21 | string defaultExtension = null, 22 | string autoIncludeNameWithoutExtension = null, 23 | string sharedLocation = null, 24 | bool? includeGeneratedSourceCode = null, 25 | bool? htmlEncode = null, 26 | IEnumerable references = null, 27 | IEnumerable namespaces = null, 28 | IEnumerable> contentProviders = null, 29 | bool replaceReferences = false, 30 | bool replaceNamespaces = false, 31 | bool replaceContentProviders = false 32 | ); 33 | 34 | IRazorConfigInitializer InitializeByXmlContent(string xmlContent); 35 | 36 | IRazorConfigInitializer InitializeByXmlFileName(string fileName); 37 | IRazorConfigInitializer TryInitializeFromConfig(); 38 | RazorConfig AsReadOnly(); 39 | } 40 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Config/RazorConfig.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Configuration; 11 | using System.IO; 12 | using System.Linq; 13 | using System.Reflection; 14 | using System.Web.Razor; 15 | using System.Xml; 16 | using System.Xml.Linq; 17 | using Xipton.Razor.Core; 18 | using Xipton.Razor.Core.ContentProvider; 19 | using Xipton.Razor.Extension; 20 | 21 | namespace Xipton.Razor.Config { 22 | /// 23 | /// Holds all configuration settings. The configuration is initialized together with the RazorMachine class. 24 | /// 25 | /// If the configuration is initialized by default (with no parameters) it is checked if a configuration section exists at 26 | /// the default application's configuration named xipton.razor.config. If such a section exists then that configuration is used. Else the configuration 27 | /// is initialized as default, i.e., using the C# compiler and using MVC complient settings. 28 | /// 29 | /// If you pass explicit settings with the constructor then at first the configuration is initialized as by default, as with the default constructor. 30 | /// After that all passed non null arguments override their correponding existing settings. For namespaces and references hold that by default 31 | /// these are merged with the already present configured (or default) namespaces and references, unless you specify that these arguments must replace 32 | /// the corresponding present settings. 33 | /// 34 | public sealed class RazorConfig : IRazorConfigInitializer 35 | { 36 | private readonly bool 37 | _allowWildcardReferences; 38 | 39 | private const string 40 | _rootElementName = "xipton.razor"; 41 | 42 | private bool 43 | _isReadOnly; 44 | 45 | public RazorConfig(bool allowWildcardReferences = true) 46 | { 47 | _allowWildcardReferences = allowWildcardReferences; 48 | LoadDefaults(); 49 | } 50 | 51 | #region Configuration Properties 52 | public RootOperatorElement RootOperator { get; private set; } 53 | public TemplatesElement Templates { get; private set; } 54 | public IList Namespaces { get; private set; } 55 | public IList References { get; private set; } 56 | public IList> ContentProviders { get; private set; } 57 | public IRazorConfigInitializer Initializer{ 58 | get{ 59 | EnsureNotReadonly(); 60 | return this; 61 | } 62 | } 63 | #endregion 64 | 65 | #region IRazorConfigInitializer 66 | 67 | IRazorConfigInitializer IRazorConfigInitializer.InitializeByValues( 68 | Type baseType, 69 | string rootOperatorPath, 70 | RazorCodeLanguage language, 71 | string defaultExtension, 72 | string autoIncludeNameWithoutExtension, 73 | string sharedLocation, 74 | bool? includeGeneratedSourceCode, 75 | bool? htmlEncode, 76 | IEnumerable references, 77 | IEnumerable namespaces, 78 | IEnumerable> contentProviders, 79 | bool replaceReferences, 80 | bool replaceNamespaces, 81 | bool replaceContentProviders 82 | ) 83 | { 84 | 85 | EnsureNotReadonly(); 86 | 87 | RootOperator.Path = rootOperatorPath ?? RootOperator.Path; 88 | 89 | Templates.BaseType = baseType ?? Templates.BaseType; 90 | Templates.Language = language ?? Templates.Language; 91 | Templates.DefaultExtension = (defaultExtension ?? Templates.DefaultExtension).EmptyAsNull(); 92 | Templates.AutoIncludeName = (autoIncludeNameWithoutExtension ?? Templates.AutoIncludeName).EmptyAsNull(); 93 | Templates.SharedLocation = (sharedLocation ?? Templates.SharedLocation).EmptyAsNull(); 94 | if (includeGeneratedSourceCode != null) 95 | Templates.IncludeGeneratedSourceCode = includeGeneratedSourceCode.Value; 96 | if (htmlEncode != null) 97 | Templates.HtmlEncode = htmlEncode.Value; 98 | if (references != null) { 99 | References = replaceReferences ? references.ToList().AsReadOnly() : References.Union(references, StringComparer.InvariantCultureIgnoreCase).ToList().AsReadOnly(); 100 | } 101 | if (namespaces != null) { 102 | Namespaces = replaceNamespaces ? namespaces.ToList().AsReadOnly() : Namespaces.Union(namespaces).ToList().AsReadOnly(); 103 | } 104 | if (contentProviders != null) { 105 | ContentProviders = replaceContentProviders ? contentProviders.ToList().AsReadOnly() : ContentProviders.Union(contentProviders).ToList().AsReadOnly(); 106 | } 107 | return this; 108 | } 109 | 110 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "StringReader is disposed on XmlTextReader.Dispose()")] 111 | IRazorConfigInitializer IRazorConfigInitializer.InitializeByXmlContent(string xmlContent) { 112 | if (xmlContent == null) throw new ArgumentNullException("xmlContent"); 113 | EnsureNotReadonly(); 114 | using (var reader = new XmlTextReader(new StringReader(xmlContent))){ 115 | LoadSettingsFromXDocumentOrElseDefaults(XDocument.Load(reader)); 116 | } 117 | return this; 118 | } 119 | 120 | IRazorConfigInitializer IRazorConfigInitializer.InitializeByXmlFileName(string fileName) { 121 | if (fileName == null) throw new ArgumentNullException("fileName"); 122 | EnsureNotReadonly(); 123 | if (!File.Exists(fileName)){ 124 | throw new FileNotFoundException("Configuration file '{0}' not found.".FormatWith(fileName), fileName); 125 | } 126 | using (var reader = new XmlTextReader(fileName)) 127 | LoadSettingsFromXDocumentOrElseDefaults(XDocument.Load(reader)); 128 | return this; 129 | } 130 | 131 | IRazorConfigInitializer IRazorConfigInitializer.TryInitializeFromConfig() { 132 | EnsureNotReadonly(); 133 | var section = ConfigurationManager.GetSection("xipton.razor.config"); 134 | XElement innerXml; 135 | if (section != null && (innerXml = section.CastTo().InnerXml) != null) 136 | LoadSettingsFromXDocumentOrElseDefaults(new XDocument(innerXml)); 137 | return this; 138 | } 139 | 140 | RazorConfig IRazorConfigInitializer.AsReadOnly(){ 141 | return AsReadonly(); 142 | } 143 | 144 | #endregion 145 | 146 | #region Internal 147 | internal RazorConfig AsReadonly() { 148 | if (!_isReadOnly) { 149 | _isReadOnly = true; 150 | TryResolveWildcardReferences(); 151 | } 152 | return this; 153 | } 154 | #endregion 155 | 156 | #region Private 157 | private void EnsureNotReadonly() { 158 | if (_isReadOnly) 159 | throw new InvalidOperationException("Configuration is read-only, after it has been registered inside the razor context, and cannot be modified anymore."); 160 | } 161 | 162 | private static IList CreateDefaultNamespaces() { 163 | return 164 | new List 165 | { 166 | "System", 167 | "System.Collections", 168 | "System.Collections.Generic", 169 | "System.Dynamic", 170 | "System.IO", 171 | "System.Linq", 172 | "Xipton.Razor.Extension" 173 | }.AsReadOnly(); 174 | } 175 | private IList CreateDefaultReferences() { 176 | var references = 177 | new List 178 | { 179 | "mscorlib.dll", 180 | "system.dll", 181 | "system.core.dll", 182 | "microsoft.csharp.dll" 183 | 184 | }; 185 | if (_allowWildcardReferences) 186 | { 187 | references.AddRange(new[] 188 | { 189 | "*.dll", 190 | "*.exe" 191 | }); 192 | } 193 | return references.AsReadOnly(); 194 | } 195 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "created instances are disposed on CompositeContentProvider.Dispose()")] 196 | private static IList> CreateDefaultContentProviders() { 197 | return new List> { () => new FileContentProvider(Directory.Exists("./Views".MakeAbsoluteDirectoryPath()) ? "./Views" : ".") }.AsReadOnly(); 198 | } 199 | 200 | private void LoadSettingsFromXDocumentOrElseDefaults(XContainer config) { 201 | try { 202 | 203 | var rootDescendents = config.Descendants(_rootElementName); 204 | 205 | var xElements = rootDescendents as XElement[] ?? rootDescendents.ToArray(); 206 | RootOperator = ConfigElement 207 | .Create() 208 | .TryLoadElement(xElements 209 | .Descendants("rootOperator") 210 | .SingleOrDefault() 211 | ) 212 | .CastTo(); 213 | 214 | Templates = ConfigElement 215 | .Create() 216 | .TryLoadElement(xElements 217 | .Descendants("templates") 218 | .SingleOrDefault() 219 | ) 220 | .CastTo(); 221 | 222 | Namespaces = xElements.HasClearChildElement("namespaces") ? new List() : CreateDefaultNamespaces(); 223 | References = xElements.HasClearChildElement("references") ? new List() : CreateDefaultReferences(); 224 | ContentProviders = xElements.HasClearChildElement("contentProviders") ? new List>() : CreateDefaultContentProviders(); 225 | 226 | Namespaces = xElements 227 | .Descendants("namespaces") 228 | .SingleOrDefault(new XElement("namespaces")) 229 | .Descendants("add") 230 | .Select(xe => xe.GetAttributeValue("namespace")) 231 | .Union(Namespaces) 232 | .ToList() 233 | .AsReadOnly(); 234 | 235 | References = xElements 236 | .Descendants("references") 237 | .SingleOrDefault(new XElement("references")) 238 | .Descendants("add") 239 | .Select(xe => xe.GetAttributeValue("reference")) 240 | .Union(References, StringComparer.InvariantCultureIgnoreCase) 241 | .ToList() 242 | .AsReadOnly(); 243 | 244 | var contentProviderElements = 245 | xElements 246 | .Descendants("contentProviders") 247 | .SingleOrDefault(new XElement("contentProviders")) 248 | .Descendants("add"); 249 | 250 | ContentProviders = ContentProviders.ToList(); 251 | foreach (var e in contentProviderElements) { 252 | var el = e; 253 | ContentProviders.Add( 254 | () => Activator.CreateInstance(Type.GetType(el.GetAttributeValue("type"), true), true) 255 | .CastTo() 256 | .InitFromConfig(el) 257 | ); 258 | } 259 | ContentProviders = ContentProviders.CastTo>>().AsReadOnly(); 260 | } 261 | catch(TypeLoadException ex){ 262 | throw new TemplateConfigurationException("Unable to load type named '{0}' at your xipton.razor configuration. Please correct the corresponding configuration setting.".FormatWith(ex.TypeName),ex); 263 | } 264 | catch(Exception ex){ 265 | throw new TemplateConfigurationException("Unable to load the xipton.razor configuration. {0}. Please correct your xipton.razor configuration. Take a look at the inner exception(s) for details.".FormatWith(ex.Message),ex); 266 | } 267 | 268 | } 269 | private void LoadDefaults() { 270 | RootOperator = ConfigElement.Create(); 271 | Templates = ConfigElement.Create(); 272 | Namespaces = CreateDefaultNamespaces(); 273 | References = CreateDefaultReferences(); 274 | ContentProviders = CreateDefaultContentProviders(); 275 | } 276 | private void TryResolveWildcardReferences() 277 | { 278 | 279 | // ensure xipton assemblies to be loaded in the execution context 280 | AppDomain.CurrentDomain 281 | .EnsureXiptonAssembliesLoaded(); 282 | 283 | if (!References.Any(s => s.Contains("*."))) 284 | { 285 | // no need to resolve references 286 | return; 287 | } 288 | 289 | if (!_allowWildcardReferences) 290 | { 291 | throw new TemplateConfigurationException("AllowWildcardReferences == false -> References cannot be configured using wildcards. All references must be configured explicitely."); 292 | } 293 | 294 | // ensure all bin assemblies to be loaded in the execution context 295 | AppDomain.CurrentDomain.EnsureBinAssembliesLoaded(); 296 | 297 | var domainAssemblies = AppDomain.CurrentDomain 298 | .GetAssemblies() 299 | .Where(a => !a.IsDynamic) 300 | .ToList(); 301 | 302 | var referencedAssemblyFileNames = new List(); 303 | 304 | FindAssemblyFileNamesByPattern("*.dll", domainAssemblies, referencedAssemblyFileNames); 305 | FindAssemblyFileNamesByPattern("*.exe", domainAssemblies, referencedAssemblyFileNames); 306 | 307 | References = 308 | References 309 | .Where(referenceName => 310 | !referenceName.Contains("*") && 311 | !referencedAssemblyFileNames.Any(filename => filename 312 | .TrimEnd() 313 | .EndsWith(EnsureLeadingBackSlash(referenceName), StringComparison.OrdinalIgnoreCase) 314 | ) 315 | ) 316 | .Concat( 317 | referencedAssemblyFileNames 318 | ) 319 | .ToList() 320 | .AsReadOnly(); 321 | } 322 | private void FindAssemblyFileNamesByPattern(string pattern, IEnumerable domainAssemblies, List referencedAssemblyFileNames){ 323 | if (References.Contains(pattern, StringComparer.OrdinalIgnoreCase)) { 324 | // if the configuration contains any entry, then all collected DLLs are added to the referenced assemblies set 325 | referencedAssemblyFileNames.AddRange( 326 | domainAssemblies 327 | .Select(assembly => assembly.GetFileName()) 328 | .Where(filename => filename.EndsWith(pattern.Substring(1), StringComparison.OrdinalIgnoreCase)) 329 | ); 330 | } 331 | } 332 | private static string EnsureLeadingBackSlash(string referenceName){ 333 | if (referenceName == null) throw new ArgumentNullException("referenceName"); 334 | return referenceName.Contains("\\") ? referenceName : "\\" + referenceName; 335 | } 336 | 337 | #endregion 338 | 339 | 340 | } 341 | 342 | } 343 | 344 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Config/RootOperatorElement.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System.Xml.Linq; 9 | 10 | namespace Xipton.Razor.Config 11 | { 12 | public class RootOperatorElement : ConfigElement { 13 | 14 | public string Path { get; internal set; } 15 | 16 | #region Overrides 17 | protected override void Load(XElement e) { 18 | Path = e.GetAttributeValue("path"); 19 | } 20 | 21 | protected internal override void SetDefaults() { 22 | Path = "/"; 23 | } 24 | #endregion 25 | } 26 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Config/TemplateConfigurationException.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Runtime.Serialization; 10 | using Xipton.Razor.Core; 11 | 12 | namespace Xipton.Razor.Config 13 | { 14 | [Serializable] 15 | public class TemplateConfigurationException : TemplateException 16 | { 17 | 18 | public TemplateConfigurationException() 19 | { 20 | } 21 | 22 | public TemplateConfigurationException(string message) : base(message) 23 | { 24 | } 25 | 26 | public TemplateConfigurationException(string message, Exception inner) : base(message, inner) 27 | { 28 | } 29 | 30 | protected TemplateConfigurationException( 31 | SerializationInfo info, 32 | StreamingContext context) : base(info, context) 33 | { 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Config/TemplatesElement.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Diagnostics; 10 | using System.Web.Razor; 11 | using System.Xml.Linq; 12 | using Xipton.Razor.Core.Generator; 13 | using Xipton.Razor.Core.Generator.CSharp; 14 | using Xipton.Razor.Core.Generator.VB; 15 | using Xipton.Razor.Extension; 16 | 17 | namespace Xipton.Razor.Config 18 | { 19 | public class TemplatesElement : ConfigElement { 20 | 21 | private Type 22 | _baseType; 23 | private RazorCodeLanguage 24 | _language; 25 | 26 | 27 | #region Properties 28 | /// 29 | /// Gets or sets the template's default base type. This settings can be overridden by the @inherits directive (Razor C#) 30 | /// 31 | /// 32 | /// The default type of the template base. 33 | /// 34 | /// The type set must be an open generic type, that inherits a subclass of . Also this type 35 | /// must have the same name as it's base type. So you need to implement two levels of custom base classes if you want 36 | /// to set this attribute. 37 | /// 38 | /// If you want to implement a custom base class named MyTemplateBase you need this class to inherit TemplateBase and add your custom implementation. 39 | /// Next you create a class named MyTemplateBase<TModel> like: 40 | /// 41 | /// public class MyTemplateBase<TModel> : MyTemplateBase, ITemplate<TModel> 42 | /// { 43 | /// public new TModel Model { get { return GetOrCreateModel<TModel>(); } } 44 | /// } 45 | /// 46 | /// 47 | /// 48 | /// 49 | public Type BaseType { 50 | get { return _baseType; } 51 | internal set { 52 | if (value == null) throw new ArgumentNullException("value"); 53 | ValidateBaseType(value); 54 | _baseType = value; 55 | } 56 | } 57 | public RazorCodeLanguage Language { 58 | get { return _language; } 59 | internal set { 60 | _language = value 61 | .CastTo() // must be a Xipton implementation => else an exception is thrown here 62 | .CastTo(); 63 | } 64 | } 65 | public string DefaultExtension { get; internal set; } 66 | public string AutoIncludeName { get; internal set; } 67 | public string SharedLocation { get; internal set; } 68 | public bool IncludeGeneratedSourceCode { get; internal set; } 69 | public bool HtmlEncode { get; internal set; } 70 | 71 | public string NonGenericBaseTypeName { 72 | get { 73 | var fullname = BaseType.FullName; 74 | var index = fullname == null ? -1 : fullname.IndexOf('`'); 75 | Debug.Assert(fullname != null, "fullname != null"); 76 | return index < 0 ? fullname : fullname.Substring(0, index); 77 | } 78 | } 79 | #endregion 80 | 81 | #region Overrides 82 | protected override void Load(XElement e) { 83 | var typeName = e.GetAttributeValue("language", false); 84 | if (typeName != null) { 85 | if (typeName.Equals("C#", StringComparison.OrdinalIgnoreCase)) 86 | // convenient name for default C# implementation 87 | Language = new XiptonCSharpCodeLanguage(); 88 | else if (typeName.Equals("VB", StringComparison.OrdinalIgnoreCase)) 89 | // convenient name for default VB implementation 90 | Language = new XiptonVBCodeLanguage(); 91 | else 92 | Language = Type.GetType(typeName, true).CreateInstance().CastTo(); 93 | } 94 | 95 | typeName = e.GetAttributeValue("baseType", false); 96 | BaseType = typeName != null ? Type.GetType(typeName, true) : BaseType; 97 | 98 | DefaultExtension = (e.GetAttributeValue("defaultExtension", false) ?? DefaultExtension).EmptyAsNull(); 99 | AutoIncludeName = (e.GetAttributeValue("autoIncludeName", false) ?? AutoIncludeName).EmptyAsNull(); 100 | SharedLocation = (e.GetAttributeValue("sharedLocation", false) ?? SharedLocation).EmptyAsNull(); 101 | var setting = e.GetAttributeValue("includeGeneratedSourceCode", false); 102 | if (setting != null) 103 | IncludeGeneratedSourceCode = bool.Parse(setting); 104 | setting = e.GetAttributeValue("htmlEncode", false); 105 | if (setting != null) 106 | HtmlEncode = bool.Parse(setting); 107 | } 108 | protected internal override void SetDefaults() { 109 | BaseType = typeof(TemplateBase); 110 | Language = new XiptonCSharpCodeLanguage(); 111 | AutoIncludeName = "_viewStart"; 112 | DefaultExtension = ".cshtml"; 113 | SharedLocation = "~/Shared"; 114 | IncludeGeneratedSourceCode = false; 115 | HtmlEncode = true; 116 | } 117 | #endregion 118 | 119 | private static void ValidateBaseType(Type baseType) { 120 | 121 | if (!baseType.IsGenericType) { 122 | try { 123 | baseType = baseType.Assembly.GetType(baseType.FullName + "`1", true); // => generic type must be defined as well 124 | } 125 | catch (Exception ex) { 126 | throw new TemplateConfigurationException("BaseType {0} must have a generic sub class with one type parameter.".FormatWith(baseType.FullName), ex); 127 | } 128 | } 129 | 130 | if (!baseType.IsGenericTypeDefinition || baseType.GetGenericArguments().Length != 1) 131 | throw new TemplateConfigurationException("BaseType must be an open generic type (or have a subclass that is a generic type) with one generic parameter (for TModel)."); 132 | 133 | if (baseType.BaseType == null || baseType.BaseType.IsGenericType || !typeof(TemplateBase).IsAssignableFrom(baseType.BaseType)) 134 | throw new TemplateConfigurationException("BaseType must inherit a non generic (custom) basetype that must be a subclass of {0}.".FormatWith(typeof(TemplateBase))); 135 | 136 | if (baseType.BaseType.Name != baseType.Name.Substring(0, baseType.Name.Length - 2)) 137 | throw new TemplateConfigurationException("BaseType must be a generic type and must inherit a non generic basetype with the same name."); 138 | } 139 | 140 | } 141 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Config/XElementExtension.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Xml.Linq; 12 | using Xipton.Razor.Extension; 13 | 14 | namespace Xipton.Razor.Config { 15 | 16 | internal static class XElementExtension { 17 | 18 | public static string GetAttributeValue(this XElement element, string name, bool required = true) { 19 | var attribute = element == null ? null : element.Attribute(XName.Get(name)); 20 | if (attribute == null) { 21 | if (required && element != null) 22 | throw new TemplateConfigurationException("required attribute '{0}' missing at element '{1}'.".FormatWith(name, element.Name)); 23 | return null; 24 | } 25 | return attribute.Value; 26 | } 27 | 28 | // convenience implementation for fluency reasons and being able to report a clear configuration error 29 | public static XElement SingleOrDefault(this IEnumerable elements, XElement defaultValue) { 30 | if (elements == null) return defaultValue; 31 | var xElements = elements as XElement[] ?? elements.ToArray(); 32 | if (xElements.Length == 0) 33 | return defaultValue; 34 | if (xElements.Length > 1) 35 | throw new TemplateConfigurationException("More that one element named '{0}' found at your configuration.".FormatWith(xElements.First().Name)); 36 | return xElements[0]; 37 | } 38 | 39 | public static bool HasClearChildElement(this IEnumerable root, string parentName) { 40 | if (root == null) throw new ArgumentNullException("root"); 41 | if (parentName == null) throw new ArgumentNullException("parentName"); 42 | return root 43 | .Descendants(parentName) 44 | .SingleOrDefault(new XElement(parentName)) 45 | .Descendants("clear") 46 | .SingleOrDefault() != null; 47 | 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Config/XmlConfigurationSection.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System.Configuration; 9 | using System.Xml; 10 | using System.Xml.Linq; 11 | 12 | namespace Xipton.Razor.Config { 13 | /// 14 | /// General purpose configuration section. The whole section's inner xml is loaded without any validation. 15 | /// 16 | public class XmlConfigurationSection : ConfigurationSection 17 | { 18 | private bool _firstUnrecognizedElementHandled; 19 | 20 | public XElement InnerXml { get; private set; } 21 | 22 | protected override bool OnDeserializeUnrecognizedElement(string elementName, XmlReader reader) { 23 | if (!_firstUnrecognizedElementHandled){ 24 | _firstUnrecognizedElementHandled = true; 25 | InnerXml = (XElement) XNode.ReadFrom(reader); 26 | } 27 | else{ 28 | throw new ConfigurationErrorsException("The XmlConfigurationSection must contain at most one child element (holding as many grand childs as you like)."); 29 | } 30 | return true; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Config/xipton.razor.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 7 | 8 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/ContentManager.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections.Concurrent; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Text; 13 | using Xipton.Razor.Config; 14 | using Xipton.Razor.Core.ContentProvider; 15 | using Xipton.Razor.Extension; 16 | 17 | namespace Xipton.Razor.Core 18 | { 19 | /// 20 | /// The ContentManager assembles content and delivers final content to the template factory. It implements additional routing logic on top of the content providers. 21 | /// 22 | public class ContentManager : IDisposable 23 | { 24 | 25 | #region Fields 26 | // if cached null results reaches this count then the cache is purged (null results are removed). 27 | // this is needed to prevent the cache from getting loaded endlessly with null results from invalid requests 28 | private const int _cacheMaxNullCount = 1000; 29 | 30 | private CompositeContentProvider 31 | _contentProvider; 32 | 33 | private int 34 | _nullNormalizedEntryCount, 35 | _nullResourceEntryCount; 36 | 37 | 38 | private readonly VirtualPathBuilder 39 | _pathBuilder; 40 | 41 | private readonly IList 42 | _virtualSearchPathList; 43 | 44 | private readonly ConcurrentDictionary 45 | _requestToResourceNameMap = new ConcurrentDictionary(), 46 | _resourceNameToVirtualPathMap = new ConcurrentDictionary(), 47 | _requestToVirtualTargetMap = new ConcurrentDictionary(); 48 | 49 | private readonly string 50 | _autoIncludeName; 51 | private readonly string 52 | _defaultExtension; 53 | #endregion 54 | 55 | public event EventHandler SharedContentModified; 56 | 57 | public ContentManager(RazorConfig config) 58 | { 59 | if (config == null) throw new ArgumentNullException("config"); 60 | config = config.AsReadonly(); 61 | _contentProvider = new CompositeContentProvider(); 62 | ContentProvider.ContentModified += OnContentModified; 63 | config.ContentProviders.ToList().ForEach(ctor => AddContentProvider(ctor())); 64 | 65 | _pathBuilder = new VirtualPathBuilder(config.RootOperator.Path); 66 | _autoIncludeName = config.Templates.AutoIncludeName.EmptyAsNull(); 67 | _defaultExtension = config.Templates.DefaultExtension.EmptyAsNull(); 68 | if (_autoIncludeName != null && !_autoIncludeName.Contains(".") && _defaultExtension != null) 69 | _autoIncludeName = _autoIncludeName + "." + _defaultExtension.TrimStart('.'); 70 | var shared = config.Templates.SharedLocation.EmptyAsNull(); 71 | var searchPathList = new List(); 72 | if (shared != null) 73 | searchPathList.Add(GetPathBuilderWithRootOperator(shared).RemoveTrailingSlash()); 74 | _virtualSearchPathList = searchPathList.AsReadOnly(); 75 | } 76 | 77 | #region Content provider management 78 | 79 | public IContentProvider ContentProvider{ 80 | get { return _contentProvider; } 81 | } 82 | 83 | public ContentManager AddContentProvider(IContentProvider contentProvider, int order = 0) 84 | { 85 | if (contentProvider == null) throw new ArgumentNullException("contentProvider"); 86 | ContentProvider.CastTo().AddContentProvider(contentProvider, order); 87 | return this; 88 | } 89 | 90 | public ContentManager RemoveContentProviders(Type type) 91 | { 92 | ContentProvider.CastTo().RemoveContentProviders(type); 93 | return this; 94 | } 95 | public ContentManager RemoveContentProviders() where TContentProvider : IContentProvider { 96 | return RemoveContentProviders(typeof(TContentProvider)); 97 | } 98 | public ContentManager RemoveContentProvider(IContentProvider provider) { 99 | ContentProvider.CastTo().RemoveContentProvider(provider); 100 | return this; 101 | } 102 | 103 | public IContentProvider TryGetContentProvider(Type contentProviderType) 104 | { 105 | return ContentProvider 106 | .CastTo() 107 | .TryGetContentProvider(contentProviderType); 108 | } 109 | public T TryGetContentProvider() where T : IContentProvider 110 | { 111 | return TryGetContentProvider(typeof(T)) 112 | .CastTo(); 113 | } 114 | 115 | public IEnumerable GetContentProviders(Type contentProviderType) { 116 | return ContentProvider 117 | .CastTo() 118 | .GetContentProviders(contentProviderType); 119 | } 120 | public IEnumerable GetContentProviders() where T : IContentProvider { 121 | return GetContentProviders(typeof (T)).Cast(); 122 | } 123 | 124 | public ContentManager ClearAllContentProviders(){ 125 | ContentProvider.CastTo().ClearAllContentProviders(); 126 | return this; 127 | } 128 | #endregion 129 | 130 | /// 131 | /// Resolves a virtual path (i.e. an existing virtual path) for any incomming virtual request. 132 | /// example: a layout request like Layout="_layout" at template ~/templates/mytemplate.cshtml at first is attempted to get resolved at "~/templates/_layout.cshtml" 133 | /// But if the "_layout" only exists at "~/shared" this method translates the incomming request "~/templates/_layout.cshtml" into "~/shared/_layout.cshtml". 134 | /// 135 | /// 136 | /// Since searching at resources may need considarable amounts of time the map results are cached. 137 | /// If any content resource changes (notified by ) the whole mapping cache is cleared. 138 | /// 139 | /// The requested path. 140 | /// The actual virtual path or null if no resolve could be made 141 | public string TryGetVirtualPath(string requestedPath) 142 | { 143 | if (string.IsNullOrEmpty(requestedPath)) 144 | return null; 145 | 146 | if (_nullNormalizedEntryCount > _cacheMaxNullCount) { 147 | // clear entries that gave a null result once in a while 148 | _nullNormalizedEntryCount = 0; 149 | string value; 150 | _requestToVirtualTargetMap 151 | .Where(x => x.Value == null) 152 | .ToList() 153 | .ForEach(x => _requestToVirtualTargetMap.TryRemove(x.Key, out value)); 154 | } 155 | 156 | return _requestToVirtualTargetMap.GetOrAdd(requestedPath, path => 157 | { 158 | var pathBuilder = GetPathBuilderWithRootOperator(path); 159 | pathBuilder.AddOrKeepExtension(_defaultExtension); 160 | 161 | var resourceName = ContentProvider.TryGetResourceName(pathBuilder); 162 | if (resourceName != null) 163 | // content was found at the requested virtual location 164 | return pathBuilder; 165 | 166 | // search all search paths for an actual virtual path 167 | var name = pathBuilder.GetLastPart(true); 168 | var result = VirtualSearchPathList.Any(searchPath => (resourceName = ContentProvider.TryGetResourceName(pathBuilder.Clear().CombineWith(searchPath, name))) != null) ? pathBuilder : null; 169 | if (result == null) 170 | _nullNormalizedEntryCount++; 171 | return result; 172 | }); 173 | } 174 | 175 | /// 176 | /// Try to reolve a virtual request into a resource name (e.g. a filename). 177 | /// 178 | /// 179 | /// Since searching in resources may need considarable amounts of time the results are cached. 180 | /// If any content resource changes (notified by ) the whole cache is cleared. 181 | /// 182 | /// The requested path. 183 | /// The targeted resource name, or null if no resource name was found 184 | public string TryGetResourceName(string requestedPath) 185 | { 186 | if (string.IsNullOrEmpty(requestedPath)) 187 | return null; 188 | 189 | if (_nullResourceEntryCount > _cacheMaxNullCount) { 190 | _nullResourceEntryCount = 0; 191 | string value; 192 | _requestToResourceNameMap 193 | .Where(x => x.Value == null) 194 | .ToList() 195 | .ForEach(x => _requestToResourceNameMap.TryRemove(x.Key, out value)); 196 | } 197 | 198 | return _requestToResourceNameMap 199 | .GetOrAdd(requestedPath, path => 200 | { 201 | var virtualLocation = TryGetVirtualPath(path); 202 | var resourceName = virtualLocation == null ? null : ContentProvider.TryGetResourceName(virtualLocation); 203 | if(resourceName != null) 204 | _resourceNameToVirtualPathMap[resourceName] = virtualLocation; 205 | else 206 | _nullResourceEntryCount++; 207 | return resourceName; 208 | }); 209 | } 210 | 211 | /// 212 | /// Try to resolve, read and return the content by the requested path. 213 | /// All includes (respresented by AutoIncludeName e.g. _viewStart) from the 214 | /// current folder until te virtual root folder are inserted as well. 215 | /// The nearest include is inserted as the last, so the nearest include 216 | /// defines any possible override (e.g. Layout setting). 217 | /// 218 | /// 219 | /// The content itself is never cached. 220 | /// 221 | /// The requested path. 222 | /// The content or null if no content was found 223 | public string TryGetContent(string requestedPath) 224 | { 225 | var resourceName = TryGetResourceName(requestedPath); 226 | if (resourceName == null) 227 | return null; 228 | 229 | var content = ContentProvider.TryGetContent(resourceName); 230 | if (content == null || _autoIncludeName == null) 231 | return content; 232 | 233 | var virtualPath = TryGetVirtualPath(requestedPath); 234 | 235 | var sb = new StringBuilder(content); 236 | var nearestInclude = _pathBuilder 237 | .New() 238 | .CombineWith(virtualPath) 239 | .RemoveLastPart() 240 | .CombineWith(_autoIncludeName) 241 | .AddOrKeepExtension(_defaultExtension); 242 | 243 | var includes = FindAutoIncludes(nearestInclude).ToList(); 244 | 245 | foreach (var include in includes) 246 | { 247 | //sb.Insert(0, Environment.NewLine); 248 | sb.Insert(0, ContentProvider.TryGetContent(include)); 249 | } 250 | return sb.ToString(); 251 | } 252 | 253 | /// 254 | /// Gets the search path list as a read only list. Normally this list has one entry. 255 | /// 256 | public IList VirtualSearchPathList 257 | { 258 | get { return _virtualSearchPathList; } 259 | } 260 | 261 | #region Implementation of IDisposable 262 | 263 | public void Dispose() 264 | { 265 | Dispose(true); 266 | } 267 | protected virtual void Dispose(bool disposing) 268 | { 269 | if (!disposing) return; 270 | var provider = _contentProvider; 271 | _contentProvider = null; 272 | if (provider == null) return; 273 | provider.ContentModified -= OnContentModified; 274 | provider.Dispose(); 275 | } 276 | 277 | #endregion 278 | 279 | #region Private 280 | private IEnumerable FindAutoIncludes(string virtualPathToNearestAutoInclude) 281 | { 282 | var vp = GetPathBuilderWithRootOperator(virtualPathToNearestAutoInclude); 283 | 284 | vp.AddOrKeepExtension(_defaultExtension); 285 | 286 | var actual = ContentProvider.TryGetResourceName(vp); 287 | if (actual != null) 288 | yield return actual; 289 | 290 | var name = vp.GetLastPart(true); 291 | var searchPathes = VirtualSearchPathList; 292 | while (!searchPathes.Contains(vp.RemoveTrailingSlash(),StringComparer.OrdinalIgnoreCase) && vp.RemoveLastPart().Length != 0) 293 | { 294 | // search all parents until the virtual root, or until any shared folder name 295 | actual = ContentProvider.TryGetResourceName(vp.CombineWith(name)); 296 | if (actual != null) yield return actual; 297 | vp.RemoveLastPart(); 298 | } 299 | } 300 | private VirtualPathBuilder GetPathBuilderWithRootOperator(string path) 301 | { 302 | return _pathBuilder.New().CombineWith(path).Normalize().WithRootOperator(); 303 | } 304 | private void OnContentModified(object sender, ContentModifiedArgs e) 305 | { 306 | _requestToResourceNameMap.Clear(); 307 | _requestToVirtualTargetMap.Clear(); 308 | 309 | if (SharedContentModified == null) return; 310 | 311 | string virtualLocation; 312 | if (!_resourceNameToVirtualPathMap.TryGetValue(e.ModifiedResourceName, out virtualLocation)){ 313 | if (_autoIncludeName != null && e.ModifiedResourceName.ToLower().Contains(_autoIncludeName.ToLower())){ 314 | // extra check to force clearing type cache of no _viewStart has been requested until now 315 | SharedContentModified(this, new ContentModifiedArgs(e.ModifiedResourceName)); 316 | } 317 | return; 318 | } 319 | 320 | var modifiedName = _pathBuilder 321 | .New() 322 | .CombineWith(virtualLocation) 323 | .AddOrKeepExtension(_defaultExtension) 324 | .GetLastPart(true); 325 | 326 | if (string.Equals(modifiedName, _autoIncludeName, StringComparison.OrdinalIgnoreCase)) 327 | { 328 | // If the name equals _autoIncludeName (like _viewStart) notify listeners. Such content is included in all generated source. Normally as a result the whole type cache needs to be cleared. 329 | SharedContentModified(this, new ContentModifiedArgs(e.ModifiedResourceName)); 330 | } 331 | } 332 | #endregion 333 | 334 | } 335 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/ContentModifiedArgs.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | 10 | namespace Xipton.Razor.Core 11 | { 12 | public class ContentModifiedArgs : EventArgs 13 | { 14 | public ContentModifiedArgs(string modifiedResourceName) 15 | { 16 | if (modifiedResourceName == null) throw new ArgumentNullException("modifiedResourceName"); 17 | ModifiedResourceName = modifiedResourceName; 18 | } 19 | 20 | public string ModifiedResourceName { get; private set; } 21 | } 22 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/ContentProvider/CompositeContentProvider.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Xml.Linq; 12 | using Xipton.Razor.Extension; 13 | 14 | namespace Xipton.Razor.Core.ContentProvider 15 | { 16 | /// 17 | /// The CompositeContentProvider behaves like an IContentProvider, and holds 18 | /// an inner list of (one or more) specific IContentProvider implementations that were registered by . 19 | /// The first inner registered IContentProvider that has a valid response to a request returns the result. 20 | /// So requests for different virtual path names may be handled by different content providers. 21 | /// 22 | /// You cannot explicitly map (or route) virtual path names to specific content providers here. The first content provider that has 23 | /// a valid result on any request holds. But you can control the order in which the inner specific content providers 24 | /// are invoked, and so manage the provider priority. e.g. you could add a file provider first followed by an embedded 25 | /// resource provider. As a result you can work with content as embedded resources unless you create a file that is resolved 26 | /// by a same virtual path as any embedded resource. In that case the file content is returned because it is found first thus 27 | /// giving you the possibility to replace or customize embedded resources (e.g. layouts) by file content simply by creating 28 | /// templates (layouts) at a file location that is resolved by the same virtual path name. 29 | /// 30 | internal sealed class CompositeContentProvider : IContentProvider, IDisposable 31 | { 32 | //now order matters no dictionary is applied here 33 | private readonly List 34 | _contentProviders = new List(); 35 | 36 | #region Implementation of IContentProvider 37 | 38 | public event EventHandler ContentModified; 39 | 40 | public string TryGetResourceName(string virtualPath) 41 | { 42 | lock(_contentProviders) 43 | { 44 | return _contentProviders 45 | .Select(provider => provider.TryGetResourceName(virtualPath)).FirstOrDefault(s => s != null); 46 | } 47 | } 48 | 49 | public string TryGetContent(string resourceName) 50 | { 51 | lock (_contentProviders) 52 | { 53 | return _contentProviders 54 | .Select(provider => provider.TryGetContent(resourceName)).FirstOrDefault(s => s != null); 55 | } 56 | } 57 | 58 | public IContentProvider InitFromConfig(XElement element){ 59 | throw new NotImplementedException("The composite cannot be initialized by configuration directly (attempted by '{0}'). Instead for all individual childs (specific content providers) constructors are created at the GeneratorConfig class.".FormatWith(element)); 60 | } 61 | 62 | #endregion 63 | 64 | public CompositeContentProvider AddContentProvider(IContentProvider contentProvider, int order = 0) 65 | { 66 | if (contentProvider == null) throw new ArgumentNullException("contentProvider"); 67 | lock (_contentProviders) 68 | { 69 | contentProvider.ContentModified += OnContentModified; 70 | if (order < _contentProviders.Count && order >= 0) 71 | _contentProviders.Insert(order, contentProvider); 72 | else 73 | _contentProviders.Add(contentProvider); 74 | return this; 75 | } 76 | } 77 | 78 | public CompositeContentProvider RemoveContentProvider(IContentProvider contentProvider) { 79 | if (contentProvider == null) return this; 80 | lock (_contentProviders){ 81 | if (_contentProviders.Remove(contentProvider)){ 82 | contentProvider.ContentModified -= OnContentModified; 83 | contentProvider.TryDispose(); 84 | } 85 | } 86 | return this; 87 | } 88 | 89 | public CompositeContentProvider RemoveContentProviders(Type type) 90 | { 91 | lock (_contentProviders){ 92 | foreach (var provider in _contentProviders.ToList().Where(provider => provider.GetType() == type)){ 93 | RemoveContentProvider(provider); 94 | } 95 | return this; 96 | } 97 | } 98 | 99 | public CompositeContentProvider ClearAllContentProviders(){ 100 | lock(_contentProviders){ 101 | _contentProviders.ForEach(p => 102 | { 103 | p.ContentModified -= OnContentModified; 104 | p.TryDispose(); 105 | }); 106 | _contentProviders.Clear(); 107 | return this; 108 | } 109 | } 110 | 111 | /// 112 | /// gets the content provider by its type. Throws an exception if more than one content providers have been registered for the same type 113 | /// 114 | /// The type. 115 | /// 116 | public IContentProvider TryGetContentProvider(Type type) { 117 | lock (_contentProviders){ 118 | return _contentProviders.SingleOrDefault(p => p.GetType() == type); 119 | } 120 | } 121 | 122 | /// 123 | /// gets all content provider with the requested type. 124 | /// 125 | /// The type. 126 | /// 127 | public IEnumerable GetContentProviders(Type type) { 128 | lock (_contentProviders){ 129 | return _contentProviders.Where(p => p.GetType() == type); 130 | } 131 | } 132 | 133 | 134 | private void OnContentModified(object sender, ContentModifiedArgs e) 135 | { 136 | if (ContentModified != null) 137 | { 138 | ContentModified(sender, e); 139 | } 140 | } 141 | 142 | #region Implementation of IDisposable 143 | 144 | public void Dispose() 145 | { 146 | ClearAllContentProviders(); 147 | } 148 | 149 | #endregion 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/ContentProvider/EmbeddedResourceContentProvider.cs: -------------------------------------------------------------------------------- 1 | // disable warning "Event is never used" 2 | // justification: event is part of interface and therefore must be declared. 3 | #pragma warning disable 67 4 | 5 | #region Microsoft Public License 6 | /* This code is part of Xipton.Razor v3.0 7 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 8 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 9 | */ 10 | #endregion 11 | 12 | 13 | using System; 14 | using System.IO; 15 | using System.Linq; 16 | using System.Reflection; 17 | using System.Xml.Linq; 18 | using Xipton.Razor.Config; 19 | using Xipton.Razor.Extension; 20 | 21 | namespace Xipton.Razor.Core.ContentProvider 22 | { 23 | /// 24 | /// The EmbeddedResourceContentProvider provides content from razor templates that 25 | /// were compiled as embedded resources. It needs to know the assembly that holds the 26 | /// embedded templates so you must pass that assembly at the constructor together with 27 | /// the root namespace for all embedded resources. 28 | /// 29 | public class EmbeddedResourceContentProvider : IContentProvider 30 | { 31 | private Assembly _resourceAssembly; 32 | private string _rootNameSpace; 33 | 34 | protected EmbeddedResourceContentProvider(){} 35 | 36 | public EmbeddedResourceContentProvider(Assembly resourceAssembly, string rootNameSpace) 37 | { 38 | if (resourceAssembly == null) throw new ArgumentNullException("resourceAssembly"); 39 | if (rootNameSpace == null) throw new ArgumentNullException("rootNameSpace"); 40 | _resourceAssembly = resourceAssembly; 41 | _rootNameSpace = rootNameSpace; 42 | } 43 | 44 | #region Implementation of IContentProvider 45 | 46 | public event EventHandler ContentModified; 47 | 48 | public string TryGetResourceName(string virtualPath) 49 | { 50 | if (virtualPath.NullOrEmpty()) return null; 51 | var resourceName = _rootNameSpace + "." + virtualPath.RemoveRoot().Replace("/",".").Replace("-","_"); 52 | var resources = _resourceAssembly.GetManifestResourceNames().ToList(); 53 | return resources.FirstOrDefault(r => AreEqualResourceNames(r, resourceName)); 54 | } 55 | 56 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "The stream can be disposed multiple times.")] 57 | public string TryGetContent(string resourceName) 58 | { 59 | using (var stream = _resourceAssembly.GetManifestResourceStream(resourceName)) 60 | { 61 | if (stream == null) return null; 62 | using (var sr = new StreamReader(stream)) 63 | return sr.ReadToEnd(); 64 | } 65 | } 66 | 67 | public IContentProvider InitFromConfig(XElement element){ 68 | var resourceAssembly = element.GetAttributeValue("resourceAssembly", false); 69 | if (resourceAssembly != null) 70 | _resourceAssembly = AppDomain.CurrentDomain.GetOrLoadAssembly(resourceAssembly); 71 | _rootNameSpace = element.GetAttributeValue("rootNameSpace", false) ?? _rootNameSpace; 72 | if (_resourceAssembly == null) throw new TemplateConfigurationException("{0}: attribute resourceAssembly is required and not allowed null".FormatWith(element)); 73 | if (_rootNameSpace == null) throw new TemplateConfigurationException("{0}: attribute rootNameSpace is required and not allowed null".FormatWith(element)); 74 | return this; 75 | } 76 | 77 | 78 | 79 | #endregion 80 | 81 | private static bool AreEqualResourceNames(string s1, string resourceName) 82 | { 83 | if (s1 == null || resourceName == null) return s1 == null && resourceName == null; 84 | if (s1.Length != resourceName.Length) 85 | return false; 86 | s1 = s1.Replace("-", "_"); 87 | return string.Compare(s1, resourceName, StringComparison.OrdinalIgnoreCase) == 0; 88 | } 89 | 90 | 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/ContentProvider/FileContentProvider.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.IO; 10 | using System.Security.Permissions; 11 | using System.Xml.Linq; 12 | using Xipton.Razor.Config; 13 | using Xipton.Razor.Extension; 14 | 15 | namespace Xipton.Razor.Core.ContentProvider 16 | { 17 | /// 18 | /// The FileContentProvider provides content from files requested by virtual path names. 19 | /// A FileWatcher is created to monitor and publish file changes. 20 | /// 21 | [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] // needed because of the file watcher events 22 | [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] // needed because of the file watcher events, needed for notification on sub classing 23 | public class FileContentProvider : IContentProvider, IDisposable 24 | { 25 | #region Fields 26 | private string 27 | _rootFolder = ".".MakeAbsoluteDirectoryPath(); 28 | 29 | private FileSystemWatcher 30 | _watcher; 31 | 32 | private readonly object 33 | _syncRoot = new object(); 34 | #endregion 35 | 36 | protected FileContentProvider() {} 37 | 38 | /// 39 | /// Initializes a new instance of the class. 40 | /// It needs the root directory of where the content folders start. Normally this is the application's 41 | /// base folder, or something like [basefolder]/Views 42 | /// 43 | /// The root directory. 44 | public FileContentProvider(string rootFolder) 45 | { 46 | _rootFolder = new DirectoryInfo(rootFolder.MakeAbsoluteDirectoryPath()).FullName; 47 | } 48 | 49 | #region Implementation of IContentProvider 50 | 51 | public event EventHandler ContentModified; 52 | 53 | public virtual string TryGetResourceName(string virtualPath) 54 | { 55 | if (string.IsNullOrEmpty(virtualPath)) return null; 56 | var file = new FileInfo(Path.Combine(_rootFolder, virtualPath.RemoveRoot()).Replace('/', Path.DirectorySeparatorChar)); 57 | if (!file.Exists) return null; 58 | EnsureWatcherInitialized(); 59 | return file.FullName; 60 | } 61 | 62 | public virtual string TryGetContent(string resourceFileName){ 63 | return !File.Exists(resourceFileName) ? null : File.ReadAllText(resourceFileName); 64 | } 65 | 66 | public virtual IContentProvider InitFromConfig(XElement element){ 67 | var rootFolder = (element.GetAttributeValue("rootFolder", false) ?? _rootFolder).MakeAbsoluteDirectoryPath(); 68 | _rootFolder = new DirectoryInfo(rootFolder).FullName; 69 | return this; 70 | } 71 | 72 | #endregion 73 | 74 | #region Implementation of IDisposable 75 | public void Dispose() 76 | { 77 | Dispose(true); 78 | } 79 | protected virtual void Dispose(bool disposing) 80 | { 81 | lock (_syncRoot) 82 | { 83 | if (disposing && _watcher != null) 84 | { 85 | _watcher.EnableRaisingEvents = false; 86 | _watcher.Changed -= OnChanged; 87 | _watcher.Deleted -= OnChanged; 88 | _watcher.Renamed -= OnRenamed; 89 | _watcher.Dispose(); 90 | _watcher = null; 91 | } 92 | } 93 | } 94 | #endregion 95 | 96 | #region FileWatcher 97 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "_watcher is disposed on FileContentProvider.Dispose()")] 98 | private void EnsureWatcherInitialized() 99 | { 100 | lock (_syncRoot) 101 | { 102 | if (_watcher != null || ContentModified == null) return; 103 | 104 | _watcher = new FileSystemWatcher 105 | { 106 | Path = _rootFolder, 107 | NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastAccess, 108 | Filter = "*.*", 109 | IncludeSubdirectories = true 110 | }; 111 | 112 | _watcher.Changed += OnChanged; 113 | _watcher.Deleted += OnChanged; 114 | _watcher.Renamed += OnRenamed; 115 | _watcher.Created += OnChanged; 116 | _watcher.EnableRaisingEvents = true; 117 | } 118 | } 119 | 120 | private void OnRenamed(object sender, RenamedEventArgs e) 121 | { 122 | if (ContentModified != null) 123 | ContentModified(this, new ContentModifiedArgs(e.OldFullPath)); 124 | } 125 | 126 | private void OnChanged(object sender, FileSystemEventArgs e) 127 | { 128 | if (ContentModified != null) 129 | ContentModified(this, new ContentModifiedArgs(e.FullPath)); 130 | } 131 | 132 | #endregion 133 | 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/ContentProvider/MemoryContentProvider.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Linq; 10 | using System.Collections.Generic; 11 | using System.Xml.Linq; 12 | 13 | namespace Xipton.Razor.Core.ContentProvider 14 | { 15 | /// 16 | /// Provides in memory registration of content by its virtual path name with 17 | /// 18 | public class MemoryContentProvider : IContentProvider 19 | { 20 | private readonly Dictionary 21 | _content = new Dictionary(StringComparer.OrdinalIgnoreCase); 22 | 23 | #region Implementation of IContentProvider 24 | 25 | public event EventHandler ContentModified; 26 | 27 | public string TryGetResourceName(string virtualPath) 28 | { 29 | lock (_content) 30 | { 31 | return virtualPath != null && _content.ContainsKey(virtualPath) ? virtualPath : null; 32 | } 33 | } 34 | 35 | public string TryGetContent(string resourceName) 36 | { 37 | lock (_content) 38 | { 39 | string content; 40 | return resourceName != null && _content.TryGetValue(resourceName, out content) ? content : null; 41 | } 42 | } 43 | 44 | public IContentProvider InitFromConfig(XElement element){ 45 | return this; 46 | } 47 | 48 | #endregion 49 | 50 | public Dictionary GetRegisteredTemplates(){ 51 | return _content.ToDictionary(item => item.Key, item => item.Value); 52 | } 53 | 54 | public MemoryContentProvider RemoveTemplate(string virtualRootRelativePath){ 55 | return RegisterTemplate(virtualRootRelativePath, null); 56 | } 57 | 58 | public MemoryContentProvider RegisterTemplate(string virtualRootRelativePath, string content) 59 | { 60 | lock (_content) 61 | { 62 | var notify = ContentModified != null && _content.ContainsKey(virtualRootRelativePath); 63 | 64 | if (content == null) 65 | _content.Remove(virtualRootRelativePath); 66 | else 67 | _content[virtualRootRelativePath] = content; 68 | 69 | if (notify) 70 | ContentModified(this, new ContentModifiedArgs(virtualRootRelativePath)); 71 | } 72 | return this; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/DynamicData.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections; 10 | using System.Collections.Concurrent; 11 | using System.Collections.Generic; 12 | using System.Dynamic; 13 | using System.Linq; 14 | using System.Runtime.CompilerServices; 15 | 16 | namespace Xipton.Razor.Core 17 | { 18 | /// 19 | /// This class is used for both the template's Model (with anonymous model types only) and ViewBag property 20 | /// 21 | public class DynamicData : DynamicObject 22 | { 23 | private readonly ConcurrentDictionary 24 | _data; 25 | 26 | public DynamicData(object target) 27 | : this(target == null ? null : (IDictionary)target.GetType() 28 | .GetProperties() 29 | .Where(property => property.CanRead && property.GetIndexParameters().Length == 0) 30 | .ToDictionary(property => property.Name, property => property.GetValue(target, null))) 31 | { 32 | } 33 | 34 | public DynamicData(IDictionary data) 35 | { 36 | _data = new ConcurrentDictionary(); 37 | if (data == null) 38 | return; 39 | 40 | foreach (var key in data.Keys) 41 | _data[key.ToString()] = ToDynamic(data[key]); 42 | } 43 | 44 | public DynamicData(IEnumerable> data = null) 45 | { 46 | _data = new ConcurrentDictionary(); 47 | if (data != null) 48 | foreach (var pair in data) 49 | _data[pair.Key] = ToDynamic(pair.Value); 50 | } 51 | 52 | /// 53 | /// The model only needs to be wrapped into a IDynamicMetaObjectProvider if it is an emitted anonymous type. 54 | /// 55 | /// The model. 56 | /// 57 | public static dynamic ToDynamic(object model) { 58 | return model == null || !Attribute.IsDefined(model.GetType(), typeof(CompilerGeneratedAttribute)) 59 | ? model 60 | : (model is IDynamicMetaObjectProvider 61 | ? model 62 | : new DynamicData(model)); 63 | } 64 | 65 | 66 | public override IEnumerable GetDynamicMemberNames() 67 | { 68 | return _data.Keys; 69 | } 70 | 71 | public override bool TryGetMember(GetMemberBinder binder, out object result) 72 | { 73 | return _data.TryGetValue(binder.Name, out result); 74 | } 75 | 76 | public override bool TrySetMember(SetMemberBinder binder, object value) 77 | { 78 | _data[binder.Name] = ToDynamic(value); 79 | return true; 80 | } 81 | 82 | 83 | } 84 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/Generator/CSharp/XiptonCSharpCodeLanguage.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System.Web.Razor; 9 | using System.Web.Razor.Parser; 10 | 11 | namespace Xipton.Razor.Core.Generator.CSharp 12 | { 13 | public class XiptonCSharpCodeLanguage : CSharpRazorCodeLanguage, IXiptonCodeLanguage 14 | { 15 | public override ParserBase CreateCodeParser() 16 | { 17 | return new XiptonCSharpCodeParser(); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/Generator/CSharp/XiptonCSharpCodeParser.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System.Web.Razor.Parser; 9 | 10 | namespace Xipton.Razor.Core.Generator.CSharp 11 | { 12 | /// 13 | /// This C# CodeParser is extended for being able to handle the @model directive 14 | /// 15 | public class XiptonCSharpCodeParser : CSharpCodeParser 16 | { 17 | 18 | private static class Constants 19 | { 20 | public const string ModelKeyword = "model"; 21 | public const string ParseErrorInheritsKeywordMustBeFollowedByTypeName = "The 'model' keyword must be followed by a type name on the same line."; 22 | } 23 | public XiptonCSharpCodeParser() 24 | { 25 | MapDirectives(ModelDirective, new[]{Constants.ModelKeyword}); 26 | } 27 | 28 | protected virtual void ModelDirective() 29 | { 30 | AcceptAndMoveNext(); 31 | ModelDirectiveCore(); 32 | } 33 | 34 | protected void ModelDirectiveCore() 35 | { 36 | BaseTypeDirective(Constants.ParseErrorInheritsKeywordMustBeFollowedByTypeName, modelType => new SetModelCodeGenerator(modelType)); 37 | } 38 | 39 | } 40 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/Generator/IXiptonCodeLanguage.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | namespace Xipton.Razor.Core.Generator 9 | { 10 | 11 | /// 12 | /// Marker interface for Xipton CodeLanguages 13 | /// 14 | public interface IXiptonCodeLanguage 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/Generator/SetModelCodeGenerator.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.CodeDom; 10 | using System.Web.Razor; 11 | using System.Web.Razor.Generator; 12 | using System.Web.Razor.Parser.SyntaxTree; 13 | using Xipton.Razor.Extension; 14 | 15 | namespace Xipton.Razor.Core.Generator 16 | { 17 | /// 18 | /// SpanCodeGenerator for the model directive 19 | /// 20 | public class SetModelCodeGenerator : SpanCodeGenerator 21 | { 22 | public SetModelCodeGenerator(string modelType){ 23 | ModelType = modelType; 24 | } 25 | 26 | public override void GenerateCode(Span target, CodeGeneratorContext context) 27 | { 28 | context.GeneratedClass.BaseTypes.Clear(); 29 | context.GeneratedClass.BaseTypes.Add(new CodeTypeReference(ResolveType(context))); 30 | 31 | #region Work Around 32 | if (!(context.Host.CodeLanguage is VBRazorCodeLanguage)) 33 | context.GeneratedClass.LinePragma = context.GenerateLinePragma(target, CalculateSpanPadding(target,0)); 34 | //else 35 | // exclude VBRazorCodeLanguage 36 | // with VB I found a problem with the #End ExternalSource directive rendered at the GeneratedClass's end while it should not be rendered 37 | // this only effects the compile error report 38 | 39 | #endregion 40 | } 41 | 42 | //thanks to Marko Lahma 43 | private int CalculateSpanPadding(Span target, int generatedStart) 44 | { 45 | int num = target.Start.CharacterIndex - generatedStart; 46 | if (num < 0) 47 | num = 0; 48 | return num; 49 | } 50 | 51 | protected virtual string ResolveType(CodeGeneratorContext context) 52 | { 53 | var modelType = ModelType.Trim(); 54 | if (context.Host.CodeLanguage is VBRazorCodeLanguage) 55 | return "{0}(Of {1})".FormatWith(context.Host.DefaultBaseClass, modelType); 56 | if (context.Host.CodeLanguage is CSharpRazorCodeLanguage) 57 | return "{0}<{1}>".FormatWith(context.Host.DefaultBaseClass, modelType); 58 | throw new TemplateException("Code language {0} is not supported.".FormatWith(context.Host.CodeLanguage)); 59 | } 60 | 61 | public string ModelType { get; private set; } 62 | 63 | public override string ToString() 64 | { 65 | return "Model:" + ModelType; 66 | } 67 | public override bool Equals(object obj) 68 | { 69 | var other = obj as SetModelCodeGenerator; 70 | return other != null && string.Equals(ModelType, other.ModelType, StringComparison.Ordinal); 71 | } 72 | public override int GetHashCode() 73 | { 74 | return ModelType.GetHashCode(); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/Generator/VB/XiptonVBCodeLanguage.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System.Web.Razor; 9 | using System.Web.Razor.Parser; 10 | 11 | namespace Xipton.Razor.Core.Generator.VB 12 | { 13 | public class XiptonVBCodeLanguage : VBRazorCodeLanguage, IXiptonCodeLanguage 14 | { 15 | public override ParserBase CreateCodeParser() 16 | { 17 | return new XiptonVBCodeParser(); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/Generator/VB/XiptonVBCodeParser.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System.Web.Razor.Generator; 9 | using System.Web.Razor.Parser; 10 | using System.Web.Razor.Parser.SyntaxTree; 11 | using System.Web.Razor.Tokenizer.Symbols; 12 | 13 | namespace Xipton.Razor.Core.Generator.VB 14 | { 15 | /// 16 | /// This VB CodeParser is extended for being able to handle the @ModelType directive 17 | /// 18 | public class XiptonVBCodeParser : VBCodeParser 19 | { 20 | 21 | private static class Constants 22 | { 23 | public const string ModelKeyword = "ModelType"; 24 | public const string ParseErrorInheritsKeywordMustBeFollowedByTypeName = "The 'ModelType' keyword must be followed by a type name on the same line."; 25 | } 26 | public XiptonVBCodeParser() 27 | { 28 | MapDirective(Constants.ModelKeyword, ModelStatement); 29 | } 30 | 31 | protected virtual bool ModelStatement() 32 | { 33 | Span.CodeGenerator = SpanCodeGenerator.Null; 34 | Context.CurrentBlock.Type = BlockType.Directive; 35 | AcceptAndMoveNext(); 36 | var currentLocation = CurrentLocation; 37 | if (At(VBSymbolType.WhiteSpace)) 38 | { 39 | Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; 40 | } 41 | AcceptWhile(VBSymbolType.WhiteSpace); 42 | Output(SpanKind.MetaCode); 43 | if (EndOfFile || At(VBSymbolType.WhiteSpace) || At(VBSymbolType.NewLine)) 44 | { 45 | Context.OnError(currentLocation, Constants.ParseErrorInheritsKeywordMustBeFollowedByTypeName); 46 | } 47 | AcceptUntil(VBSymbolType.NewLine); 48 | if (!Context.DesignTimeMode) 49 | { 50 | Optional(VBSymbolType.NewLine); 51 | } 52 | string model = Span.GetContent(); 53 | Span.CodeGenerator = new SetModelCodeGenerator(model); 54 | Output(SpanKind.Code); 55 | return false; 56 | } 57 | 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/Generator/XiptonEngineHost.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Web.Razor; 12 | using System.Web.Razor.Generator; 13 | using Xipton.Razor.Config; 14 | 15 | namespace Xipton.Razor.Core.Generator 16 | { 17 | /// 18 | /// The engine host is the part where the code rendering takes place. 19 | /// 20 | public class XiptonEngineHost : RazorEngineHost 21 | { 22 | #region Fields 23 | private readonly RazorConfig 24 | _config; 25 | 26 | private string 27 | _defaultBaseClass, 28 | _defaultNamespace; 29 | 30 | private GeneratedClassContext 31 | _generatedClassContext; 32 | 33 | private readonly ISet 34 | _namespaceImports; 35 | #endregion 36 | 37 | /// 38 | /// Initializes a new instance of the class. 39 | /// 40 | /// The config holds all settings that are needed to initialzie the host. 41 | public XiptonEngineHost(RazorConfig config) 42 | : base(config.Templates.Language) 43 | { 44 | if (config == null) throw new ArgumentNullException("config"); 45 | _defaultNamespace = "Xipton.Razor.Generated"; 46 | _config = config.AsReadonly(); 47 | _defaultBaseClass = _config.Templates.NonGenericBaseTypeName; 48 | _namespaceImports = new HashSet(); 49 | _config.Namespaces.ToList().ForEach(ns => _namespaceImports.Add(ns)); 50 | 51 | // the GeneratedClassContext defines the methods that are generated to handle the template 52 | // control like writing the generated output and also handle other control operations like 53 | // defining sections inside the template 54 | _generatedClassContext = new GeneratedClassContext("Execute", "Write", "WriteLiteral", "WriteTo", "WriteLiteralTo", typeof(HelperResult).FullName, "DefineSection") { 55 | ResolveUrlMethodName = "ResolveUrl", 56 | }; 57 | 58 | } 59 | 60 | public override string DefaultBaseClass 61 | { 62 | get { return _defaultBaseClass; } 63 | set { _defaultBaseClass = value; } 64 | } 65 | 66 | public override GeneratedClassContext GeneratedClassContext 67 | { 68 | get { return _generatedClassContext; } 69 | set { _generatedClassContext = value; } 70 | } 71 | 72 | public override ISet NamespaceImports 73 | { 74 | get { return _namespaceImports; } 75 | } 76 | 77 | public override string DefaultNamespace 78 | { 79 | get { return _defaultNamespace; } 80 | set{ _defaultNamespace = value; } 81 | } 82 | 83 | } 84 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/HelperResult.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | using System; 8 | using System.Globalization; 9 | using System.IO; 10 | 11 | namespace Xipton.Razor.Core { 12 | 13 | public class HelperResult : ILiteralString { 14 | private readonly Action _action; 15 | 16 | public HelperResult(Action action) { 17 | if (action == null) throw new ArgumentNullException("action"); 18 | _action = action; 19 | } 20 | 21 | public override string ToString() { 22 | using (var writer = new StringWriter(CultureInfo.InvariantCulture)) { 23 | _action(writer); 24 | return writer.ToString(); 25 | } 26 | } 27 | 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/IContentProvider.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Xml.Linq; 10 | 11 | namespace Xipton.Razor.Core 12 | { 13 | /// 14 | /// The contentent provider provides content by its virtual path. 15 | /// 16 | public interface IContentProvider 17 | { 18 | /// 19 | /// Occurs when content is modified. If the corresponding content provider provides content that 20 | /// cannot be modified, this event is never thrown by that instance, e.g., an embedded resource content provider. 21 | /// 22 | event EventHandler ContentModified; 23 | /// 24 | /// Maps the virtual path to the native resource name, e.g., a file system path name, 25 | /// an embedded resource name, a database key, etc... You could use such a native name 26 | /// as a cache key 27 | /// 28 | /// The virtual path. 29 | /// The reousrce name or null if no resource was found 30 | string TryGetResourceName(string virtualPath); 31 | 32 | /// 33 | /// Returns the content by its native resource name. It must return null 34 | /// if no content was found. 35 | /// 36 | /// Name of the resource. 37 | /// The content of null if no content was found 38 | string TryGetContent(string resourceName); 39 | 40 | /// 41 | /// Initializes this instance from the corresponding configuration node. 42 | /// 43 | /// The configuration element. 44 | /// 45 | IContentProvider InitFromConfig(XElement element); 46 | } 47 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/ITemplateController.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | namespace Xipton.Razor.Core 9 | { 10 | 11 | public interface ITemplateController : ITemplate 12 | { 13 | ITemplateController SetModel(object model); 14 | ITemplateController SetViewBag(object viewBag); 15 | ITemplateController ApplyLayout(ITemplateController layoutTemplate); 16 | ITemplateController SetVirtualPath(string virualPath); 17 | ITemplateController SetContext(RazorContext context); 18 | ITemplateController SetParent(ITemplateController parent); 19 | ITemplateController AddChild(ITemplateController child); 20 | ITemplateController Execute(); 21 | ITemplateController TryApplyLayout(); 22 | ITemplateController SetGeneratedSourceCode(string generatedSourceCode); 23 | string RenderSectionByChildRequest(string sectionName); 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/TemplateBindingException.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Runtime.Serialization; 10 | 11 | namespace Xipton.Razor.Core 12 | { 13 | // thrown if any runtime binding error (late binding error) occurs at any template 14 | [Serializable] 15 | public class TemplateBindingException : TemplateException 16 | { 17 | public TemplateBindingException() 18 | { 19 | } 20 | 21 | public TemplateBindingException(string message) 22 | : base(message) 23 | { 24 | } 25 | 26 | public TemplateBindingException(string message, Exception inner) 27 | : base(message, inner) 28 | { 29 | } 30 | 31 | protected TemplateBindingException( 32 | SerializationInfo info, 33 | StreamingContext context) 34 | : base(info, context) 35 | { 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/TemplateCompileException.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Runtime.Serialization; 10 | 11 | namespace Xipton.Razor.Core 12 | { 13 | [Serializable] 14 | public class TemplateCompileException : TemplateException 15 | { 16 | public TemplateCompileException() 17 | { 18 | } 19 | 20 | public TemplateCompileException(string message) : base(message) 21 | { 22 | } 23 | 24 | public TemplateCompileException(string message, Exception inner) : base(message, inner) 25 | { 26 | } 27 | 28 | protected TemplateCompileException( 29 | SerializationInfo info, 30 | StreamingContext context) : base(info, context) 31 | { 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/TemplateException.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Runtime.Serialization; 10 | 11 | namespace Xipton.Razor.Core 12 | { 13 | [Serializable] 14 | public class TemplateException : Exception 15 | { 16 | 17 | public TemplateException() 18 | { 19 | } 20 | 21 | public TemplateException(string message) : base(message) 22 | { 23 | } 24 | 25 | public TemplateException(string message, Exception inner) : base(message, inner) 26 | { 27 | } 28 | 29 | protected TemplateException( 30 | SerializationInfo info, 31 | StreamingContext context) : base(info, context) 32 | { 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/TemplateFactory.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.CodeDom; 10 | using System.CodeDom.Compiler; 11 | using System.Collections.Concurrent; 12 | using System.Collections.Generic; 13 | using System.IO; 14 | using System.Linq; 15 | using System.Reflection; 16 | using System.Text; 17 | using System.Web.Razor; 18 | using Xipton.Razor.Core.Generator; 19 | using Xipton.Razor.Extension; 20 | 21 | namespace Xipton.Razor.Core 22 | { 23 | /// 24 | /// The TemplateFactory provides template instances by their virtual path names. 25 | /// The corresponding template types are cached, the template instances are not cached 26 | /// The template factory needs a context to be injected at the constructor. The context is passed to each created template instance 27 | /// 28 | public class TemplateFactory : IDisposable { 29 | 30 | #region Types 31 | private class CacheBucket 32 | { 33 | public CacheBucket(Type generatedTemplateType){ 34 | GeneratedTemplateType = generatedTemplateType; 35 | } 36 | public Type GeneratedTemplateType { get; private set; } 37 | public string GeneratedSourceCode { get; set; } 38 | 39 | public override bool Equals(object obj) { 40 | var other = obj as CacheBucket; 41 | return other != null && other.GeneratedTemplateType == GeneratedTemplateType; 42 | } 43 | public override int GetHashCode() { 44 | return GeneratedTemplateType == null ? 0 : GeneratedTemplateType.GetHashCode(); 45 | } 46 | } 47 | #endregion 48 | 49 | #region Fields 50 | private const string 51 | _sourceFilenamePrefix = "template: "; 52 | 53 | private readonly ConcurrentDictionary 54 | _compiledTemplateTypeCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); 55 | 56 | private readonly RazorTemplateEngine 57 | _razorEngine; 58 | 59 | private readonly RazorContext 60 | _razorContext; 61 | #endregion 62 | 63 | public TemplateFactory(RazorContext razorContext) { 64 | if (razorContext == null) throw new ArgumentNullException("razorContext"); 65 | _razorContext = razorContext; 66 | _razorEngine = new RazorTemplateEngine(new XiptonEngineHost(razorContext.Config)); 67 | ContentManager = new ContentManager(razorContext.Config); 68 | ContentManager.ContentProvider.ContentModified += OnContentModified; 69 | ContentManager.SharedContentModified += OnSharedContentModified; 70 | } 71 | 72 | #region Public 73 | 74 | public ContentManager ContentManager { get; private set; } 75 | 76 | public ITemplate CreateTemplateInstance(string requestedVirtualTemplateName, bool throwExceptionOnVirtualPathNotFound = true) 77 | { 78 | // all caching is done by the native resource names because any change notification is done with the native resource name as a parameter 79 | var resourceName = ContentManager.TryGetResourceName(requestedVirtualTemplateName); 80 | if (resourceName == null){ 81 | if (throwExceptionOnVirtualPathNotFound) 82 | throw new TemplateException("Template '{0}' not found.".FormatWith(requestedVirtualTemplateName)); 83 | return null; 84 | } 85 | 86 | var bucket = _compiledTemplateTypeCache.GetOrAdd(resourceName, key => CreateBucket(requestedVirtualTemplateName)); 87 | 88 | // note that the types are cached, and not the template instances 89 | var template = bucket 90 | .GeneratedTemplateType 91 | .CreateInstance() 92 | .CastTo() 93 | .SetVirtualPath(ContentManager.TryGetVirtualPath(requestedVirtualTemplateName)) 94 | .CastTo() 95 | .SetGeneratedSourceCode(bucket.GeneratedSourceCode) 96 | .SetContext(_razorContext); 97 | 98 | return template; 99 | } 100 | 101 | public void ClearTypeCache() 102 | { 103 | _compiledTemplateTypeCache.Clear(); 104 | } 105 | 106 | #endregion 107 | 108 | #region Implementation of IDisposable 109 | 110 | public void Dispose() { 111 | Dispose(true); 112 | } 113 | 114 | private void Dispose(bool disposing) { 115 | if (!disposing) return; 116 | var provider = ContentManager; 117 | ContentManager = null; 118 | if (provider == null) return; 119 | provider.ContentProvider.ContentModified -= OnContentModified; 120 | provider.SharedContentModified -= OnSharedContentModified; 121 | provider.Dispose(); 122 | } 123 | 124 | #endregion 125 | 126 | #region Private 127 | 128 | private void OnContentModified(object sender, ContentModifiedArgs e) 129 | { 130 | CacheBucket value; 131 | _compiledTemplateTypeCache.TryRemove(e.ModifiedResourceName, out value); 132 | } 133 | 134 | private void OnSharedContentModified(object sender, ContentModifiedArgs e) 135 | { 136 | // clear the whole cache is any shared content has been modified 137 | ClearTypeCache(); 138 | } 139 | 140 | private CacheBucket CreateBucket(string reqestedPath) { 141 | var virtualPath = ContentManager.TryGetVirtualPath(reqestedPath); 142 | var resourceName = ContentManager.TryGetResourceName(virtualPath); 143 | var content = ContentManager.TryGetContent(virtualPath); 144 | string className, rootNamespace; 145 | GetClassName(virtualPath, out rootNamespace, out className); 146 | string generatedSource; 147 | var assembly = CreateAssembly(resourceName, @rootNamespace, className, content, out generatedSource); 148 | return new CacheBucket(assembly.GetType(rootNamespace + "." + className, true, false)){GeneratedSourceCode = generatedSource }; 149 | } 150 | 151 | private Assembly CreateAssembly(string resourceName, string rootNamespace, string className, string content, out string generatedSource) 152 | { 153 | using (var codeProvider = CreateCodeProvider()) 154 | { 155 | using (var stringReader = new StringReader(content)) { 156 | var generatorResult = _razorEngine.GenerateCode( 157 | stringReader, 158 | className, 159 | rootNamespace, 160 | "{0}{1}".FormatWith(_sourceFilenamePrefix, resourceName) 161 | ); 162 | generatedSource = _razorContext.Config.Templates.IncludeGeneratedSourceCode ? GenerateSourceCode(generatorResult.GeneratedCode) : null; 163 | 164 | if (!CheckParseResults(resourceName, generatorResult, content)) 165 | return null; 166 | 167 | 168 | var compilerParameter = new CompilerParameters(_razorContext.Config.References.ToArray()) { GenerateInMemory = true, CompilerOptions = "/optimize" }; 169 | 170 | var compilerResults = codeProvider.CompileAssemblyFromDom(compilerParameter, generatorResult.GeneratedCode); 171 | 172 | return !CheckCompileResults(generatorResult, compilerResults, content, generatedSource) ? null : compilerResults.CompiledAssembly; 173 | } 174 | } 175 | } 176 | 177 | private void GetClassName(string virtualTemplatePath, out string @namespace, out string className) 178 | { 179 | virtualTemplatePath = new VirtualPathBuilder(_razorContext.Config.RootOperator.Path) 180 | .CombineWith(virtualTemplatePath) 181 | .Normalize() 182 | .WithRootOperator() 183 | .RemoveExtension() 184 | .ToString() 185 | .RemoveRoot(); 186 | var sb = new StringBuilder(); 187 | bool firstTokenChar = true; 188 | foreach(var ch in virtualTemplatePath) 189 | { 190 | if (ch >= '0' && ch <= '9'){ 191 | if (firstTokenChar) 192 | sb.Append('_'); 193 | sb.Append(ch); 194 | firstTokenChar = false; 195 | } 196 | else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_'){ 197 | sb.Append(firstTokenChar ? char.ToUpper(ch) : ch); 198 | firstTokenChar = false; 199 | } 200 | else if (ch == '/'){ 201 | sb.Append('.'); 202 | firstTokenChar = true; 203 | } 204 | 205 | } 206 | className = sb.ToString(); 207 | var index = className.LastIndexOf(".", StringComparison.Ordinal); 208 | if (index == -1) 209 | { 210 | @namespace = _razorEngine.Host.DefaultNamespace; 211 | } 212 | else 213 | { 214 | @namespace = className.Substring(0, index); 215 | className = className.Substring(index + 1); 216 | } 217 | } 218 | 219 | private CodeDomProvider CreateCodeProvider() 220 | { 221 | return _razorEngine 222 | .Host 223 | .CodeLanguage 224 | .CodeDomProviderType 225 | .CreateInstance() 226 | .CastTo(); 227 | } 228 | 229 | private static bool CheckParseResults(string resourceName, GeneratorResults generatorResults, string templateContent) 230 | { 231 | if (!generatorResults.ParserErrors.Any()) 232 | return true; 233 | 234 | var contentlines = templateContent 235 | .Split('\n') 236 | .Select(line => line.Trim()) 237 | .ToList(); 238 | string parseExceptionMessage; 239 | 240 | try 241 | { 242 | parseExceptionMessage = string.Join( 243 | Environment.NewLine, 244 | generatorResults 245 | .ParserErrors 246 | .Select(error => "parse error at template: {0}{1}line {2}: {3}{1}parse error: {4}".FormatWith(resourceName, Environment.NewLine, error.Location.LineIndex + 1, contentlines[error.Location.LineIndex], error.Message)) 247 | .ToArray() 248 | ); 249 | } 250 | catch 251 | { 252 | // on any error create a raw error message 253 | parseExceptionMessage = string.Join( 254 | Environment.NewLine, 255 | generatorResults 256 | .ParserErrors 257 | .Select(error => error.ToString()) 258 | .ToArray()); 259 | } 260 | 261 | throw new TemplateParseException(parseExceptionMessage); 262 | } 263 | private bool CheckCompileResults(GeneratorResults generatorResults, CompilerResults compilerResults, string templateContent, string generatedSource) 264 | { 265 | if (!compilerResults.Errors.HasErrors) 266 | return true; 267 | 268 | List sourceLines = null; 269 | Func> generatedSourceLines = () => sourceLines ?? (sourceLines = (generatedSource ?? GenerateSourceCode(generatorResults.GeneratedCode)).Split('\n').Select(line => line.Trim()).ToList()); 270 | 271 | var contentlines = templateContent 272 | .Split('\n') 273 | .Select(line => line.Trim()) 274 | .ToList(); 275 | 276 | string compileExceptionMessage; 277 | try 278 | { 279 | compileExceptionMessage = 280 | string.Join( 281 | "{0}{0}".FormatWith(Environment.NewLine), 282 | compilerResults 283 | .Errors 284 | .OfType() 285 | .Where(error => !error.IsWarning) 286 | .Select(error => "compile error at {0}{1}line {2}: {3}{1}compile error: {4}: {5}".FormatWith(error.FileName, Environment.NewLine, error.Line, (error.FileName ?? "").StartsWith(_sourceFilenamePrefix) ? contentlines[error.Line - 1] : generatedSourceLines()[error.Line - 1], error.ErrorNumber, error.ErrorText)) 287 | .ToArray() 288 | ); 289 | } 290 | catch 291 | { 292 | // on any error create a raw error message 293 | compileExceptionMessage = string.Join( 294 | "{0}{0}".FormatWith(Environment.NewLine), 295 | compilerResults 296 | .Errors 297 | .OfType() 298 | .Where(error => !error.IsWarning) 299 | .Select(error => error.ToString()) 300 | .ToArray()); 301 | } 302 | 303 | throw new TemplateCompileException(compileExceptionMessage); 304 | } 305 | 306 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "StringWriter can be disposed more than once.")] 307 | private string GenerateSourceCode(CodeCompileUnit compileunit) 308 | { 309 | using (var sw = new StringWriter()) 310 | { 311 | using (var tw = new IndentedTextWriter(sw, " ")) 312 | CreateCodeProvider().GenerateCodeFromCompileUnit(compileunit, tw, new CodeGeneratorOptions()); 313 | return sw.ToString(); 314 | } 315 | } 316 | 317 | #endregion 318 | 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/TemplateParseException.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Runtime.Serialization; 10 | 11 | namespace Xipton.Razor.Core 12 | { 13 | [Serializable] 14 | public class TemplateParseException : TemplateException 15 | { 16 | public TemplateParseException() 17 | { 18 | } 19 | 20 | public TemplateParseException(string message) 21 | : base(message) 22 | { 23 | } 24 | 25 | public TemplateParseException(string message, Exception inner) 26 | : base(message, inner) 27 | { 28 | } 29 | 30 | protected TemplateParseException( 31 | SerializationInfo info, 32 | StreamingContext context) 33 | : base(info, context) 34 | { 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Core/TemplateTreeException.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Runtime.Serialization; 10 | 11 | namespace Xipton.Razor.Core 12 | { 13 | // thrown if there are problems detected with the template tree (parent or childs) 14 | [Serializable] 15 | public class TemplateTreeException : TemplateException 16 | { 17 | public TemplateTreeException() 18 | { 19 | } 20 | 21 | public TemplateTreeException(string message) 22 | : base(message) 23 | { 24 | } 25 | 26 | public TemplateTreeException(string message, Exception inner) 27 | : base(message, inner) 28 | { 29 | } 30 | 31 | protected TemplateTreeException( 32 | SerializationInfo info, 33 | StreamingContext context) 34 | : base(info, context) 35 | { 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Extension/AppDomainExtension.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections.Concurrent; 10 | using System.IO; 11 | using System.Linq; 12 | using System.Reflection; 13 | using System.Web.Razor; 14 | 15 | namespace Xipton.Razor.Extension { 16 | public static class AppDomainExtension { 17 | 18 | private static readonly ConcurrentDictionary 19 | _binAssembliesLoadedBefore = new ConcurrentDictionary(); 20 | 21 | /// 22 | /// Ensures that the required xipton assemblies are loaded in the excution context. 23 | /// 24 | public static AppDomain EnsureXiptonAssembliesLoaded(this AppDomain domain) { 25 | #pragma warning disable 168 26 | var t = typeof(ParserResults); 27 | #pragma warning restore 168 28 | return domain; 29 | } 30 | 31 | /// 32 | /// Ensures that all assemblies in the bin folder have been loaded into the excution context. 33 | /// 34 | public static AppDomain EnsureBinAssembliesLoaded(this AppDomain domain) { 35 | 36 | if (_binAssembliesLoadedBefore.ContainsKey(domain.FriendlyName)) 37 | return domain; 38 | 39 | var binFolder = !string.IsNullOrEmpty(domain.RelativeSearchPath) 40 | ? Path.Combine(domain.BaseDirectory, domain.RelativeSearchPath) 41 | : domain.BaseDirectory; 42 | 43 | Directory.GetFiles(binFolder, "*.dll") 44 | .Union(Directory.GetFiles(binFolder, "*.exe")) 45 | .ToList() 46 | .ForEach(domain.EnsureAssemblyIsLoaded); 47 | 48 | _binAssembliesLoadedBefore[domain.FriendlyName] = true; 49 | 50 | return domain; 51 | } 52 | 53 | public static Assembly GetOrLoadAssembly(this AppDomain domain, string assemblyFileNameOrAssemblyDisplayName) { 54 | var assemblyName = assemblyFileNameOrAssemblyDisplayName.IsFileName() 55 | ? AssemblyName.GetAssemblyName(assemblyFileNameOrAssemblyDisplayName) 56 | : new AssemblyName(assemblyFileNameOrAssemblyDisplayName); 57 | return domain 58 | .GetAssemblies() 59 | .FirstOrDefault(a => ReferenceMatchesDefinitionEx(assemblyName, a.GetName())) ?? domain.Load(assemblyName); 60 | } 61 | 62 | private static void EnsureAssemblyIsLoaded(this AppDomain domain, string assemblyFileName) { 63 | try{ 64 | var assemblyName = AssemblyName.GetAssemblyName(assemblyFileName); 65 | if (!domain.GetAssemblies().Any(a => ReferenceMatchesDefinitionEx(assemblyName, a.GetName()))){ 66 | domain.Load(assemblyName); 67 | } 68 | } 69 | catch (BadImageFormatException){ 70 | // thrown by GetAssemblyName 71 | // ignore this assembly since it is an unmanaged assembly 72 | } 73 | } 74 | 75 | private static bool ReferenceMatchesDefinitionEx(AssemblyName reference, AssemblyName definition) 76 | { 77 | #if __MonoCS__ 78 | return string.Equals(reference.ToString(), definition.ToString(), StringComparison.OrdinalIgnoreCase); 79 | #else 80 | return AssemblyName.ReferenceMatchesDefinition(reference, definition); 81 | #endif 82 | } 83 | 84 | 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Extension/AssemblyExtension.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.IO; 10 | using System.Reflection; 11 | 12 | namespace Xipton.Razor.Extension 13 | { 14 | public static class AssemblyExtension 15 | { 16 | public static string GetFileName(this Assembly assembly) 17 | { 18 | 19 | #if __MonoCS__ 20 | return assembly == null 21 | ? null 22 | : new DirectoryInfo(IsNotWindows ? assembly.CodeBase.Replace("file:///", "/") : assembly.CodeBase.Replace("file:///", string.Empty)).FullName; 23 | #else 24 | return assembly == null 25 | ? null 26 | : new DirectoryInfo(assembly.CodeBase.Replace("file:///", string.Empty)).FullName; 27 | #endif 28 | 29 | } 30 | 31 | public static bool IsNotWindows 32 | { 33 | get 34 | { 35 | var p = (int)Environment.OSVersion.Platform; 36 | return (p == 4) || (p == 6) || (p == 128); 37 | } 38 | } 39 | 40 | //http://msdn.microsoft.com/en-us/library/ms173100.aspx 41 | public static bool IsManagedAssembly(this string self) { 42 | 43 | if (self == null || !File.Exists(self)) { 44 | return false; 45 | } 46 | 47 | try{ 48 | AssemblyName.GetAssemblyName(self); 49 | return true; 50 | } 51 | catch (BadImageFormatException){ 52 | return false; 53 | } 54 | catch (FileLoadException){ 55 | return true; 56 | } 57 | catch{ 58 | return false; 59 | } 60 | 61 | } 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Extension/ObjectExtension.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | 10 | namespace Xipton.Razor.Extension 11 | { 12 | public static class ObjectExtension 13 | { 14 | public static T CastTo(this object target) 15 | { 16 | return target == null ? default(T) : (T) target; 17 | } 18 | 19 | public static bool TryDispose(this object target) 20 | { 21 | var disposable = target as IDisposable; 22 | if (disposable != null) 23 | { 24 | disposable.Dispose(); 25 | return true; 26 | } 27 | return false; 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Extension/StringExtension.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.IO; 10 | 11 | namespace Xipton.Razor.Extension 12 | { 13 | public static class StringExtension 14 | { 15 | 16 | public static string FormatWith(this string format, params object[] args) 17 | { 18 | return format == null ? null : string.Format(format, args); 19 | } 20 | 21 | public static bool NullOrEmpty(this string value) 22 | { 23 | return string.IsNullOrEmpty(value); 24 | } 25 | 26 | public static string EmptyAsNull(this string value) { 27 | return string.IsNullOrEmpty(value) ? null : value; 28 | } 29 | 30 | public static bool HasVirtualRootOperator(this string path) 31 | { 32 | return path != null && path.StartsWith("~"); 33 | } 34 | 35 | public static string RemoveRoot(this string path) 36 | { 37 | if (path.NullOrEmpty()) return path; 38 | if (path[0] == '~') path = path.Substring(1); 39 | return path.TrimStart('\\').TrimStart('/'); 40 | } 41 | 42 | public static bool IsAbsoluteVirtualPath(string path) 43 | { 44 | return path != null && (path.Contains(":") || path.StartsWith("/") || path.StartsWith("\\")); 45 | } 46 | 47 | public static string MakeAbsoluteDirectoryPath(this string path){ 48 | var baseDir = Path.GetDirectoryName(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile); 49 | if (baseDir == null) 50 | return path; 51 | return Path.Combine(baseDir, path ?? "."); 52 | } 53 | 54 | public static string HtmlEncode(this string value) 55 | { 56 | return value == null ? null : System.Net.WebUtility.HtmlEncode(value); 57 | } 58 | 59 | internal static bool IsFileName(this string path) { 60 | return path != null && File.Exists(path); 61 | } 62 | internal static bool IsXmlContent(this string content) { 63 | return content != null && content.TrimStart().StartsWith("<"); 64 | } 65 | internal static bool IsUrl(this string value) { 66 | return value != null && (value.HasVirtualRootOperator() || value.StartsWith("/")); 67 | } 68 | 69 | } 70 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/Extension/TemplateExtension.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | 10 | namespace Xipton.Razor.Extension 11 | { 12 | //pre compile view extension 13 | public static class RazorMachineExtension 14 | { 15 | public static void EnsureViewCompiled(this RazorMachine self, string path, bool throwException = true) 16 | { 17 | if (self == null) throw new ArgumentNullException("self"); 18 | self.Context.TemplateFactory.CreateTemplateInstance(path, throwException); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Extension/TypeExtension.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | 10 | namespace Xipton.Razor.Extension 11 | { 12 | public static class TypeExtension 13 | { 14 | public static object CreateInstance(this Type type) 15 | { 16 | return type == null ? null : Activator.CreateInstance(type, true); 17 | } 18 | 19 | public static T CreateInstance(this Type type) { 20 | return type == null ? default(T) : (T)Activator.CreateInstance(type, true); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/ILiteralString.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | namespace Xipton.Razor 8 | { 9 | public interface ILiteralString{} 10 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/IRazorMachine.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Xipton.Razor 4 | { 5 | public interface IRazorMachine 6 | { 7 | /// 8 | /// Executes the template by its virtual path and returns the resulting executed template instance. 9 | /// 10 | /// The requested virtual path for the template. 11 | /// The optional model. 12 | /// The optional viewbag. 13 | /// if set true then any layout setting is ignored 14 | /// 15 | /// Optional. If set to true an exception is thrown if the requested path could not be resolved. 16 | /// If set to false then null is returned if the requested path could not be resolved. 17 | /// 18 | /// An executed template instance. The corresponding rendered result can be found at 19 | ITemplate ExecuteUrl(string templateVirtualPath, object model = null, object viewbag = null, bool skipLayout = false, bool throwExceptionOnVirtualPathNotFound = true); 20 | 21 | /// 22 | /// Renders the specified template content's result with the passed model instance and returns the template's rendered result. 23 | /// A corresponding virtual path is generated internally for being able to keep the compiled template type cached. 24 | /// 25 | /// Content of the template. 26 | /// The model 27 | /// The optional viewbag 28 | /// if set to true then any layout setting (probably at _ViewStart) is ignored. 29 | /// 30 | /// The rendered string 31 | /// 32 | ITemplate ExecuteContent(string templateContent, object model = null, object viewbag = null, bool skipLayout = false); 33 | 34 | /// 35 | /// Hybrid convenience executer. It decides whether to execute content or an url. 36 | /// 37 | /// Content of the template URL or. 38 | /// The model. 39 | /// The viewbag. 40 | /// if set to true [skip layout]. 41 | /// if set to true [throw exception on virtual path not found]. 42 | /// 43 | ITemplate Execute(string templateVirtualPathOrContent, object model = null, object viewbag = null, bool skipLayout = false, bool throwExceptionOnVirtualPathNotFound = true); 44 | 45 | RazorContext Context { get; } 46 | RazorMachine RegisterTemplate(string virtualPath, string content); 47 | RazorMachine RemoveTemplate(string virtualPath); 48 | IDictionary GetRegisteredInMemoryTemplates(); 49 | RazorMachine ClearTypeCache(); 50 | } 51 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/ITemplate.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections.Generic; 10 | using Xipton.Razor.Core; 11 | 12 | namespace Xipton.Razor 13 | { 14 | /// 15 | /// All templates need to implement this interface. It is implementated by . 16 | /// 17 | /// 18 | /// for interchangeability reasons some operations are (about) the same as MVC view operations. 19 | /// 20 | public interface ITemplate { 21 | 22 | #region MVC complient 23 | string Layout { get; set; } 24 | dynamic Model { get; } 25 | dynamic ViewBag { get; } 26 | LiteralString RenderBody(); 27 | 28 | // you may skip the layout using the parameter skipLayoutwhich can be handy 29 | // when rendering template partials (controls) 30 | LiteralString RenderPage(string name, object model = null, bool skipLayout = false); 31 | 32 | LiteralString RenderSection(string sectionName, bool required = false); 33 | bool IsSectionDefined(string sectionName); 34 | #endregion 35 | 36 | // If set true then the Write method HTML encode the output. 37 | // The default setting can be configured. 38 | bool HtmlEncode { get; set; } 39 | 40 | // If generated source code must be included (needs to be configured) 41 | // then that source code can be accessed by this property 42 | // for debug purposes 43 | string GeneratedSourceCode { get; } 44 | 45 | // If this template is rendered by another template 46 | // (I am a layout or control) then that other template is 47 | // registered as the Parent and can be accessed during execution time 48 | ITemplate Parent { get; } 49 | 50 | // Any layout and all controls are registered as childs 51 | // The child list should not be accessed during 52 | // template execution, but only after complete execution. 53 | IList Childs { get; } 54 | 55 | // Returns the Root parent, or myself (this) if no Parent exists. 56 | ITemplate RootOrSelf { get; } 57 | 58 | // PathBuilder contains the virtual path as a starting point 59 | VirtualPathBuilder VirtualLocation { get; } 60 | 61 | // The actual virtual path that resolved this template 62 | string VirtualPath { get; } 63 | 64 | // Context gives access to the template factory and to the razor configuration 65 | RazorContext RazorContext { get; } 66 | 67 | // The rendered result 68 | String Result { get; } 69 | 70 | // Writes encoded output only if HtmlEncode is true 71 | void Write(object value); 72 | 73 | // Writes a literal string, never encoded 74 | void WriteLiteral(object value); 75 | 76 | // Ensures the value to be written as a raw string. 77 | // You can invoke it like: @Raw("a & b") 78 | LiteralString Raw(string value); 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/ITemplate`1.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | namespace Xipton.Razor 9 | { 10 | /// 11 | /// This interface redefines the Model property making it typed 12 | /// 13 | /// The type of the model. 14 | public interface ITemplate : ITemplate 15 | { 16 | new TModel Model { get; } 17 | } 18 | } -------------------------------------------------------------------------------- /Source/Xipton.Razor/LiteralString.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | 10 | namespace Xipton.Razor { 11 | /// 12 | /// a LiteralString represents a string that should not be encoded (again) 13 | /// 14 | public class LiteralString : ILiteralString { 15 | 16 | private readonly string 17 | _literalString; 18 | 19 | public LiteralString(string literalString){ 20 | _literalString = literalString; 21 | } 22 | 23 | public override bool Equals(object obj) { 24 | var other = obj as LiteralString; 25 | return Equals(_literalString, other != null ? other._literalString : obj); 26 | } 27 | 28 | public override int GetHashCode() { 29 | return _literalString != null ? _literalString.GetHashCode() : 0; 30 | } 31 | 32 | public static implicit operator String(LiteralString s){ 33 | return s == null ? null : s._literalString; 34 | } 35 | public static implicit operator LiteralString(string s) { 36 | return new LiteralString(s); 37 | } 38 | 39 | public static bool operator ==(LiteralString s1, string s){ 40 | return Equals(s1, null) ? Equals(s, null) : Equals(s1._literalString, s); 41 | } 42 | public static bool operator !=(LiteralString s1, string s){ 43 | return !(s1 == s); 44 | } 45 | 46 | public override string ToString() { 47 | // should never return null 48 | return _literalString ?? string.Empty; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | //[assembly:InternalsVisibleTo("Xipton.Razor.UnitTest")] 8 | [assembly: AssemblyTitle("Xipton.Razor")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Xipton")] 12 | [assembly: AssemblyProduct("Xipton.Razor")] 13 | [assembly: AssemblyCopyright("Copyright © 2013")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("3169c7ca-13f9-44a6-a5cd-08a83224af47")] 24 | 25 | [assembly: AssemblyVersion("3.1.0.0")] 26 | [assembly: AssemblyFileVersion("3.1.0.0")] 27 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/RazorContext.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using Xipton.Razor.Config; 10 | using Xipton.Razor.Core; 11 | using Xipton.Razor.Extension; 12 | 13 | namespace Xipton.Razor 14 | { 15 | /// 16 | /// The RazorContext holds the template factory (with its type cache) and the configurtation. 17 | /// 18 | public class RazorContext : IDisposable 19 | { 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | public RazorContext(RazorConfig config = null) 24 | { 25 | Config = (config ?? new RazorConfig().Initializer.TryInitializeFromConfig().CastTo()).AsReadonly(); 26 | TemplateFactory = new TemplateFactory(this); 27 | } 28 | 29 | public RazorConfig Config { get; private set; } 30 | /// 31 | /// Gets the template factory. The template factory instantiates the template instances by a virtual template name. 32 | /// Internally it handles caching of the generated template types. 33 | /// 34 | public TemplateFactory TemplateFactory { get; private set; } 35 | 36 | #region Implementation of IDisposable 37 | 38 | public void Dispose() 39 | { 40 | Dispose(true); 41 | } 42 | 43 | protected virtual void Dispose(bool disposing) 44 | { 45 | if (!disposing) return; 46 | var target = TemplateFactory; 47 | TemplateFactory = null; 48 | if (target != null) 49 | { 50 | target.Dispose(); 51 | } 52 | } 53 | 54 | #endregion 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/RazorMachine.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | using System; 9 | using System.Collections.Concurrent; 10 | using System.Collections.Generic; 11 | using System.Web.Razor; 12 | using Xipton.Razor.Config; 13 | using Xipton.Razor.Core; 14 | using Xipton.Razor.Core.ContentProvider; 15 | using Xipton.Razor.Extension; 16 | 17 | namespace Xipton.Razor 18 | { 19 | /// 20 | /// The main razor template executer. Use a singleton instance because each instance creates its own type cache (at the template factory). 21 | /// 22 | public class RazorMachine : IDisposable, IRazorMachine 23 | { 24 | 25 | protected internal const string ContentGeneratedUrlPrefix = "~/_MemoryContent/_{0}"; 26 | 27 | private readonly ConcurrentDictionary 28 | _templateContentToGeneratedVirtualPathMap = new ConcurrentDictionary(); 29 | 30 | private readonly object _syncroot = new object(); 31 | 32 | #region Constructors 33 | public RazorMachine(){ 34 | Context = new RazorContext(new RazorConfig().Initializer.TryInitializeFromConfig().AsReadOnly()); 35 | } 36 | public RazorMachine(RazorConfig config){ 37 | Context = new RazorContext(config ?? new RazorConfig().Initializer.TryInitializeFromConfig().AsReadOnly()); 38 | } 39 | public RazorMachine(string xmlContentOrFileName){ 40 | if (xmlContentOrFileName == null) 41 | // default initialization (from app.config or else default settings) 42 | Context = new RazorContext(new RazorConfig().Initializer.TryInitializeFromConfig().AsReadOnly()); 43 | else if (xmlContentOrFileName.IsFileName()) 44 | // initialization by xml configuration file 45 | Context = new RazorContext(new RazorConfig().Initializer.InitializeByXmlFileName(xmlContentOrFileName).AsReadOnly()); 46 | else if (xmlContentOrFileName.IsXmlContent()) 47 | // initialization by literal xml configuration string 48 | Context = new RazorContext(new RazorConfig().Initializer.InitializeByXmlContent(xmlContentOrFileName).AsReadOnly()); 49 | else 50 | throw new ArgumentException("Argument must be either an existing file name or literal xml content", "xmlContentOrFileName"); 51 | } 52 | 53 | public RazorMachine( 54 | Type baseType = null, 55 | string rootOperatorPath = null, 56 | RazorCodeLanguage language = null, 57 | string defaultExtension = null, 58 | string autoIncludeNameWithoutExtension = null, 59 | string sharedLocation = null, 60 | bool? includeGeneratedSourceCode = null, 61 | bool? htmlEncode = null, 62 | IEnumerable references = null, 63 | IEnumerable namespaces = null, 64 | IEnumerable> contentProviders = null, 65 | bool replaceReferences = false, 66 | bool replaceNamespaces = false, 67 | bool replaceContentProviders = false 68 | ) 69 | { 70 | Context = new RazorContext(new RazorConfig() 71 | .Initializer 72 | .TryInitializeFromConfig() 73 | .InitializeByValues( 74 | baseType, 75 | rootOperatorPath, 76 | language, 77 | defaultExtension, 78 | autoIncludeNameWithoutExtension, 79 | sharedLocation, 80 | includeGeneratedSourceCode, 81 | htmlEncode, 82 | references, 83 | namespaces, 84 | contentProviders, 85 | replaceReferences, 86 | replaceNamespaces, 87 | replaceContentProviders 88 | ) 89 | .AsReadOnly() 90 | ); 91 | } 92 | 93 | #endregion 94 | 95 | /// 96 | /// Executes the template by its virtual path and returns the resulting executed template instance. 97 | /// 98 | /// The requested virtual path for the template. 99 | /// The optional model. 100 | /// The optional viewbag. 101 | /// if set true then any layout setting is ignored 102 | /// 103 | /// Optional. If set to true an exception is thrown if the requested path could not be resolved. 104 | /// If set to false then null is returned if the requested path could not be resolved. 105 | /// 106 | /// An executed template instance. The corresponding rendered result can be found at 107 | public virtual ITemplate ExecuteUrl(string templateVirtualPath, object model = null, object viewbag = null, bool skipLayout = false, bool throwExceptionOnVirtualPathNotFound = true) 108 | { 109 | if (templateVirtualPath == null) throw new ArgumentNullException("templateVirtualPath"); 110 | 111 | var instance = Context 112 | .TemplateFactory 113 | .CreateTemplateInstance(templateVirtualPath, throwExceptionOnVirtualPathNotFound); 114 | 115 | if (instance == null) return null; 116 | 117 | var template = instance 118 | .CastTo() 119 | .SetModel(model) 120 | .SetViewBag(viewbag) 121 | .Execute(); 122 | 123 | if (!skipLayout) 124 | template.TryApplyLayout(); 125 | 126 | return template; 127 | } 128 | 129 | /// 130 | /// Renders the specified template content's result with the passed model instance and returns the template's rendered result. 131 | /// A corresponding virtual path is generated internally for being able to keep the compiled template type cached. 132 | /// 133 | /// Content of the template. 134 | /// The model 135 | /// The optional viewbag 136 | /// if set to true then any layout setting (probably at _ViewStart) is ignored. 137 | /// 138 | /// The rendered string 139 | /// 140 | public virtual ITemplate ExecuteContent(string templateContent, object model = null, object viewbag = null, bool skipLayout = false) { 141 | if (templateContent == null) return null; 142 | var generatedVirtualPath = _templateContentToGeneratedVirtualPathMap.GetOrAdd(templateContent, k => 143 | { 144 | var path = ContentGeneratedUrlPrefix.FormatWith(Guid.NewGuid().ToString("N")); 145 | RegisterTemplate(path, templateContent); 146 | return path; 147 | } 148 | ); 149 | return ExecuteUrl(generatedVirtualPath, model, viewbag, skipLayout); 150 | } 151 | 152 | /// 153 | /// Hybrid convenience executer. It decides whether to execute content or an url. 154 | /// 155 | /// Content of the template URL or. 156 | /// The model. 157 | /// The viewbag. 158 | /// if set to true [skip layout]. 159 | /// if set to true [throw exception on virtual path not found]. 160 | /// 161 | public virtual ITemplate Execute(string templateVirtualPathOrContent, object model = null, object viewbag = null, bool skipLayout = false, bool throwExceptionOnVirtualPathNotFound = true){ 162 | if (templateVirtualPathOrContent == null) throw new ArgumentNullException("templateVirtualPathOrContent"); 163 | return templateVirtualPathOrContent.IsUrl() 164 | ? ExecuteUrl(templateVirtualPathOrContent, model, viewbag, skipLayout, throwExceptionOnVirtualPathNotFound) 165 | : ExecuteContent(templateVirtualPathOrContent, model, viewbag, skipLayout); 166 | } 167 | 168 | public RazorContext Context { get; private set; } 169 | 170 | public RazorMachine RegisterTemplate(string virtualPath, string content) 171 | { 172 | MemoryContentProvider.RegisterTemplate(NormalizeVirtualPath(virtualPath), content); 173 | return this; 174 | } 175 | public RazorMachine RemoveTemplate(string virtualPath) { 176 | MemoryContentProvider.RegisterTemplate(NormalizeVirtualPath(virtualPath),null); 177 | return this; 178 | } 179 | public IDictionary GetRegisteredInMemoryTemplates() { 180 | return MemoryContentProvider.GetRegisteredTemplates(); 181 | } 182 | 183 | public RazorMachine ClearTypeCache(){ 184 | Context.TemplateFactory.ClearTypeCache(); 185 | return this; 186 | } 187 | 188 | protected MemoryContentProvider MemoryContentProvider { 189 | get 190 | { 191 | var provider = Context.TemplateFactory.ContentManager.TryGetContentProvider(); 192 | if (provider != null) return provider; 193 | lock (_syncroot) 194 | { 195 | provider = Context.TemplateFactory.ContentManager.TryGetContentProvider(); 196 | if (provider == null) 197 | Context.TemplateFactory.ContentManager.AddContentProvider(provider = new MemoryContentProvider()); 198 | return provider; 199 | } 200 | } 201 | } 202 | 203 | protected string NormalizeVirtualPath(string virtualPath) { 204 | if (virtualPath.NullOrEmpty()) throw new ArgumentNullException("virtualPath"); 205 | virtualPath = new VirtualPathBuilder(Context.Config.RootOperator.Path) 206 | .CombineWith(virtualPath) 207 | .WithRootOperator() 208 | .AddOrKeepExtension(Context.Config.Templates.DefaultExtension); 209 | return virtualPath; 210 | } 211 | 212 | 213 | #region Implementation of IDisposable 214 | 215 | public void Dispose() 216 | { 217 | Dispose(true); 218 | } 219 | 220 | protected virtual void Dispose(bool disposing) 221 | { 222 | if (!disposing) return; 223 | var context = Context; 224 | Context = null; 225 | if (context != null) 226 | context.Dispose(); 227 | } 228 | 229 | #endregion 230 | 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/TemplateBase`1.cs: -------------------------------------------------------------------------------- 1 | #region Microsoft Public License 2 | /* This code is part of Xipton.Razor v3.0 3 | * (c) Jaap Lamfers, 2013 - jaap.lamfers@xipton.net 4 | * Licensed under the Microsoft Public License (MS-PL) http://www.microsoft.com/en-us/openness/licenses.aspx#MPL 5 | */ 6 | #endregion 7 | 8 | namespace Xipton.Razor 9 | { 10 | /// 11 | /// Redefines the Model property, making it typed 12 | /// 13 | /// The type of the model. 14 | public abstract class TemplateBase : TemplateBase, ITemplate 15 | { 16 | /// 17 | /// Gets the model or the Parent's model (recursively) if the current model is null. 18 | /// If no model exists a new model is instantiated at RootOrSelf. 19 | /// 20 | public new TModel Model{ 21 | get{return GetOrCreateModel();} 22 | } 23 | 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Xipton.Razor.Overview.cd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 16 | 17 | 18 | 19 | Config\RazorConfig.cs 20 | 21 | 22 | 23 | 24 | Config\RazorConfig.cs 25 | 26 | 27 | 28 | 29 | Config\RazorConfig.cs 30 | 31 | 32 | 33 | 34 | AAAAAAEAQEgAAQAAiAEBAAAAAABQAAAAAQAAAAAiAAA= 35 | Config\RazorConfig.cs 36 | 37 | 38 | 39 | 40 | 41 | 57 | 58 | 59 | 60 | Core\TemplateFactory.cs 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | CAABAAAAACAAAAAAAAAAiEAAgAAAIAhQAAAAAAMIAIA= 74 | Core\TemplateFactory.cs 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | AAASQEAAEKAYGAAAQAAQSwACGAAAAAAAIgAIBgAAEIA= 112 | Core\ContentManager.cs 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 133 | 134 | BAAAQEAAECAACAAAAAAACQAAAAAAAgAAIAAKAAAAAIA= 135 | Core\ContentProvider\CompositeContentProvider.cs 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | BAAAAAAAAAAAAAAAAAAACAAAAAACAAAAAAAqAAABAAA= 146 | Core\ContentProvider\EmbeddedResourceContentProvider.cs 147 | 148 | 149 | 150 | 151 | 152 | 153 | BAAAAAAAAWAAAAAAAAAACAAAAQAIAAAAAAAKAAAAAEQ= 154 | Core\ContentProvider\FileContentProvider.cs 155 | 156 | 157 | 158 | 159 | 160 | 161 | 167 | 168 | BAAAAAAAAAAAAQABAAAACAAAAAAAAAAAAAAKAAAAAAA= 169 | Core\ContentProvider\MemoryContentProvider.cs 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | AAAAAAAAAiAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAA= 184 | RazorContext.cs 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 201 | 202 | BAAAAiAAAiAAAQQiAAAAAAAAAAAAAAAAAAAAAAEAAAA= 203 | RazorMachine.cs 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | EAAEEAKRBAIBCswEEgRgSAQAYASABEgJQIwAQAFBgAU= 214 | TemplateBase.cs 215 | 216 | 217 | 218 | 219 | 220 | 221 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAA= 222 | TemplateBase`1.cs 223 | 224 | 225 | 226 | 227 | 228 | 229 | BAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAKAAAAAAA= 230 | Core\IContentProvider.cs 231 | 232 | 233 | 234 | 235 | 236 | AAAAAgEACAIAEAQCAAAAAAAAAAAAAAAAAkAAEABAAAA= 237 | Core\ITemplateInternal.cs 238 | 239 | 240 | 241 | 242 | 243 | AAAAAAABBAABCoQAEAAAQAQAAASAAEAIAAgAQAEAAAU= 244 | ITemplate.cs 245 | 246 | 247 | 248 | 249 | 250 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAA= 251 | ITemplate`1.cs 252 | 253 | 254 | 255 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/Xipton.Razor.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {3A7A2F2F-0B57-47F9-8E1A-D34ECB961831} 9 | Library 10 | Properties 11 | Xipton.Razor 12 | Xipton.Razor 13 | v4.5 14 | 512 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | false 35 | 36 | 37 | false 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | False 49 | ..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | Code 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | Always 111 | Designer 112 | 113 | 114 | 115 | Designer 116 | 117 | 118 | 119 | 120 | 121 | 128 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/legacy/web.config: -------------------------------------------------------------------------------- 1 |  2 | 36 | 37 | 38 | 39 |
40 |
41 | 42 | 43 | 44 | 45 | 46 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /Source/Xipton.Razor/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --------------------------------------------------------------------------------