a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
274 lines
12 KiB
C#
274 lines
12 KiB
C#
namespace System.Web.Mvc {
|
|
using System;
|
|
using System.Collections.Generic;
|
|
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}:{areaName}:"
|
|
private const string _cacheKeyFormat = ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:";
|
|
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;
|
|
internal Func<string, string> GetExtensionThunk = VirtualPathUtility.GetExtension;
|
|
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public string[] AreaMasterLocationFormats {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public string[] AreaPartialViewLocationFormats {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public string[] AreaViewLocationFormats {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public string[] FileExtensions {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
[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, string areaName) {
|
|
return String.Format(CultureInfo.InvariantCulture, _cacheKeyFormat,
|
|
GetType().AssemblyQualifiedName, prefix, name, controllerName, areaName);
|
|
}
|
|
|
|
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, AreaPartialViewLocationFormats, "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, AreaViewLocationFormats, "ViewLocationFormats", viewName, controllerName, _cacheKeyPrefix_View, useCache, out viewLocationsSearched);
|
|
string masterPath = GetPath(controllerContext, MasterLocationFormats, AreaMasterLocationFormats, "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[] areaLocations, string locationsPropertyName, string name, string controllerName, string cacheKeyPrefix, bool useCache, out string[] searchedLocations) {
|
|
searchedLocations = _emptyLocations;
|
|
|
|
if (String.IsNullOrEmpty(name)) {
|
|
return String.Empty;
|
|
}
|
|
|
|
string areaName = AreaHelpers.GetAreaName(controllerContext.RouteData);
|
|
bool usingAreas = !String.IsNullOrEmpty(areaName);
|
|
List<ViewLocation> viewLocations = GetViewLocations(locations, (usingAreas) ? areaLocations : null);
|
|
|
|
if (viewLocations.Count == 0) {
|
|
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
|
|
MvcResources.Common_PropertyCannotBeNullOrEmpty, locationsPropertyName));
|
|
}
|
|
|
|
bool nameRepresentsPath = IsSpecificPath(name);
|
|
string cacheKey = CreateCacheKey(cacheKeyPrefix, name, (nameRepresentsPath) ? String.Empty : controllerName, areaName);
|
|
|
|
if (useCache) {
|
|
return ViewLocationCache.GetViewLocation(controllerContext.HttpContext, cacheKey);
|
|
}
|
|
|
|
return (nameRepresentsPath) ?
|
|
GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations) :
|
|
GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, cacheKey, ref searchedLocations);
|
|
}
|
|
|
|
private string GetPathFromGeneralName(ControllerContext controllerContext, List<ViewLocation> locations, string name, string controllerName, string areaName, string cacheKey, ref string[] searchedLocations) {
|
|
string result = String.Empty;
|
|
searchedLocations = new string[locations.Count];
|
|
|
|
for (int i = 0; i < locations.Count; i++) {
|
|
ViewLocation location = locations[i];
|
|
string virtualPath = location.Format(name, controllerName, areaName);
|
|
|
|
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 (!(FilePathIsSupported(name) && FileExists(controllerContext, name))) {
|
|
result = String.Empty;
|
|
searchedLocations = new[] { name };
|
|
}
|
|
|
|
ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result);
|
|
return result;
|
|
}
|
|
|
|
private bool FilePathIsSupported(string virtualPath) {
|
|
if (FileExtensions == null) {
|
|
// legacy behavior for custom ViewEngine that might not set the FileExtensions property
|
|
return true;
|
|
}
|
|
else {
|
|
// get rid of the '.' because the FileExtensions property expects extensions withouth a dot.
|
|
string extension = GetExtensionThunk(virtualPath).TrimStart('.');
|
|
return FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
}
|
|
|
|
private static List<ViewLocation> GetViewLocations(string[] viewLocationFormats, string[] areaViewLocationFormats) {
|
|
List<ViewLocation> allLocations = new List<ViewLocation>();
|
|
|
|
if (areaViewLocationFormats != null) {
|
|
foreach (string areaViewLocationFormat in areaViewLocationFormats) {
|
|
allLocations.Add(new AreaAwareViewLocation(areaViewLocationFormat));
|
|
}
|
|
}
|
|
|
|
if (viewLocationFormats != null) {
|
|
foreach (string viewLocationFormat in viewLocationFormats) {
|
|
allLocations.Add(new ViewLocation(viewLocationFormat));
|
|
}
|
|
}
|
|
|
|
return allLocations;
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
private class ViewLocation {
|
|
|
|
protected string _virtualPathFormatString;
|
|
|
|
public ViewLocation(string virtualPathFormatString) {
|
|
_virtualPathFormatString = virtualPathFormatString;
|
|
}
|
|
|
|
public virtual string Format(string viewName, string controllerName, string areaName) {
|
|
return String.Format(CultureInfo.InvariantCulture, _virtualPathFormatString, viewName, controllerName);
|
|
}
|
|
|
|
}
|
|
|
|
private class AreaAwareViewLocation : ViewLocation {
|
|
|
|
public AreaAwareViewLocation(string virtualPathFormatString)
|
|
: base(virtualPathFormatString) {
|
|
}
|
|
|
|
public override string Format(string viewName, string controllerName, string areaName) {
|
|
return String.Format(CultureInfo.InvariantCulture, _virtualPathFormatString, viewName, controllerName, areaName);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|