1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
|
namespace System.Web.Mvc {
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
internal sealed class ControllerTypeCache {
private const string _typeCacheName = "MVC-ControllerTypeCache.xml";
private Dictionary<string, ILookup<string, Type>> _cache;
private object _lockObj = new object();
internal int Count {
get {
int count = 0;
foreach (var lookup in _cache.Values) {
foreach (var grouping in lookup) {
count += grouping.Count();
}
}
return count;
}
}
public void EnsureInitialized(IBuildManager buildManager) {
if (_cache == null) {
lock (_lockObj) {
if (_cache == null) {
List<Type> controllerTypes = TypeCacheUtil.GetFilteredTypesFromAssemblies(_typeCacheName, IsControllerType, buildManager);
var groupedByName = controllerTypes.GroupBy(
t => t.Name.Substring(0, t.Name.Length - "Controller".Length),
StringComparer.OrdinalIgnoreCase);
_cache = groupedByName.ToDictionary(
g => g.Key,
g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase),
StringComparer.OrdinalIgnoreCase);
}
}
}
}
public ICollection<Type> GetControllerTypes(string controllerName, HashSet<string> namespaces) {
HashSet<Type> matchingTypes = new HashSet<Type>();
ILookup<string, Type> nsLookup;
if (_cache.TryGetValue(controllerName, out nsLookup)) {
// this friendly name was located in the cache, now cycle through namespaces
if (namespaces != null) {
foreach (string requestedNamespace in namespaces) {
foreach (var targetNamespaceGrouping in nsLookup) {
if (IsNamespaceMatch(requestedNamespace, targetNamespaceGrouping.Key)) {
matchingTypes.UnionWith(targetNamespaceGrouping);
}
}
}
}
else {
// if the namespaces parameter is null, search *every* namespace
foreach (var nsGroup in nsLookup) {
matchingTypes.UnionWith(nsGroup);
}
}
}
return matchingTypes;
}
internal static bool IsControllerType(Type t) {
return
t != null &&
t.IsPublic &&
t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
!t.IsAbstract &&
typeof(IController).IsAssignableFrom(t);
}
internal static bool IsNamespaceMatch(string requestedNamespace, string targetNamespace) {
// degenerate cases
if (requestedNamespace == null) {
return false;
}
else if (requestedNamespace.Length == 0) {
return true;
}
if (!requestedNamespace.EndsWith(".*", StringComparison.OrdinalIgnoreCase)) {
// looking for exact namespace match
return String.Equals(requestedNamespace, targetNamespace, StringComparison.OrdinalIgnoreCase);
}
else {
// looking for exact or sub-namespace match
requestedNamespace = requestedNamespace.Substring(0, requestedNamespace.Length - ".*".Length);
if (!targetNamespace.StartsWith(requestedNamespace, StringComparison.OrdinalIgnoreCase)) {
return false;
}
if (requestedNamespace.Length == targetNamespace.Length) {
// exact match
return true;
}
else if (targetNamespace[requestedNamespace.Length] == '.') {
// good prefix match, e.g. requestedNamespace = "Foo.Bar" and targetNamespace = "Foo.Bar.Baz"
return true;
}
else {
// bad prefix match, e.g. requestedNamespace = "Foo.Bar" and targetNamespace = "Foo.Bar2"
return false;
}
}
}
}
}
|