/* **************************************************************************** * * Copyright (c) Microsoft Corporation. All rights reserved. * * This software is subject to the Microsoft Public License (Ms-PL). * A copy of the license can be found in the license.htm file included * in this distribution. * * You must not remove this notice, or any other, from this software. * * ***************************************************************************/ namespace System.Web.Mvc { using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Web; using System.Web.Hosting; using System.Web.Mvc.Resources; public abstract class VirtualPathProviderViewEngine : IViewEngine { // format is ":ViewCacheEntry:{cacheType}:{prefix}:{name}:{controllerName}:" private const string _cacheKeyFormat = ":ViewCacheEntry:{0}:{1}:{2}:{3}:"; private const string _cacheKeyPrefix_Master = "Master"; private const string _cacheKeyPrefix_Partial = "Partial"; private const string _cacheKeyPrefix_View = "View"; private static readonly string[] _emptyLocations = new string[0]; private VirtualPathProvider _vpp; [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] MasterLocationFormats { get; set; } [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] PartialViewLocationFormats { get; set; } public IViewLocationCache ViewLocationCache { get; set; } [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] ViewLocationFormats { get; set; } protected VirtualPathProvider VirtualPathProvider { get { if (_vpp == null) { _vpp = HostingEnvironment.VirtualPathProvider; } return _vpp; } set { _vpp = value; } } protected VirtualPathProviderViewEngine() { if (HttpContext.Current == null || HttpContext.Current.IsDebuggingEnabled) { ViewLocationCache = DefaultViewLocationCache.Null; } else { ViewLocationCache = new DefaultViewLocationCache(); } } private string CreateCacheKey(string prefix, string name, string controllerName) { return String.Format(CultureInfo.InvariantCulture, _cacheKeyFormat, GetType().AssemblyQualifiedName, prefix, name, controllerName); } protected abstract IView CreatePartialView(ControllerContext controllerContext, string partialPath); protected abstract IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath); protected virtual bool FileExists(ControllerContext controllerContext, string virtualPath) { return VirtualPathProvider.FileExists(virtualPath); } public virtual ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (String.IsNullOrEmpty(partialViewName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "partialViewName"); } string[] searched; string controllerName = controllerContext.RouteData.GetRequiredString("controller"); string partialPath = GetPath(controllerContext, PartialViewLocationFormats, "PartialViewLocationFormats", partialViewName, controllerName, _cacheKeyPrefix_Partial, useCache, out searched); if (String.IsNullOrEmpty(partialPath)) { return new ViewEngineResult(searched); } return new ViewEngineResult(CreatePartialView(controllerContext, partialPath), this); } public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (String.IsNullOrEmpty(viewName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewName"); } string[] viewLocationsSearched; string[] masterLocationsSearched; string controllerName = controllerContext.RouteData.GetRequiredString("controller"); string viewPath = GetPath(controllerContext, ViewLocationFormats, "ViewLocationFormats", viewName, controllerName, _cacheKeyPrefix_View, useCache, out viewLocationsSearched); string masterPath = GetPath(controllerContext, MasterLocationFormats, "MasterLocationFormats", masterName, controllerName, _cacheKeyPrefix_Master, useCache, out masterLocationsSearched); if (String.IsNullOrEmpty(viewPath) || (String.IsNullOrEmpty(masterPath) && !String.IsNullOrEmpty(masterName))) { return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched)); } return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this); } private string GetPath(ControllerContext controllerContext, string[] locations, string locationsPropertyName, string name, string controllerName, string cacheKeyPrefix, bool useCache, out string[] searchedLocations) { searchedLocations = _emptyLocations; if (String.IsNullOrEmpty(name)) { return String.Empty; } if (locations == null || locations.Length == 0) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, MvcResources.Common_PropertyCannotBeNullOrEmpty, locationsPropertyName)); } bool nameRepresentsPath = IsSpecificPath(name); string cacheKey = CreateCacheKey(cacheKeyPrefix, name, (nameRepresentsPath) ? String.Empty : controllerName); if (useCache) { string result = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, cacheKey); if (result != null) { return result; } } return (nameRepresentsPath) ? GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations) : GetPathFromGeneralName(controllerContext, locations, name, controllerName, cacheKey, ref searchedLocations); } private string GetPathFromGeneralName(ControllerContext controllerContext, string[] locations, string name, string controllerName, string cacheKey, ref string[] searchedLocations) { string result = String.Empty; searchedLocations = new string[locations.Length]; for (int i = 0; i < locations.Length; i++) { string virtualPath = String.Format(CultureInfo.InvariantCulture, locations[i], name, controllerName); if (FileExists(controllerContext, virtualPath)) { searchedLocations = _emptyLocations; result = virtualPath; ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result); break; } searchedLocations[i] = virtualPath; } return result; } private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, ref string[] searchedLocations) { string result = name; if (!FileExists(controllerContext, name)) { result = String.Empty; searchedLocations = new[] { name }; } ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result); return result; } private static bool IsSpecificPath(string name) { char c = name[0]; return (c == '~' || c == '/'); } public virtual void ReleaseView(ControllerContext controllerContext, IView view) { IDisposable disposable = view as IDisposable; if (disposable != null) { disposable.Dispose(); } } } }