PageRenderTime 66ms CodeModel.GetById 48ms app.highlight 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// 
 15using System;
 16using System.Collections.Generic;
 17using System.Configuration;
 18using System.IO;
 19using System.Linq;
 20using System.Reflection;
 21using System.Threading;
 22using System.Web.Mvc;
 23using System.Web.Routing;
 24using Spark.Compiler;
 25using Spark.FileSystem;
 26using Spark.Web.Mvc.Wrappers;
 27
 28namespace Spark.Web.Mvc
 29{
 30    public class SparkViewFactory : IViewEngine, IViewFolderContainer, ISparkServiceInitialize
 31    {
 32        private ISparkViewEngine _engine;
 33        private IDescriptorBuilder _descriptorBuilder;
 34        private ICacheServiceProvider _cacheServiceProvider;
 35
 36
 37        public SparkViewFactory()
 38            : this(null)
 39        {
 40        }
 41
 42        public SparkViewFactory(ISparkSettings settings)
 43        {
 44            Settings = settings ?? (ISparkSettings)ConfigurationManager.GetSection("spark") ?? new SparkSettings();
 45        }
 46
 47
 48        public virtual void Initialize(ISparkServiceContainer container)
 49        {
 50            Settings = container.GetService<ISparkSettings>();
 51            Engine = container.GetService<ISparkViewEngine>();
 52            DescriptorBuilder = container.GetService<IDescriptorBuilder>();
 53            CacheServiceProvider = container.GetService<ICacheServiceProvider>();
 54        }
 55
 56        public ISparkSettings Settings { get; set; }
 57
 58        public ISparkViewEngine Engine
 59        {
 60            get
 61            {
 62                if (_engine == null)
 63                    SetEngine(new SparkViewEngine(Settings));
 64
 65                return _engine;
 66            }
 67            set
 68            {
 69                SetEngine(value);
 70            }
 71        }
 72
 73        public void SetEngine(ISparkViewEngine engine)
 74        {
 75            _descriptorBuilder = null;
 76            _engine = engine;
 77            if (_engine != null)
 78            {
 79                _engine.DefaultPageBaseType = typeof(SparkView).FullName;
 80            }
 81        }
 82
 83        public IViewActivatorFactory ViewActivatorFactory
 84        {
 85            get { return Engine.ViewActivatorFactory; }
 86            set { Engine.ViewActivatorFactory = value; }
 87        }
 88
 89        public IViewFolder ViewFolder
 90        {
 91            get { return Engine.ViewFolder; }
 92            set { Engine.ViewFolder = value; }
 93        }
 94
 95        public IDescriptorBuilder DescriptorBuilder
 96        {
 97            get
 98            {
 99                return _descriptorBuilder ??
100                       Interlocked.CompareExchange(ref _descriptorBuilder, new DefaultDescriptorBuilder(Engine), null) ??
101                       _descriptorBuilder;
102            }
103            set { _descriptorBuilder = value; }
104        }
105
106        public ICacheServiceProvider CacheServiceProvider
107        {
108            get
109            {
110                return _cacheServiceProvider ??
111                       Interlocked.CompareExchange(ref _cacheServiceProvider, new DefaultCacheServiceProvider(), null) ??
112                       _cacheServiceProvider;
113            }
114            set { _cacheServiceProvider = value; }
115        }
116
117        public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName)
118        {
119            return FindViewInternal(controllerContext, viewName, masterName, true, false);
120        }
121
122        public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
123        {
124            return FindViewInternal(controllerContext, viewName, masterName, true, useCache);
125        }
126
127        public virtual ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName)
128        {
129            return FindViewInternal(controllerContext, partialViewName, null /*masterName*/, false, false);
130        }
131
132        public virtual ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
133        {
134            return FindViewInternal(controllerContext, partialViewName, null /*masterName*/, false, useCache);
135        }
136
137        public virtual void ReleaseView(ControllerContext controllerContext, IView view)
138        {
139            Engine.ReleaseInstance((ISparkView)view);
140        }
141
142        private readonly Dictionary<BuildDescriptorParams, ISparkViewEntry> _cache =
143            new Dictionary<BuildDescriptorParams, ISparkViewEntry>();
144
145        private readonly ViewEngineResult _cacheMissResult = new ViewEngineResult(new string[0]);
146
147        private ViewEngineResult FindViewInternal(ControllerContext controllerContext, string viewName, string masterName, bool findDefaultMaster, bool useCache)
148        {
149            var searchedLocations = new List<string>();
150            var targetNamespace = controllerContext.Controller.GetType().Namespace;
151
152            var controllerName = controllerContext.RouteData.GetRequiredString("controller");
153
154            var descriptorParams = new BuildDescriptorParams(
155                targetNamespace,
156                controllerName,
157                viewName,
158                masterName,
159                findDefaultMaster,
160                DescriptorBuilder.GetExtraParameters(controllerContext));
161
162            ISparkViewEntry entry;
163            if (useCache)
164            {
165                if (TryGetCacheValue(descriptorParams, out entry) && entry.IsCurrent())
166                {
167                    return BuildResult(controllerContext.RequestContext, entry);
168                }
169                return _cacheMissResult;
170            }
171
172            var descriptor = DescriptorBuilder.BuildDescriptor(
173                descriptorParams,
174                searchedLocations);
175
176            if (descriptor == null)
177                return new ViewEngineResult(searchedLocations);
178
179            entry = Engine.CreateEntry(descriptor);
180            SetCacheValue(descriptorParams, entry);
181            return BuildResult(controllerContext.RequestContext, entry);
182        }
183
184        private bool TryGetCacheValue(BuildDescriptorParams descriptorParams, out ISparkViewEntry entry)
185        {
186            lock (_cache) return _cache.TryGetValue(descriptorParams, out entry);
187        }
188
189        private void SetCacheValue(BuildDescriptorParams descriptorParams, ISparkViewEntry entry)
190        {
191            lock (_cache) _cache[descriptorParams] = entry;
192        }
193
194
195        private ViewEngineResult BuildResult(RequestContext requestContext, ISparkViewEntry entry)
196        {
197            var view = (IView)entry.CreateInstance();
198            if (view is SparkView)
199            {
200                var sparkView = (SparkView)view;
201                sparkView.ResourcePathManager = Engine.ResourcePathManager;
202                sparkView.CacheService = CacheServiceProvider.GetCacheService(requestContext);
203            }
204            return new ViewEngineResult(view, this);
205        }
206
207        public SparkViewDescriptor CreateDescriptor(
208            ControllerContext controllerContext,
209            string viewName,
210            string masterName,
211            bool findDefaultMaster,
212            ICollection<string> searchedLocations)
213        {
214            var targetNamespace = controllerContext.Controller.GetType().Namespace;
215
216            var controllerName = controllerContext.RouteData.GetRequiredString("controller");
217
218            return DescriptorBuilder.BuildDescriptor(
219                new BuildDescriptorParams(
220                    targetNamespace,
221                    controllerName,
222                    viewName,
223                    masterName,
224                    findDefaultMaster,
225                    DescriptorBuilder.GetExtraParameters(controllerContext)),
226                searchedLocations);
227        }
228
229        public SparkViewDescriptor CreateDescriptor(string targetNamespace, string controllerName, string viewName,
230                                                    string masterName, bool findDefaultMaster)
231        {
232            var searchedLocations = new List<string>();
233            var descriptor = DescriptorBuilder.BuildDescriptor(
234                new BuildDescriptorParams(
235                    targetNamespace /*areaName*/,
236                    controllerName,
237                    viewName,
238                    masterName,
239                    findDefaultMaster, null),
240                searchedLocations);
241
242            if (descriptor == null)
243            {
244                throw new CompilerException("Unable to find templates at " +
245                                            string.Join(", ", searchedLocations.ToArray()));
246            }
247            return descriptor;
248        }
249
250
251        public Assembly Precompile(SparkBatchDescriptor batch)
252        {
253            return Engine.BatchCompilation(batch.OutputAssembly, CreateDescriptors(batch));
254        }
255
256        public List<SparkViewDescriptor> CreateDescriptors(SparkBatchDescriptor batch)
257        {
258            var descriptors = new List<SparkViewDescriptor>();
259            foreach (var entry in batch.Entries)
260                descriptors.AddRange(CreateDescriptors(entry));
261            return descriptors;
262        }
263
264        public IList<SparkViewDescriptor> CreateDescriptors(SparkBatchEntry entry)
265        {
266            var descriptors = new List<SparkViewDescriptor>();
267
268            var controllerName = RemoveSuffix(entry.ControllerType.Name, "Controller");
269
270            var viewNames = new List<string>();
271            var includeViews = entry.IncludeViews;
272            if (includeViews.Count == 0)
273                includeViews = new[] { "*" };
274
275            foreach (var include in includeViews)
276            {
277                if (include.EndsWith("*"))
278                {
279                    foreach (var fileName in ViewFolder.ListViews(controllerName))
280                    {
281                        if (!string.Equals(Path.GetExtension(fileName), ".spark", StringComparison.InvariantCultureIgnoreCase))
282                            continue;
283
284                        var potentialMatch = Path.GetFileNameWithoutExtension(fileName);
285                        if (!TestMatch(potentialMatch, include))
286                            continue;
287
288                        var isExcluded = false;
289                        foreach (var exclude in entry.ExcludeViews)
290                        {
291                            if (!TestMatch(potentialMatch, RemoveSuffix(exclude, ".spark")))
292                                continue;
293
294                            isExcluded = true;
295                            break;
296                        }
297                        if (!isExcluded)
298                            viewNames.Add(potentialMatch);
299                    }
300                }
301                else
302                {
303                    // explicitly included views don't test for exclusion
304                    viewNames.Add(RemoveSuffix(include, ".spark"));
305                }
306            }
307
308            foreach (var viewName in viewNames)
309            {
310                if (entry.LayoutNames.Count == 0)
311                {
312                    descriptors.Add(CreateDescriptor(
313                                        entry.ControllerType.Namespace,
314                                        controllerName,
315                                        viewName,
316                                        null /*masterName*/,
317                                        true));
318                }
319                else
320                {
321                    foreach (var masterName in entry.LayoutNames)
322                    {
323                        descriptors.Add(CreateDescriptor(
324                                            entry.ControllerType.Namespace,
325                                            controllerName,
326                                            viewName,
327                                            string.Join(" ", masterName.ToArray()),
328                                            false));
329                    }
330                }
331            }
332
333            return descriptors;
334        }
335
336        private static bool TestMatch(string potentialMatch, string pattern)
337        {
338            if (!pattern.EndsWith("*"))
339            {
340                return string.Equals(potentialMatch, pattern, StringComparison.InvariantCultureIgnoreCase);
341            }
342
343            // raw wildcard matches anything that's not a partial
344            if (pattern == "*")
345            {
346                return !potentialMatch.StartsWith("_");
347            }
348
349            // otherwise the only thing that's supported is "starts with"
350            return potentialMatch.StartsWith(pattern.Substring(0, pattern.Length - 1),
351                                             StringComparison.InvariantCultureIgnoreCase);
352        }
353
354        private static string RemoveSuffix(string value, string suffix)
355        {
356            if (value.EndsWith(suffix, StringComparison.InvariantCultureIgnoreCase))
357                return value.Substring(0, value.Length - suffix.Length);
358            return value;
359        }
360
361
362
363        #region IViewEngine Members
364
365        ViewEngineResult IViewEngine.FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
366        {
367            return FindPartialView(controllerContext, partialViewName, useCache);
368        }
369
370        ViewEngineResult IViewEngine.FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
371        {
372            return FindView(controllerContext, viewName, masterName, useCache);
373        }
374
375        void IViewEngine.ReleaseView(ControllerContext controllerContext, IView view)
376        {
377            ReleaseView(controllerContext, view);
378        }
379
380        #endregion
381
382
383        #region ISparkServiceInitialize Members
384
385        void ISparkServiceInitialize.Initialize(ISparkServiceContainer container)
386        {
387            Initialize(container);
388        }
389
390        #endregion
391
392
393        #region IViewFolderContainer Members
394
395        IViewFolder IViewFolderContainer.ViewFolder
396        {
397            get { return ViewFolder; }
398            set { ViewFolder = value; }
399        }
400
401        #endregion
402    }
403}