PageRenderTime 41ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Spark.Web.Mvc/SparkViewFactory.cs

https://github.com/briandonahue/spark
C# | 403 lines | 318 code | 68 blank | 17 comment | 25 complexity | 011c29596f9a26371b96c0fb1d1a3411 MD5 | raw file
  1. // Copyright 2008-2009 Louis DeJardin - http://whereslou.com
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Configuration;
  18. using System.IO;
  19. using System.Linq;
  20. using System.Reflection;
  21. using System.Threading;
  22. using System.Web.Mvc;
  23. using System.Web.Routing;
  24. using Spark.Compiler;
  25. using Spark.FileSystem;
  26. using Spark.Web.Mvc.Wrappers;
  27. namespace Spark.Web.Mvc
  28. {
  29. public class SparkViewFactory : IViewEngine, IViewFolderContainer, ISparkServiceInitialize
  30. {
  31. private ISparkViewEngine _engine;
  32. private IDescriptorBuilder _descriptorBuilder;
  33. private ICacheServiceProvider _cacheServiceProvider;
  34. public SparkViewFactory()
  35. : this(null)
  36. {
  37. }
  38. public SparkViewFactory(ISparkSettings settings)
  39. {
  40. Settings = settings ?? (ISparkSettings)ConfigurationManager.GetSection("spark") ?? new SparkSettings();
  41. }
  42. public virtual void Initialize(ISparkServiceContainer container)
  43. {
  44. Settings = container.GetService<ISparkSettings>();
  45. Engine = container.GetService<ISparkViewEngine>();
  46. DescriptorBuilder = container.GetService<IDescriptorBuilder>();
  47. CacheServiceProvider = container.GetService<ICacheServiceProvider>();
  48. }
  49. public ISparkSettings Settings { get; set; }
  50. public ISparkViewEngine Engine
  51. {
  52. get
  53. {
  54. if (_engine == null)
  55. SetEngine(new SparkViewEngine(Settings));
  56. return _engine;
  57. }
  58. set
  59. {
  60. SetEngine(value);
  61. }
  62. }
  63. public void SetEngine(ISparkViewEngine engine)
  64. {
  65. _descriptorBuilder = null;
  66. _engine = engine;
  67. if (_engine != null)
  68. {
  69. _engine.DefaultPageBaseType = typeof(SparkView).FullName;
  70. }
  71. }
  72. public IViewActivatorFactory ViewActivatorFactory
  73. {
  74. get { return Engine.ViewActivatorFactory; }
  75. set { Engine.ViewActivatorFactory = value; }
  76. }
  77. public IViewFolder ViewFolder
  78. {
  79. get { return Engine.ViewFolder; }
  80. set { Engine.ViewFolder = value; }
  81. }
  82. public IDescriptorBuilder DescriptorBuilder
  83. {
  84. get
  85. {
  86. return _descriptorBuilder ??
  87. Interlocked.CompareExchange(ref _descriptorBuilder, new DefaultDescriptorBuilder(Engine), null) ??
  88. _descriptorBuilder;
  89. }
  90. set { _descriptorBuilder = value; }
  91. }
  92. public ICacheServiceProvider CacheServiceProvider
  93. {
  94. get
  95. {
  96. return _cacheServiceProvider ??
  97. Interlocked.CompareExchange(ref _cacheServiceProvider, new DefaultCacheServiceProvider(), null) ??
  98. _cacheServiceProvider;
  99. }
  100. set { _cacheServiceProvider = value; }
  101. }
  102. public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName)
  103. {
  104. return FindViewInternal(controllerContext, viewName, masterName, true, false);
  105. }
  106. public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
  107. {
  108. return FindViewInternal(controllerContext, viewName, masterName, true, useCache);
  109. }
  110. public virtual ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName)
  111. {
  112. return FindViewInternal(controllerContext, partialViewName, null /*masterName*/, false, false);
  113. }
  114. public virtual ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
  115. {
  116. return FindViewInternal(controllerContext, partialViewName, null /*masterName*/, false, useCache);
  117. }
  118. public virtual void ReleaseView(ControllerContext controllerContext, IView view)
  119. {
  120. Engine.ReleaseInstance((ISparkView)view);
  121. }
  122. private readonly Dictionary<BuildDescriptorParams, ISparkViewEntry> _cache =
  123. new Dictionary<BuildDescriptorParams, ISparkViewEntry>();
  124. private readonly ViewEngineResult _cacheMissResult = new ViewEngineResult(new string[0]);
  125. private ViewEngineResult FindViewInternal(ControllerContext controllerContext, string viewName, string masterName, bool findDefaultMaster, bool useCache)
  126. {
  127. var searchedLocations = new List<string>();
  128. var targetNamespace = controllerContext.Controller.GetType().Namespace;
  129. var controllerName = controllerContext.RouteData.GetRequiredString("controller");
  130. var descriptorParams = new BuildDescriptorParams(
  131. targetNamespace,
  132. controllerName,
  133. viewName,
  134. masterName,
  135. findDefaultMaster,
  136. DescriptorBuilder.GetExtraParameters(controllerContext));
  137. ISparkViewEntry entry;
  138. if (useCache)
  139. {
  140. if (TryGetCacheValue(descriptorParams, out entry) && entry.IsCurrent())
  141. {
  142. return BuildResult(controllerContext.RequestContext, entry);
  143. }
  144. return _cacheMissResult;
  145. }
  146. var descriptor = DescriptorBuilder.BuildDescriptor(
  147. descriptorParams,
  148. searchedLocations);
  149. if (descriptor == null)
  150. return new ViewEngineResult(searchedLocations);
  151. entry = Engine.CreateEntry(descriptor);
  152. SetCacheValue(descriptorParams, entry);
  153. return BuildResult(controllerContext.RequestContext, entry);
  154. }
  155. private bool TryGetCacheValue(BuildDescriptorParams descriptorParams, out ISparkViewEntry entry)
  156. {
  157. lock (_cache) return _cache.TryGetValue(descriptorParams, out entry);
  158. }
  159. private void SetCacheValue(BuildDescriptorParams descriptorParams, ISparkViewEntry entry)
  160. {
  161. lock (_cache) _cache[descriptorParams] = entry;
  162. }
  163. private ViewEngineResult BuildResult(RequestContext requestContext, ISparkViewEntry entry)
  164. {
  165. var view = (IView)entry.CreateInstance();
  166. if (view is SparkView)
  167. {
  168. var sparkView = (SparkView)view;
  169. sparkView.ResourcePathManager = Engine.ResourcePathManager;
  170. sparkView.CacheService = CacheServiceProvider.GetCacheService(requestContext);
  171. }
  172. return new ViewEngineResult(view, this);
  173. }
  174. public SparkViewDescriptor CreateDescriptor(
  175. ControllerContext controllerContext,
  176. string viewName,
  177. string masterName,
  178. bool findDefaultMaster,
  179. ICollection<string> searchedLocations)
  180. {
  181. var targetNamespace = controllerContext.Controller.GetType().Namespace;
  182. var controllerName = controllerContext.RouteData.GetRequiredString("controller");
  183. return DescriptorBuilder.BuildDescriptor(
  184. new BuildDescriptorParams(
  185. targetNamespace,
  186. controllerName,
  187. viewName,
  188. masterName,
  189. findDefaultMaster,
  190. DescriptorBuilder.GetExtraParameters(controllerContext)),
  191. searchedLocations);
  192. }
  193. public SparkViewDescriptor CreateDescriptor(string targetNamespace, string controllerName, string viewName,
  194. string masterName, bool findDefaultMaster)
  195. {
  196. var searchedLocations = new List<string>();
  197. var descriptor = DescriptorBuilder.BuildDescriptor(
  198. new BuildDescriptorParams(
  199. targetNamespace /*areaName*/,
  200. controllerName,
  201. viewName,
  202. masterName,
  203. findDefaultMaster, null),
  204. searchedLocations);
  205. if (descriptor == null)
  206. {
  207. throw new CompilerException("Unable to find templates at " +
  208. string.Join(", ", searchedLocations.ToArray()));
  209. }
  210. return descriptor;
  211. }
  212. public Assembly Precompile(SparkBatchDescriptor batch)
  213. {
  214. return Engine.BatchCompilation(batch.OutputAssembly, CreateDescriptors(batch));
  215. }
  216. public List<SparkViewDescriptor> CreateDescriptors(SparkBatchDescriptor batch)
  217. {
  218. var descriptors = new List<SparkViewDescriptor>();
  219. foreach (var entry in batch.Entries)
  220. descriptors.AddRange(CreateDescriptors(entry));
  221. return descriptors;
  222. }
  223. public IList<SparkViewDescriptor> CreateDescriptors(SparkBatchEntry entry)
  224. {
  225. var descriptors = new List<SparkViewDescriptor>();
  226. var controllerName = RemoveSuffix(entry.ControllerType.Name, "Controller");
  227. var viewNames = new List<string>();
  228. var includeViews = entry.IncludeViews;
  229. if (includeViews.Count == 0)
  230. includeViews = new[] { "*" };
  231. foreach (var include in includeViews)
  232. {
  233. if (include.EndsWith("*"))
  234. {
  235. foreach (var fileName in ViewFolder.ListViews(controllerName))
  236. {
  237. if (!string.Equals(Path.GetExtension(fileName), ".spark", StringComparison.InvariantCultureIgnoreCase))
  238. continue;
  239. var potentialMatch = Path.GetFileNameWithoutExtension(fileName);
  240. if (!TestMatch(potentialMatch, include))
  241. continue;
  242. var isExcluded = false;
  243. foreach (var exclude in entry.ExcludeViews)
  244. {
  245. if (!TestMatch(potentialMatch, RemoveSuffix(exclude, ".spark")))
  246. continue;
  247. isExcluded = true;
  248. break;
  249. }
  250. if (!isExcluded)
  251. viewNames.Add(potentialMatch);
  252. }
  253. }
  254. else
  255. {
  256. // explicitly included views don't test for exclusion
  257. viewNames.Add(RemoveSuffix(include, ".spark"));
  258. }
  259. }
  260. foreach (var viewName in viewNames)
  261. {
  262. if (entry.LayoutNames.Count == 0)
  263. {
  264. descriptors.Add(CreateDescriptor(
  265. entry.ControllerType.Namespace,
  266. controllerName,
  267. viewName,
  268. null /*masterName*/,
  269. true));
  270. }
  271. else
  272. {
  273. foreach (var masterName in entry.LayoutNames)
  274. {
  275. descriptors.Add(CreateDescriptor(
  276. entry.ControllerType.Namespace,
  277. controllerName,
  278. viewName,
  279. string.Join(" ", masterName.ToArray()),
  280. false));
  281. }
  282. }
  283. }
  284. return descriptors;
  285. }
  286. private static bool TestMatch(string potentialMatch, string pattern)
  287. {
  288. if (!pattern.EndsWith("*"))
  289. {
  290. return string.Equals(potentialMatch, pattern, StringComparison.InvariantCultureIgnoreCase);
  291. }
  292. // raw wildcard matches anything that's not a partial
  293. if (pattern == "*")
  294. {
  295. return !potentialMatch.StartsWith("_");
  296. }
  297. // otherwise the only thing that's supported is "starts with"
  298. return potentialMatch.StartsWith(pattern.Substring(0, pattern.Length - 1),
  299. StringComparison.InvariantCultureIgnoreCase);
  300. }
  301. private static string RemoveSuffix(string value, string suffix)
  302. {
  303. if (value.EndsWith(suffix, StringComparison.InvariantCultureIgnoreCase))
  304. return value.Substring(0, value.Length - suffix.Length);
  305. return value;
  306. }
  307. #region IViewEngine Members
  308. ViewEngineResult IViewEngine.FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
  309. {
  310. return FindPartialView(controllerContext, partialViewName, useCache);
  311. }
  312. ViewEngineResult IViewEngine.FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
  313. {
  314. return FindView(controllerContext, viewName, masterName, useCache);
  315. }
  316. void IViewEngine.ReleaseView(ControllerContext controllerContext, IView view)
  317. {
  318. ReleaseView(controllerContext, view);
  319. }
  320. #endregion
  321. #region ISparkServiceInitialize Members
  322. void ISparkServiceInitialize.Initialize(ISparkServiceContainer container)
  323. {
  324. Initialize(container);
  325. }
  326. #endregion
  327. #region IViewFolderContainer Members
  328. IViewFolder IViewFolderContainer.ViewFolder
  329. {
  330. get { return ViewFolder; }
  331. set { ViewFolder = value; }
  332. }
  333. #endregion
  334. }
  335. }