1 /* ****************************************************************************
\r
3 * Copyright (c) Microsoft Corporation. All rights reserved.
\r
5 * This software is subject to the Microsoft Public License (Ms-PL).
\r
6 * A copy of the license can be found in the license.htm file included
\r
7 * in this distribution.
\r
9 * You must not remove this notice, or any other, from this software.
\r
11 * ***************************************************************************/
\r
13 namespace System.Web.Mvc {
\r
15 using System.Collections;
\r
16 using System.Collections.Generic;
\r
18 using System.Reflection;
\r
20 internal sealed class ControllerTypeCache {
\r
22 private const string _typeCacheName = "MVC-ControllerTypeCache.xml";
\r
24 private Dictionary<string, ILookup<string, Type>> _cache;
\r
25 private object _lockObj = new object();
\r
27 internal int Count {
\r
30 foreach (var lookup in _cache.Values) {
\r
31 foreach (var grouping in lookup) {
\r
32 count += grouping.Count();
\r
39 public void EnsureInitialized(IBuildManager buildManager) {
\r
40 if (_cache == null) {
\r
42 if (_cache == null) {
\r
43 List<Type> controllerTypes = TypeCacheUtil.GetFilteredTypesFromAssemblies(_typeCacheName, IsControllerType, buildManager);
\r
44 var groupedByName = controllerTypes.GroupBy(
\r
45 t => t.Name.Substring(0, t.Name.Length - "Controller".Length),
\r
46 StringComparer.OrdinalIgnoreCase);
\r
47 _cache = groupedByName.ToDictionary(
\r
49 g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase),
\r
50 StringComparer.OrdinalIgnoreCase);
\r
56 public ICollection<Type> GetControllerTypes(string controllerName, HashSet<string> namespaces) {
\r
57 HashSet<Type> matchingTypes = new HashSet<Type>();
\r
59 ILookup<string, Type> nsLookup;
\r
60 if (_cache.TryGetValue(controllerName, out nsLookup)) {
\r
61 // this friendly name was located in the cache, now cycle through namespaces
\r
62 if (namespaces != null) {
\r
63 foreach (string requestedNamespace in namespaces) {
\r
64 foreach (var targetNamespaceGrouping in nsLookup) {
\r
65 if (IsNamespaceMatch(requestedNamespace, targetNamespaceGrouping.Key)) {
\r
66 matchingTypes.UnionWith(targetNamespaceGrouping);
\r
72 // if the namespaces parameter is null, search *every* namespace
\r
73 foreach (var nsGroup in nsLookup) {
\r
74 matchingTypes.UnionWith(nsGroup);
\r
79 return matchingTypes;
\r
82 internal static bool IsControllerType(Type t) {
\r
86 t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
\r
88 typeof(IController).IsAssignableFrom(t);
\r
91 internal static bool IsNamespaceMatch(string requestedNamespace, string targetNamespace) {
\r
93 if (requestedNamespace == null) {
\r
96 else if (requestedNamespace.Length == 0) {
\r
100 if (!requestedNamespace.EndsWith(".*", StringComparison.OrdinalIgnoreCase)) {
\r
101 // looking for exact namespace match
\r
102 return String.Equals(requestedNamespace, targetNamespace, StringComparison.OrdinalIgnoreCase);
\r
105 // looking for exact or sub-namespace match
\r
106 requestedNamespace = requestedNamespace.Substring(0, requestedNamespace.Length - ".*".Length);
\r
107 if (!targetNamespace.StartsWith(requestedNamespace, StringComparison.OrdinalIgnoreCase)) {
\r
111 if (requestedNamespace.Length == targetNamespace.Length) {
\r
115 else if (targetNamespace[requestedNamespace.Length] == '.') {
\r
116 // good prefix match, e.g. requestedNamespace = "Foo.Bar" and targetNamespace = "Foo.Bar.Baz"
\r
120 // bad prefix match, e.g. requestedNamespace = "Foo.Bar" and targetNamespace = "Foo.Bar2"
\r