Fix the build.
[castle.git] / MonoRail / Castle.MonoRail.Framework / Services / DefaultUrlBuilder.cs
blob3205841df95781e076a875c5cc137be26027304e
1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.MonoRail.Framework.Services
17 using System;
18 using System.Collections;
19 using System.Collections.Specialized;
20 using Castle.Core;
21 using Castle.MonoRail.Framework.Internal;
22 using Framework;
24 /// <summary>
25 /// Default implementation of <see cref="IUrlBuilder"/>
26 /// </summary>
27 /// <remarks>
28 /// The property <see cref="UseExtensions"/> defines whether the builder should output
29 /// file extension. This might be handy to use in combination with a url rewrite strategy
30 ///
31 /// <para>
32 /// If you want to create a custom urlbuilder, you can extend this one and override
33 /// the <see cref="InternalBuildUrl(string,string,string,string,string,string,string,string,string,bool,bool,string)"/>
34 /// </para>
35 ///
36 /// </remarks>
37 /// <seealso cref="BuildUrl(UrlInfo,IDictionary)"/>
38 public class DefaultUrlBuilder : IUrlBuilder, IServiceEnabledComponent
40 private bool useExtensions = true;
41 private IServerUtility serverUtil;
42 private IRoutingEngine routingEng;
44 /// <summary>
45 /// Initializes a new instance of the <see cref="DefaultUrlBuilder"/> class.
46 /// </summary>
47 public DefaultUrlBuilder()
51 #region Properties
53 /// <summary>
54 /// Gets or sets a value indicating whether the builder should output an extension.
55 /// </summary>
56 /// <value><c>true</c> if should use extensions; otherwise, <c>false</c>.</value>
57 public bool UseExtensions
59 get { return useExtensions; }
60 set { useExtensions = value; }
63 /// <summary>
64 /// Gets or sets the server utility instance.
65 /// </summary>
66 /// <value>The server util.</value>
67 public IServerUtility ServerUtil
69 get { return serverUtil; }
70 set { serverUtil = value; }
73 /// <summary>
74 /// Gets or sets the routing engine.
75 /// </summary>
76 /// <value>The routing engine.</value>
77 public IRoutingEngine RoutingEngine
79 get { return routingEng; }
80 set { routingEng = value; }
83 #endregion
85 #region IServiceEnabledComponent
87 /// <summary>
88 /// Services the specified provider.
89 /// </summary>
90 /// <param name="provider">The provider.</param>
91 public void Service(IServiceProvider provider)
93 serverUtil = (IServerUtility) provider.GetService(typeof(IServerUtility));
94 routingEng = (IRoutingEngine) provider.GetService(typeof(IRoutingEngine));
97 #endregion
99 #region IUrlBuilder
101 /// <summary>
102 /// Builds the URL using the current url as contextual information and a parameter dictionary.
103 /// <para>
104 /// Common parameters includes area, controller and action.
105 /// </para>
106 /// </summary>
107 /// <param name="current">The current Url information.</param>
108 /// <param name="parameters">The parameters.</param>
109 /// <returns></returns>
110 public UrlPartsBuilder CreateUrlPartsBuilder(UrlInfo current, IDictionary parameters)
112 string routeName = CommonUtils.ObtainEntryAndRemove(parameters, "named");
113 bool encode = CommonUtils.ObtainEntryAndRemove(parameters, "encode", "false") == "true";
115 if (routeName != null)
117 object routeParams = parameters["params"];
118 parameters.Remove("params");
120 return InternalBuildRouteUrl(current.Domain, current.AppVirtualDir, routeName, routeParams, encode);
122 else
124 bool applySubdomain = false;
125 bool createAbsolutePath = CommonUtils.ObtainEntryAndRemove(parameters, "absolute", "false") == "true";
127 string area;
129 if (parameters.Contains("area"))
131 area = CommonUtils.ObtainEntryAndRemove(parameters, "area");
133 if (area == null) area = string.Empty;
135 else
137 area = current.Area;
140 string controller = CommonUtils.ObtainEntryAndRemove(parameters, "controller", current.Controller);
141 string action = CommonUtils.ObtainEntryAndRemove(parameters, "action", current.Action);
143 string domain = CommonUtils.ObtainEntryAndRemove(parameters, "domain", current.Domain);
144 string subdomain = CommonUtils.ObtainEntryAndRemove(parameters, "subdomain", current.Subdomain);
145 string protocol = CommonUtils.ObtainEntryAndRemove(parameters, "protocol", current.Protocol);
146 string port = CommonUtils.ObtainEntryAndRemove(parameters, "port", current.Port.ToString());
147 string pathInfo = CommonUtils.ObtainEntryAndRemove(parameters, "pathinfo");
148 string suffix = null;
150 object queryString = CommonUtils.ObtainObjectEntryAndRemove(parameters, "querystring");
152 string basePath = null;
154 if (parameters.Contains("basepath"))
156 basePath = CommonUtils.ObtainEntryAndRemove(parameters, "basepath");
159 if (queryString != null)
161 if (queryString is IDictionary)
163 IDictionary qsDictionary = (IDictionary) queryString;
165 suffix = CommonUtils.BuildQueryString(serverUtil, qsDictionary, encode);
167 else if (queryString is NameValueCollection)
169 suffix = CommonUtils.BuildQueryString(serverUtil, (NameValueCollection) queryString, encode);
171 else if (queryString is string)
173 suffix = queryString.ToString();
177 if (subdomain.ToLower() != current.Subdomain.ToLower())
179 applySubdomain = true;
182 return InternalBuildUrl(area, controller, action, protocol, port, domain, subdomain,
183 current.AppVirtualDir, current.Extension, createAbsolutePath, applySubdomain, suffix,
184 basePath, pathInfo);
188 /// <summary>
189 /// Builds the URL using the current url as contextual information and a parameter dictionary.
190 ///
191 /// </summary>
192 ///
193 /// <remarks>
194 /// <para>
195 /// Common parameters includes <c>area</c>, <c>controller</c> and <c>action</c>, which outputs
196 /// <c>/area/controller/name.extension</c>
197 /// </para>
198 ///
199 /// <para>
200 /// Please note that if you dont specify an area or controller name, they will be inferred from the
201 /// context. If you want to use an empty area, you must specify <c>area=''</c>.
202 /// This is commonly a source of confusion, so understand the following cases:
203 /// </para>
204 ///
205 /// <example>
206 /// <code>
207 /// UrlInfo current = ... // Assume that the current is area Admin, controller Products and action List
208 ///
209 /// BuildUrl(current, {action: 'view'})
210 /// // returns /Admin/Products/view.castle
211 ///
212 /// BuildUrl(current, {controller: 'Home', action: 'index'})
213 /// // returns /Admin/Home/index.castle
214 ///
215 /// BuildUrl(current, {area:'', controller: 'Home', action: 'index'})
216 /// // returns /Home/index.castle
217 /// </code>
218 /// </example>
219 ///
220 /// <para>
221 /// The <c>querystring</c> parameter can be a string or a dictionary. It appends a query string to the url:
222 /// <c>/area/controller/name.extension?id=1</c>
223 /// </para>
224 ///
225 /// <para>
226 /// The <c>absolute</c> parameter forces the builder to output a full url like
227 /// <c>http://hostname/virtualdir/area/controller/name.extension</c>
228 /// </para>
230 /// <para>
231 /// The <c>pathinfo</c> parameter can be used to add information between the resource identifier and the
232 /// query string (optional)
233 /// <c>http://hostname/virtualdir/area/controller/name.extension/path/info/here</c> or
234 /// <c>http://hostname/virtualdir/area/controller/name.extension/path/info/here?key=value</c>
235 /// </para>
236 ///
237 /// <para>
238 /// The <c>encode</c> parameter forces the builder to encode the querystring
239 /// <c>/controller/name.extension?id=1&amp;name=John</c> which is required to output full xhtml compliant content.
240 /// </para>
241 ///
242 /// </remarks>
243 /// <param name="current">The current Url information.</param>
244 /// <param name="parameters">The parameters.</param>
245 /// <returns></returns>
246 public virtual string BuildUrl(UrlInfo current, IDictionary parameters)
248 return CreateUrlPartsBuilder(current, parameters).BuildPath();
251 /// <summary>
252 /// Pendent.
253 /// </summary>
254 /// <param name="current">The current.</param>
255 /// <param name="routeName">Name of the route.</param>
256 /// <returns></returns>
257 public string BuildRouteUrl(UrlInfo current, string routeName)
259 return InternalBuildRouteUrl(current.Domain, current.AppVirtualDir, routeName, null, false).BuildPath();
262 /// <summary>
263 /// Pendent.
264 /// </summary>
265 /// <param name="current">The current.</param>
266 /// <param name="routeName">Name of the route.</param>
267 /// <param name="parameters">The parameters.</param>
268 /// <returns></returns>
269 public string BuildRouteUrl(UrlInfo current, string routeName, IDictionary parameters)
271 return InternalBuildRouteUrl(current.Domain, current.AppVirtualDir, routeName, parameters, false).BuildPath();
274 /// <summary>
275 /// Pendent.
276 /// </summary>
277 /// <param name="current">The current.</param>
278 /// <param name="routeName">Name of the route.</param>
279 /// <param name="parameters">The parameters.</param>
280 /// <returns></returns>
281 public string BuildRouteUrl(UrlInfo current, string routeName, object parameters)
283 return InternalBuildRouteUrl(current.Domain, current.AppVirtualDir, routeName, parameters, false).BuildPath();
286 /// <summary>
287 /// Builds an URL using the controller name and action name.
288 /// </summary>
289 /// <param name="current">The current Url information.</param>
290 /// <param name="controller">The controller.</param>
291 /// <param name="action">The action.</param>
292 /// <returns></returns>
293 public virtual string BuildUrl(UrlInfo current, string controller, string action)
295 Hashtable parameters = new Hashtable();
296 parameters["controller"] = controller;
297 parameters["action"] = action;
298 return BuildUrl(current, parameters);
301 /// <summary>
302 /// Builds an URL using the controller name, action name, and a querystring dictionary.
303 /// </summary>
304 /// <param name="current">The current Url information.</param>
305 /// <param name="controller">The controller.</param>
306 /// <param name="action">The action.</param>
307 /// <param name="queryStringParams">The query string params.</param>
308 /// <returns></returns>
309 public virtual string BuildUrl(UrlInfo current, string controller, string action, IDictionary queryStringParams)
311 Hashtable parameters = new Hashtable();
312 parameters["controller"] = controller;
313 parameters["action"] = action;
314 parameters["querystring"] = queryStringParams;
315 return BuildUrl(current, parameters);
318 /// <summary>
319 /// Builds an URL using the controller name, action name, and a querystring name value collection.
320 /// </summary>
321 /// <param name="current">The current Url information.</param>
322 /// <param name="controller">The controller.</param>
323 /// <param name="action">The action.</param>
324 /// <param name="queryStringParams">The query string params.</param>
325 /// <returns></returns>
326 public virtual string BuildUrl(UrlInfo current, string controller, string action, NameValueCollection queryStringParams)
328 Hashtable parameters = new Hashtable();
329 parameters["controller"] = controller;
330 parameters["action"] = action;
331 parameters["querystring"] = queryStringParams;
332 return BuildUrl(current, parameters);
335 /// <summary>
336 /// Builds an URL using the area name, controller name and action name.
337 /// </summary>
338 /// <param name="current">The current Url information.</param>
339 /// <param name="area">The area.</param>
340 /// <param name="controller">The controller.</param>
341 /// <param name="action">The action.</param>
342 /// <returns></returns>
343 public virtual string BuildUrl(UrlInfo current, string area, string controller, string action)
345 Hashtable parameters = new Hashtable();
346 parameters["area"] = area;
347 parameters["controller"] = controller;
348 parameters["action"] = action;
349 return BuildUrl(current, parameters);
352 /// <summary>
353 /// Builds an URL using the area name, controller name, action name, and a querystring dictionary.
354 /// </summary>
355 /// <param name="current">The current Url information.</param>
356 /// <param name="area">The area.</param>
357 /// <param name="controller">The controller.</param>
358 /// <param name="action">The action.</param>
359 /// <param name="queryStringParams">The query string params.</param>
360 /// <returns></returns>
361 public virtual string BuildUrl(UrlInfo current, string area, string controller, string action, IDictionary queryStringParams)
363 Hashtable parameters = new Hashtable();
364 parameters["area"] = area;
365 parameters["controller"] = controller;
366 parameters["action"] = action;
367 parameters["querystring"] = queryStringParams;
368 return BuildUrl(current, parameters);
371 /// <summary>
372 /// Builds an URL using the area name, controller name, action name, and a querystring name value collection.
373 /// </summary>
374 /// <param name="current">The current Url information.</param>
375 /// <param name="area">The area.</param>
376 /// <param name="controller">The controller.</param>
377 /// <param name="action">The action.</param>
378 /// <param name="queryStringParams">The query string params.</param>
379 /// <returns></returns>
380 public virtual string BuildUrl(UrlInfo current, string area, string controller, string action,
381 NameValueCollection queryStringParams)
383 Hashtable parameters = new Hashtable();
384 parameters["area"] = area;
385 parameters["controller"] = controller;
386 parameters["action"] = action;
387 parameters["querystring"] = queryStringParams;
388 return BuildUrl(current, parameters);
391 #endregion
393 /// <summary>
394 /// Internals the build URL.
395 /// </summary>
396 /// <param name="area">The area.</param>
397 /// <param name="controller">The controller.</param>
398 /// <param name="action">The action.</param>
399 /// <param name="protocol">The protocol.</param>
400 /// <param name="port">The port.</param>
401 /// <param name="domain">The domain.</param>
402 /// <param name="subdomain">The subdomain.</param>
403 /// <param name="appVirtualDir">The app virtual dir.</param>
404 /// <param name="extension">The extension.</param>
405 /// <param name="absolutePath">if set to <c>true</c> [absolute path].</param>
406 /// <param name="applySubdomain">if set to <c>true</c> [apply subdomain].</param>
407 /// <param name="suffix">The suffix.</param>
408 /// <returns></returns>
409 protected virtual string InternalBuildUrl(string area, string controller, string action, string protocol,
410 string port, string domain, string subdomain, string appVirtualDir,
411 string extension,
412 bool absolutePath, bool applySubdomain, string suffix)
414 return
415 InternalBuildUrl(area, controller, action, protocol, port, domain, subdomain, appVirtualDir, extension, absolutePath,
416 applySubdomain, suffix, null, null).BuildPath();
419 /// <summary>
420 /// Internals the build URL.
421 /// </summary>
422 /// <param name="area">The area.</param>
423 /// <param name="controller">The controller.</param>
424 /// <param name="action">The action.</param>
425 /// <param name="protocol">The protocol.</param>
426 /// <param name="port">The port.</param>
427 /// <param name="domain">The domain.</param>
428 /// <param name="subdomain">The subdomain.</param>
429 /// <param name="appVirtualDir">The app virtual dir.</param>
430 /// <param name="extension">The extension.</param>
431 /// <param name="absolutePath">if set to <c>true</c> [absolute path].</param>
432 /// <param name="applySubdomain">if set to <c>true</c> [apply subdomain].</param>
433 /// <param name="suffix">The suffix.</param>
434 /// <param name="basePath">The base path.</param>
435 /// <param name="pathInfo">Path info</param>
436 /// <returns></returns>
437 protected virtual UrlPartsBuilder InternalBuildUrl(string area, string controller, string action, string protocol,
438 string port, string domain, string subdomain, string appVirtualDir,
439 string extension,
440 bool absolutePath, bool applySubdomain, string suffix,
441 string basePath,
442 string pathInfo)
444 if (area == null) throw new ArgumentNullException("area");
445 if (controller == null) throw new ArgumentNullException("controller");
446 if (action == null) throw new ArgumentNullException("action");
447 if (appVirtualDir == null) throw new ArgumentNullException("appVirtualDir");
449 string path;
451 if (basePath != null)
453 path = InternalBuildUrlUsingBasePath(action, area, basePath, controller);
455 else
457 path = InternalBuildUsingAppVirtualDir(absolutePath, action, applySubdomain, appVirtualDir, area, controller, domain, port, protocol, subdomain);
460 if (useExtensions)
462 path += "." + extension;
465 UrlPartsBuilder urlBuilder = new UrlPartsBuilder(path);
467 urlBuilder.PathInfoDict.Parse(pathInfo);
469 if (!string.IsNullOrEmpty(suffix))
471 urlBuilder.SetQueryString(suffix);
474 return urlBuilder;
477 /// <summary>
478 /// Internals the build route URL.
479 /// </summary>
480 /// <param name="hostname">The hostname.</param>
481 /// <param name="virtualDir">The virtual dir.</param>
482 /// <param name="name">The name.</param>
483 /// <param name="routeParams">The route params.</param>
484 /// <param name="encode">if set to <c>true</c> [encode].</param>
485 /// <returns></returns>
486 protected virtual UrlPartsBuilder InternalBuildRouteUrl(string hostname, string virtualDir,
487 string name, object routeParams, bool encode)
489 IDictionary parameters;
491 if (routeParams != null)
493 if (typeof(IDictionary).IsAssignableFrom(routeParams.GetType()))
495 parameters = (IDictionary) routeParams;
497 else
499 parameters = new ReflectionBasedDictionaryAdapter(routeParams);
502 else
504 parameters = new Hashtable();
507 String url = routingEng.CreateUrl(name, hostname, virtualDir, parameters);
509 // TODO: should encode?
511 return new UrlPartsBuilder(url);
514 /// <summary>
515 /// Internals the build using app virtual dir.
516 /// </summary>
517 /// <param name="absolutePath">if set to <c>true</c> [absolute path].</param>
518 /// <param name="action">The action.</param>
519 /// <param name="applySubdomain">if set to <c>true</c> [apply subdomain].</param>
520 /// <param name="appVirtualDir">The app virtual dir.</param>
521 /// <param name="area">The area.</param>
522 /// <param name="controller">The controller.</param>
523 /// <param name="domain">The domain.</param>
524 /// <param name="port">The port.</param>
525 /// <param name="protocol">The protocol.</param>
526 /// <param name="subdomain">The subdomain.</param>
527 /// <returns></returns>
528 protected static string InternalBuildUsingAppVirtualDir(bool absolutePath, string action,
529 bool applySubdomain, string appVirtualDir,
530 string area, string controller,
531 string domain, string port, string protocol, string subdomain)
533 string path;
535 if (appVirtualDir.Length > 1 && !(appVirtualDir[0] == '/'))
537 appVirtualDir = "/" + appVirtualDir;
540 if (area != String.Empty)
542 path = appVirtualDir + "/" + area + "/" + controller + "/" + action;
544 else
546 path = appVirtualDir + "/" + controller + "/" + action;
549 if (absolutePath)
551 string url;
553 if (applySubdomain)
555 url = protocol + "://" + subdomain + domain;
557 else
559 url = protocol + "://" + domain;
562 if (port != "80")
564 url += ":" + port;
567 path = url + path;
570 return path;
573 /// <summary>
574 /// Internals the build URL using base path.
575 /// </summary>
576 /// <param name="action">The action.</param>
577 /// <param name="area">The area.</param>
578 /// <param name="basePath">The base path.</param>
579 /// <param name="controller">The controller.</param>
580 /// <returns></returns>
581 protected static string InternalBuildUrlUsingBasePath(string action, string area, string basePath, string controller)
583 string path;
585 basePath = basePath[basePath.Length - 1] == '/' ? basePath.Substring(0, basePath.Length - 1) : basePath;
587 if (basePath.EndsWith(area))
589 path = basePath + "/" + controller + "/" + action;
591 else
593 path = basePath + "/" + area + "/" + controller + "/" + action;
596 return path;