Files
linux-packaging-mono/external/aspnetwebstack/src/System.Web.Mvc/VirtualPathProviderViewEngine.cs
Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

348 lines
15 KiB
C#

// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Web.Hosting;
using System.Web.Mvc.Properties;
using System.Web.WebPages;
namespace System.Web.Mvc
{
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 CacheKeyPrefixMaster = "Master";
private const string CacheKeyPrefixPartial = "Partial";
private const string CacheKeyPrefixView = "View";
private static readonly string[] _emptyLocations = new string[0];
private DisplayModeProvider _displayModeProvider;
private VirtualPathProvider _vpp;
internal Func<string, string> GetExtensionThunk = VirtualPathUtility.GetExtension;
protected VirtualPathProviderViewEngine()
{
if (HttpContext.Current == null || HttpContext.Current.IsDebuggingEnabled)
{
ViewLocationCache = DefaultViewLocationCache.Null;
}
else
{
ViewLocationCache = new DefaultViewLocationCache();
}
}
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")]
public string[] AreaMasterLocationFormats { get; set; }
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")]
public string[] AreaPartialViewLocationFormats { get; set; }
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")]
public string[] AreaViewLocationFormats { get; set; }
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")]
public string[] FileExtensions { get; set; }
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")]
public string[] MasterLocationFormats { get; set; }
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")]
public string[] PartialViewLocationFormats { get; set; }
public IViewLocationCache ViewLocationCache { get; set; }
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")]
public string[] ViewLocationFormats { get; set; }
protected VirtualPathProvider VirtualPathProvider
{
get
{
if (_vpp == null)
{
_vpp = HostingEnvironment.VirtualPathProvider;
}
return _vpp;
}
set { _vpp = value; }
}
protected internal DisplayModeProvider DisplayModeProvider
{
get { return _displayModeProvider ?? DisplayModeProvider.Instance; }
set { _displayModeProvider = value; }
}
private string CreateCacheKey(string prefix, string name, string controllerName, string areaName)
{
return String.Format(CultureInfo.InvariantCulture, CacheKeyFormat,
GetType().AssemblyQualifiedName, prefix, name, controllerName, areaName);
}
internal static string AppendDisplayModeToCacheKey(string cacheKey, string displayMode)
{
// key format is ":ViewCacheEntry:{cacheType}:{prefix}:{name}:{controllerName}:{areaName}:"
// so append "{displayMode}:" to the key
return cacheKey + displayMode + ":";
}
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, CacheKeyPrefixPartial, 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, CacheKeyPrefixView, useCache, out viewLocationsSearched);
string masterPath = GetPath(controllerContext, MasterLocationFormats, AreaMasterLocationFormats, "MasterLocationFormats", masterName, controllerName, CacheKeyPrefixMaster, 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)
{
// Only look at cached display modes that can handle the context.
IEnumerable<IDisplayMode> possibleDisplayModes = DisplayModeProvider.GetAvailableDisplayModesForContext(controllerContext.HttpContext, controllerContext.DisplayMode);
foreach (IDisplayMode displayMode in possibleDisplayModes)
{
string cachedLocation = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, displayMode.DisplayModeId));
if (cachedLocation != null)
{
if (controllerContext.DisplayMode == null)
{
controllerContext.DisplayMode = displayMode;
}
return cachedLocation;
}
}
// GetPath is called again without using the cache.
return null;
}
else
{
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);
DisplayInfo virtualPathDisplayInfo = DisplayModeProvider.GetDisplayInfoForVirtualPath(virtualPath, controllerContext.HttpContext, path => FileExists(controllerContext, path), controllerContext.DisplayMode);
if (virtualPathDisplayInfo != null)
{
string resolvedVirtualPath = virtualPathDisplayInfo.FilePath;
searchedLocations = _emptyLocations;
result = resolvedVirtualPath;
ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, virtualPathDisplayInfo.DisplayMode.DisplayModeId), result);
if (controllerContext.DisplayMode == null)
{
controllerContext.DisplayMode = virtualPathDisplayInfo.DisplayMode;
}
// Populate the cache with the existing paths returned by all display modes.
// Since we currently don't keep track of cache misses, if we cache view.aspx on a request from a standard browser
// we don't want a cache hit for view.aspx from a mobile browser so we populate the cache with view.Mobile.aspx.
IEnumerable<IDisplayMode> allDisplayModes = DisplayModeProvider.Modes;
foreach (IDisplayMode displayMode in allDisplayModes)
{
if (displayMode.DisplayModeId != virtualPathDisplayInfo.DisplayMode.DisplayModeId)
{
DisplayInfo displayInfoToCache = displayMode.GetDisplayInfo(controllerContext.HttpContext, virtualPath, virtualPathExists: path => FileExists(controllerContext, path));
if (displayInfoToCache != null && displayInfoToCache.FilePath != null)
{
ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, displayInfoToCache.DisplayMode.DisplayModeId), displayInfoToCache.FilePath);
}
}
}
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 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);
}
}
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);
}
}
}
}