PageRenderTime 56ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/SharpTAL/TemplateCache/FileSystemTemplateCache.cs

https://bitbucket.org/rlacko/sharptal
C# | 212 lines | 116 code | 29 blank | 67 comment | 9 complexity | b9f8462927ecc9584d3289d8c4b275ea MD5 | raw file
Possible License(s): Apache-2.0
  1. //
  2. // FileSystemTemplateCache.cs
  3. //
  4. // Author:
  5. // Roman Lacko (backup.rlacko@gmail.com)
  6. //
  7. // Copyright (c) 2010 - 2013 Roman Lacko
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. namespace SharpTAL.TemplateCache
  29. {
  30. using System;
  31. using System.Collections.Generic;
  32. using System.IO;
  33. using System.Reflection;
  34. using System.Text.RegularExpressions;
  35. using SharpTAL.TemplateProgram;
  36. public class FileSystemTemplateCache : AbstractTemplateCache
  37. {
  38. const string DEFAULT_FILENAME_PATTER = @"Template_{key}.dll";
  39. const string SHA1_KEY_PATTERN = @"[a-zA-Z0-9]{38}";
  40. Dictionary<string, TemplateInfo> templateInfoCache;
  41. object templateInfoCacheLock;
  42. string templateCacheFolder;
  43. string fileNamePattern;
  44. Regex fileNamePatternRegex;
  45. /// <summary>
  46. /// Initialize the filesystem template cache
  47. /// </summary>
  48. /// <param name="cacheFolder">Folder where cache will write and read generated assemblies</param>
  49. public FileSystemTemplateCache(string cacheFolder)
  50. : this(cacheFolder, false, null)
  51. {
  52. }
  53. /// <summary>
  54. /// Initialize the filesystem template cache
  55. /// </summary>
  56. /// <param name="cacheFolder">Folder where cache will write and read generated assemblies</param>
  57. /// <param name="clearCache">If it's true, clear all files matching the <paramref name="pattern"/> from cache folder</param>
  58. public FileSystemTemplateCache(string cacheFolder, bool clearCache)
  59. : this(cacheFolder, clearCache, null)
  60. {
  61. }
  62. /// <summary>
  63. /// Initialize the filesystem template cache
  64. /// </summary>
  65. /// <param name="cacheFolder">Folder where cache will write and read generated assemblies</param>
  66. /// <param name="clearCache">If it's true, clear all files matching the <paramref name="pattern"/> from cache folder</param>
  67. /// <param name="pattern">
  68. /// File name regular expression pattern.
  69. /// Default pattern is "Template_{key}.dll".
  70. /// Macro {key} will be replaced with computed hash key."
  71. /// </param>
  72. public FileSystemTemplateCache(string cacheFolder, bool clearCache, string pattern) :
  73. base()
  74. {
  75. this.templateInfoCacheLock = new object();
  76. this.templateCacheFolder = cacheFolder;
  77. this.templateInfoCache = null;
  78. this.fileNamePattern = string.IsNullOrEmpty(pattern) ? DEFAULT_FILENAME_PATTER : pattern;
  79. InitCache(clearCache);
  80. }
  81. public override TemplateInfo CompileTemplate(string templateBody, Dictionary<string, Type> globalsTypes, List<Assembly> referencedAssemblies)
  82. {
  83. lock (templateInfoCacheLock)
  84. {
  85. // Cache is empty, load templates from cache folder
  86. if (templateInfoCache == null)
  87. {
  88. templateInfoCache = LoadTemplatesInfo(templateCacheFolder);
  89. }
  90. // Generate template program
  91. TemplateInfo ti = GenerateTemplateProgram(templateBody, globalsTypes, referencedAssemblies);
  92. // Generated template found in cache
  93. if (templateInfoCache.ContainsKey(ti.TemplateKey))
  94. {
  95. return templateInfoCache[ti.TemplateKey];
  96. }
  97. // Generate code
  98. ICodeGenerator codeGenerator = new CodeGenerator();
  99. ti.GeneratedSourceCode = codeGenerator.GenerateCode(ti);
  100. // Path to output assembly
  101. string assemblyFileName = fileNamePattern.Replace("{key}", ti.TemplateKey);
  102. string assemblyPath = Path.Combine(templateCacheFolder, assemblyFileName);
  103. // Generate assembly
  104. AssemblyGenerator assemblyCompiler = new AssemblyGenerator();
  105. Assembly assembly = assemblyCompiler.GenerateAssembly(ti, false, assemblyPath, null);
  106. // Try to load the Render() method from assembly
  107. ti.RenderMethod = GetTemplateRenderMethod(assembly, ti);
  108. templateInfoCache.Add(ti.TemplateKey, ti);
  109. return ti;
  110. }
  111. }
  112. void InitCache(bool clearCache)
  113. {
  114. // Check cache folder
  115. if (!Directory.Exists(templateCacheFolder))
  116. {
  117. throw new ArgumentException(string.Format("Template cache folder does not exists: [{0}]", templateCacheFolder));
  118. }
  119. // Setup pattern
  120. // If input pattern is "Template_{key}.dll"
  121. // then result pattern is "(^Template_)(?<key>[a-zA-Z0-9]{38})(\.dll$)"
  122. string[] patternGroups = fileNamePattern.Split(new string[] { "{key}" }, StringSplitOptions.None);
  123. // Check if pattern contains exactly one "{key}" macro
  124. if (patternGroups.Length != 2)
  125. {
  126. throw new ArgumentException(
  127. string.Format(@"Invalid pattern specified. Macro ""{key}"" is missing or specified more than once: [{0}]",
  128. fileNamePattern));
  129. }
  130. // Normalize pattern
  131. string rePattern = string.Format(@"(^{0})(?<key>{1})({2}$)",
  132. patternGroups[0].Replace(".", @"\."),
  133. SHA1_KEY_PATTERN,
  134. patternGroups[1].Replace(".", @"\."));
  135. fileNamePatternRegex = new Regex(rePattern);
  136. // Delete all files from cache folder matching the pattern
  137. if (clearCache)
  138. {
  139. DirectoryInfo di = new DirectoryInfo(templateCacheFolder);
  140. foreach (FileInfo fi in di.GetFiles())
  141. {
  142. if (fileNamePatternRegex.Match(fi.Name).Success)
  143. {
  144. File.Delete(fi.FullName);
  145. }
  146. }
  147. }
  148. }
  149. Dictionary<string, TemplateInfo> LoadTemplatesInfo(string cacheFolder)
  150. {
  151. Dictionary<string, TemplateInfo> templateCache = new Dictionary<string, TemplateInfo>();
  152. // Load assemblies containing type with full name [<TEMPLATE_NAMESPACE>.<TEMPLATE_TYPENAME>],
  153. // with one public method [public static string Render(Dictionary<string, object> globals)]
  154. DirectoryInfo di = new DirectoryInfo(cacheFolder);
  155. foreach (FileInfo fi in di.GetFiles())
  156. {
  157. Match fileNameMatch = fileNamePatternRegex.Match(fi.Name);
  158. if (fileNameMatch.Success)
  159. {
  160. // Try to load file as assembly
  161. try
  162. {
  163. AssemblyName.GetAssemblyName(fi.FullName);
  164. }
  165. catch (System.BadImageFormatException)
  166. {
  167. // The file is not an Assembly
  168. continue;
  169. }
  170. // Get template hash from file name
  171. string templateHash = fileNameMatch.Groups["key"].Value;
  172. // Create template info
  173. TemplateInfo ti = new TemplateInfo() { TemplateKey = templateHash };
  174. // Try to load the Render() method from assembly
  175. Assembly assembly = Utils.ReadAssembly(fi.FullName);
  176. ti.RenderMethod = GetTemplateRenderMethod(assembly, ti);
  177. templateCache.Add(templateHash, ti);
  178. }
  179. }
  180. return templateCache;
  181. }
  182. }
  183. }