Fix bugs in sizing TableLayoutPanel (Xamarin bug 18638)
[mono.git] / mcs / class / System.Web.Mvc2 / System.Web.Mvc / DefaultControllerFactory.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.Globalization;\r
18     using System.Linq;\r
19     using System.Text;\r
20     using System.Web;\r
21     using System.Web.Mvc.Resources;\r
22     using System.Web.Routing;\r
23 \r
24     public class DefaultControllerFactory : IControllerFactory {\r
25 \r
26         private IBuildManager _buildManager;\r
27         private ControllerBuilder _controllerBuilder;\r
28         private ControllerTypeCache _instanceControllerTypeCache;\r
29         private static ControllerTypeCache _staticControllerTypeCache = new ControllerTypeCache();\r
30 \r
31         internal IBuildManager BuildManager {\r
32             get {\r
33                 if (_buildManager == null) {\r
34                     _buildManager = new BuildManagerWrapper();\r
35                 }\r
36                 return _buildManager;\r
37             }\r
38             set {\r
39                 _buildManager = value;\r
40             }\r
41         }\r
42 \r
43         internal ControllerBuilder ControllerBuilder {\r
44             get {\r
45                 return _controllerBuilder ?? ControllerBuilder.Current;\r
46             }\r
47             set {\r
48                 _controllerBuilder = value;\r
49             }\r
50         }\r
51 \r
52         internal ControllerTypeCache ControllerTypeCache {\r
53             get {\r
54                 return _instanceControllerTypeCache ?? _staticControllerTypeCache;\r
55             }\r
56             set {\r
57                 _instanceControllerTypeCache = value;\r
58             }\r
59         }\r
60 \r
61         internal static InvalidOperationException CreateAmbiguousControllerException(RouteBase route, string controllerName, ICollection<Type> matchingTypes) {\r
62             // we need to generate an exception containing all the controller types\r
63             StringBuilder typeList = new StringBuilder();\r
64             foreach (Type matchedType in matchingTypes) {\r
65                 typeList.AppendLine();\r
66                 typeList.Append(matchedType.FullName);\r
67             }\r
68 \r
69             string errorText;\r
70             Route castRoute = route as Route;\r
71             if (castRoute != null) {\r
72                 errorText = String.Format(CultureInfo.CurrentUICulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithRouteUrl,\r
73                     controllerName, castRoute.Url, typeList);\r
74             }\r
75             else {\r
76                 errorText = String.Format(CultureInfo.CurrentUICulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithoutRouteUrl,\r
77                     controllerName, typeList);\r
78             }\r
79 \r
80             return new InvalidOperationException(errorText);\r
81         }\r
82 \r
83         public virtual IController CreateController(RequestContext requestContext, string controllerName) {\r
84             if (requestContext == null) {\r
85                 throw new ArgumentNullException("requestContext");\r
86             }\r
87             if (String.IsNullOrEmpty(controllerName)) {\r
88                 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");\r
89             }\r
90             Type controllerType = GetControllerType(requestContext, controllerName);\r
91             IController controller = GetControllerInstance(requestContext, controllerType);\r
92             return controller;\r
93         }\r
94 \r
95         protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType) {\r
96             if (controllerType == null) {\r
97                 throw new HttpException(404,\r
98                     String.Format(\r
99                         CultureInfo.CurrentUICulture,\r
100                         MvcResources.DefaultControllerFactory_NoControllerFound,\r
101                         requestContext.HttpContext.Request.Path));\r
102             }\r
103             if (!typeof(IController).IsAssignableFrom(controllerType)) {\r
104                 throw new ArgumentException(\r
105                     String.Format(\r
106                         CultureInfo.CurrentUICulture,\r
107                         MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,\r
108                         controllerType),\r
109                     "controllerType");\r
110             }\r
111             try {\r
112                 return (IController)Activator.CreateInstance(controllerType);\r
113             }\r
114             catch (Exception ex) {\r
115                 throw new InvalidOperationException(\r
116                     String.Format(\r
117                         CultureInfo.CurrentUICulture,\r
118                         MvcResources.DefaultControllerFactory_ErrorCreatingController,\r
119                         controllerType),\r
120                     ex);\r
121             }\r
122         }\r
123 \r
124         protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) {\r
125             if (String.IsNullOrEmpty(controllerName)) {\r
126                 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");\r
127             }\r
128 \r
129             // first search in the current route's namespace collection\r
130             object routeNamespacesObj;\r
131             Type match;\r
132             if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)) {\r
133                 IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;\r
134                 if (routeNamespaces != null && routeNamespaces.Any()) {\r
135                     HashSet<string> nsHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);\r
136                     match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsHash);\r
137 \r
138                     // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"\r
139                     if (match != null || false.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"])) {\r
140                         // got a match or the route requested we stop looking\r
141                         return match;\r
142                     }\r
143                 }\r
144             }\r
145 \r
146             // then search in the application's default namespace collection\r
147             if (ControllerBuilder.DefaultNamespaces.Count > 0) {\r
148                 HashSet<string> nsDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);\r
149                 match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsDefaults);\r
150                 if (match != null) {\r
151                     return match;\r
152                 }\r
153             }\r
154 \r
155             // if all else fails, search every namespace\r
156             return GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null /* namespaces */);\r
157         }\r
158 \r
159         private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces) {\r
160             // Once the master list of controllers has been created we can quickly index into it\r
161             ControllerTypeCache.EnsureInitialized(BuildManager);\r
162 \r
163             ICollection<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);\r
164             switch (matchingTypes.Count) {\r
165                 case 0:\r
166                     // no matching types\r
167                     return null;\r
168 \r
169                 case 1:\r
170                     // single matching type\r
171                     return matchingTypes.First();\r
172 \r
173                 default:\r
174                     // multiple matching types\r
175                     throw CreateAmbiguousControllerException(route, controllerName, matchingTypes);\r
176             }\r
177         }\r
178 \r
179         public virtual void ReleaseController(IController controller) {\r
180             IDisposable disposable = controller as IDisposable;\r
181             if (disposable != null) {\r
182                 disposable.Dispose();\r
183             }\r
184         }\r
185 \r
186     }\r
187 }\r