PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/src/RazorEngine/Compilation/CompilerServiceBase.cs

http://github.com/ServiceStack/ServiceStack
C# | 217 lines | 137 code | 35 blank | 45 comment | 22 complexity | 6b457b5ef2ae15bd54eee6659dd094bd MD5 | raw file
Possible License(s): BSD-3-Clause
  1. using System.Web.Mvc.Razor;
  2. using System.Web.Razor.Parser.SyntaxTree;
  3. using RazorEngine.Compilation.CSharp;
  4. namespace RazorEngine.Compilation
  5. {
  6. using System;
  7. using System.CodeDom;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using System.Linq;
  11. using System.Reflection;
  12. using System.Web.Razor;
  13. using System.Web.Razor.Generator;
  14. using System.Web.Razor.Parser;
  15. using Templating;
  16. /// <summary>
  17. /// Provides a base implementation of a compiler service.
  18. /// </summary>
  19. public abstract class CompilerServiceBase : ICompilerService
  20. {
  21. #region Constructor
  22. /// <summary>
  23. /// Initialises a new instance of <see cref="CompilerServiceBase"/>
  24. /// </summary>
  25. /// <param name="codeLanguage">The code language.</param>
  26. /// <param name="markupParser">The markup parser.</param>
  27. protected CompilerServiceBase(RazorCodeLanguage codeLanguage, MarkupParser markupParser)
  28. {
  29. if (codeLanguage == null)
  30. throw new ArgumentNullException("codeLanguage");
  31. CodeLanguage = codeLanguage;
  32. MarkupParser = markupParser ?? new HtmlMarkupParser();
  33. }
  34. #endregion
  35. #region Properties
  36. /// <summary>
  37. /// Gets the code language.
  38. /// </summary>
  39. public RazorCodeLanguage CodeLanguage { get; private set; }
  40. /// <summary>
  41. /// Gets the markup parser.
  42. /// </summary>
  43. public MarkupParser MarkupParser { get; private set; }
  44. #endregion
  45. #region Methods
  46. /// <summary>
  47. /// Builds a type name for the specified template type and model type.
  48. /// </summary>
  49. /// <param name="templateType">The template type.</param>
  50. /// <param name="modelType">The model type.</param>
  51. /// <returns>The string type name (including namespace).</returns>
  52. public virtual string BuildTypeName(Type templateType, Type modelType)
  53. {
  54. if (templateType == null)
  55. throw new ArgumentNullException("templateType");
  56. if (!templateType.IsGenericTypeDefinition && !templateType.IsGenericType)
  57. return templateType.FullName;
  58. if (modelType == null)
  59. throw new ArgumentException("The template type is a generic defintion, and no model type has been supplied.");
  60. bool @dynamic = CompilerServices.IsDynamicType(modelType);
  61. Type genericType = templateType.MakeGenericType(modelType);
  62. return BuildTypeNameInternal(genericType, @dynamic);
  63. }
  64. /// <summary>
  65. /// Builds a type name for the specified generic type.
  66. /// </summary>
  67. /// <param name="type">The type.</param>
  68. /// <param name="isDynamic">Is the model type dynamic?</param>
  69. /// <returns>The string typename (including namespace and generic type parameters).</returns>
  70. public abstract string BuildTypeNameInternal(Type type, bool isDynamic);
  71. /// <summary>
  72. /// Compiles the type defined in the specified type context.
  73. /// </summary>
  74. /// <param name="context">The type context which defines the type to compile.</param>
  75. /// <returns>The compiled type.</returns>
  76. public abstract Type CompileType(TypeContext context);
  77. /// <summary>
  78. /// Generates any required contructors for the specified type.
  79. /// </summary>
  80. /// <param name="constructors">The set of constructors.</param>
  81. /// <param name="codeType">The code type declaration.</param>
  82. private static void GenerateConstructors(IEnumerable<ConstructorInfo> constructors, CodeTypeDeclaration codeType)
  83. {
  84. if (constructors == null || !constructors.Any())
  85. return;
  86. var existingConstructors = codeType.Members.OfType<CodeConstructor>().ToArray();
  87. foreach (var existingConstructor in existingConstructors)
  88. codeType.Members.Remove(existingConstructor);
  89. foreach (var constructor in constructors)
  90. {
  91. var ctor = new CodeConstructor();
  92. ctor.Attributes = MemberAttributes.Public;
  93. foreach (var param in constructor.GetParameters())
  94. {
  95. ctor.Parameters.Add(new CodeParameterDeclarationExpression(param.ParameterType, param.Name));
  96. ctor.BaseConstructorArgs.Add(new CodeSnippetExpression(param.Name));
  97. }
  98. codeType.Members.Add(ctor);
  99. }
  100. }
  101. /// <summary>
  102. /// Gets the code compile unit used to compile a type.
  103. /// </summary>
  104. /// <param name="className">The class name.</param>
  105. /// <param name="template">The template to compile.</param>
  106. /// <param name="namespaceImports">The set of namespace imports.</param>
  107. /// <param name="templateType">The template type.</param>
  108. /// <param name="modelType">The model type.</param>
  109. /// <returns>A <see cref="CodeCompileUnit"/> used to compile a type.</returns>
  110. public CodeCompileUnit GetCodeCompileUnit(string className, string template, ISet<string> namespaceImports, Type templateType, Type modelType)
  111. {
  112. if (string.IsNullOrEmpty(className))
  113. throw new ArgumentException("Class name is required.");
  114. if (string.IsNullOrEmpty(template))
  115. throw new ArgumentException("Template is required.");
  116. templateType = templateType
  117. ?? ((modelType == null)
  118. ? typeof(TemplateBase)
  119. : typeof(TemplateBase<>));
  120. var host = new MvcWebPageRazorHost(CodeLanguage, () => MarkupParser)
  121. {
  122. DefaultBaseClass = BuildTypeName(templateType, modelType),
  123. DefaultClassName = className,
  124. DefaultNamespace = "CompiledRazorTemplates.Dynamic",
  125. GeneratedClassContext = new GeneratedClassContext("Execute", "Write", "WriteLiteral",
  126. "WriteTo", "WriteLiteralTo",
  127. "RazorEngine.Templating.TemplateWriter",
  128. "WriteSection")
  129. };
  130. host.NamespaceImports.Add("ServiceStack.Markdown.Html");
  131. var templateNamespaces = templateType.GetCustomAttributes(typeof (RequireNamespacesAttribute), true)
  132. .Cast<RequireNamespacesAttribute>()
  133. .SelectMany(att => att.Namespaces);
  134. foreach (string ns in templateNamespaces)
  135. namespaceImports.Add(ns);
  136. foreach (string @namespace in namespaceImports)
  137. host.NamespaceImports.Add(@namespace);
  138. var engine = new RazorTemplateEngine(host);
  139. GeneratorResults result;
  140. using (var reader = new StringReader(template))
  141. {
  142. result = engine.GenerateCode(reader);
  143. }
  144. var type = result.GeneratedCode.Namespaces[0].Types[0];
  145. if (modelType != null)
  146. {
  147. if (CompilerServices.IsAnonymousType(modelType))
  148. {
  149. type.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(HasDynamicModelAttribute))));
  150. }
  151. }
  152. GenerateConstructors(CompilerServices.GetConstructors(templateType), type);
  153. var statement = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "Clear");
  154. foreach (CodeTypeMember member in type.Members)
  155. {
  156. if (member.Name.Equals("Execute"))
  157. {
  158. ((CodeMemberMethod)member).Statements.Insert(0, new CodeExpressionStatement(statement));
  159. break;
  160. }
  161. }
  162. return result.GeneratedCode;
  163. }
  164. #endregion
  165. public IEnumerable<T> AllNodesOfType<T>(Block block)
  166. {
  167. if (block is T)
  168. yield return (T)(object)block;
  169. foreach (var syntaxTreeNode in block.Children)
  170. {
  171. if (syntaxTreeNode is T)
  172. yield return (T)(object)syntaxTreeNode;
  173. var childBlock = syntaxTreeNode as Block;
  174. if (childBlock == null) continue;
  175. foreach (var variable in AllNodesOfType<T>(childBlock))
  176. {
  177. yield return variable;
  178. }
  179. }
  180. }
  181. }
  182. }