Fix bugs in sizing TableLayoutPanel (Xamarin bug 18638)
[mono.git] / mcs / class / System.Web.Mvc2 / System.Web.Mvc / ControllerTypeCache.cs
1 /* ****************************************************************************\r
2  *\r
3  * Copyright (c) Microsoft Corporation. All rights reserved.\r
4  *\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
8  *\r
9  * You must not remove this notice, or any other, from this software.\r
10  *\r
11  * ***************************************************************************/\r
12 \r
13 namespace System.Web.Mvc {\r
14     using System;\r
15     using System.Collections;\r
16     using System.Collections.Generic;\r
17     using System.Linq;\r
18     using System.Reflection;\r
19 \r
20     internal sealed class ControllerTypeCache {\r
21 \r
22         private const string _typeCacheName = "MVC-ControllerTypeCache.xml";\r
23 \r
24         private Dictionary<string, ILookup<string, Type>> _cache;\r
25         private object _lockObj = new object();\r
26 \r
27         internal int Count {\r
28             get {\r
29                 int count = 0;\r
30                 foreach (var lookup in _cache.Values) {\r
31                     foreach (var grouping in lookup) {\r
32                         count += grouping.Count();\r
33                     }\r
34                 }\r
35                 return count;\r
36             }\r
37         }\r
38 \r
39         public void EnsureInitialized(IBuildManager buildManager) {\r
40             if (_cache == null) {\r
41                 lock (_lockObj) {\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
48                             g => g.Key,\r
49                             g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase),\r
50                             StringComparer.OrdinalIgnoreCase);\r
51                     }\r
52                 }\r
53             }\r
54         }\r
55 \r
56         public ICollection<Type> GetControllerTypes(string controllerName, HashSet<string> namespaces) {\r
57             HashSet<Type> matchingTypes = new HashSet<Type>();\r
58 \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
67                             }\r
68                         }\r
69                     }\r
70                 }\r
71                 else {\r
72                     // if the namespaces parameter is null, search *every* namespace\r
73                     foreach (var nsGroup in nsLookup) {\r
74                         matchingTypes.UnionWith(nsGroup);\r
75                     }\r
76                 }\r
77             }\r
78 \r
79             return matchingTypes;\r
80         }\r
81 \r
82         internal static bool IsControllerType(Type t) {\r
83             return\r
84                 t != null &&\r
85                 t.IsPublic &&\r
86                 t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&\r
87                 !t.IsAbstract &&\r
88                 typeof(IController).IsAssignableFrom(t);\r
89         }\r
90 \r
91         internal static bool IsNamespaceMatch(string requestedNamespace, string targetNamespace) {\r
92             // degenerate cases\r
93             if (requestedNamespace == null) {\r
94                 return false;\r
95             }\r
96             else if (requestedNamespace.Length == 0) {\r
97                 return true;\r
98             }\r
99 \r
100             if (!requestedNamespace.EndsWith(".*", StringComparison.OrdinalIgnoreCase)) {\r
101                 // looking for exact namespace match\r
102                 return String.Equals(requestedNamespace, targetNamespace, StringComparison.OrdinalIgnoreCase);\r
103             }\r
104             else {\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
108                     return false;\r
109                 }\r
110 \r
111                 if (requestedNamespace.Length == targetNamespace.Length) {\r
112                     // exact match\r
113                     return true;\r
114                 }\r
115                 else if (targetNamespace[requestedNamespace.Length] == '.') {\r
116                     // good prefix match, e.g. requestedNamespace = "Foo.Bar" and targetNamespace = "Foo.Bar.Baz"\r
117                     return true;\r
118                 }\r
119                 else {\r
120                     // bad prefix match, e.g. requestedNamespace = "Foo.Bar" and targetNamespace = "Foo.Bar2"\r
121                     return false;\r
122                 }\r
123             }\r
124         }\r
125 \r
126     }\r
127 }\r