Fixing an issue with output parameters that are of type IntPtr
[castle.git] / MonoRail / Castle.MonoRail.Framework / Services / UrlParts.cs
blob03b5197032fcb64dbf2074353663caa745adfd04
1 // Copyright 2004-2008 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.Generic;
19 using System.Collections.Specialized;
20 using System.Text;
21 using Castle.MonoRail.Framework.Internal;
23 /// <summary>
24 /// Pendent
25 /// </summary>
26 public class UrlParts
28 private readonly StringBuilder url;
29 private PathInfoBuilder pathInfoBuilder;
30 private PathInfoDictBuilder pathInfoDictBuilder;
31 private bool nextPathBelongsToPathInfo;
32 private string queryString;
33 private NameValueCollection queryStringDict;
35 /// <summary>
36 /// Initializes a new instance of the <see cref="UrlParts"/> class.
37 /// </summary>
38 /// <param name="pathPieces">The path pieces.</param>
39 public UrlParts(params string[] pathPieces)
41 url = new StringBuilder();
43 AppendPaths(pathPieces);
46 /// <summary>
47 /// Pendent
48 /// </summary>
49 /// <param name="url">The URL.</param>
50 /// <returns></returns>
51 public static UrlParts Parse(string url)
53 if (url == null)
55 throw new ArgumentNullException("url");
58 Uri uri = new Uri(url, UriKind.RelativeOrAbsolute);
60 if (uri.IsAbsoluteUri)
62 return CreateForAbsolutePath(uri);
64 else
66 return CreateForRelativePath(url);
70 /// <summary>
71 /// Gets the path info builder.
72 /// </summary>
73 /// <value>The path info.</value>
74 public PathInfoBuilder PathInfo
76 get
78 if (pathInfoBuilder == null)
80 pathInfoBuilder = new PathInfoBuilder(this);
82 return pathInfoBuilder;
86 /// <summary>
87 /// Gets the path info builder with dictionary api.
88 /// </summary>
89 /// <value>The path info.</value>
90 public PathInfoDictBuilder PathInfoDict
92 get
94 if (pathInfoDictBuilder == null)
96 pathInfoDictBuilder = new PathInfoDictBuilder(this);
98 return pathInfoDictBuilder;
102 /// <summary>
103 /// Pendent
104 /// </summary>
105 /// <param name="queryStringParam">The query string.</param>
106 public UrlParts SetQueryString(string queryStringParam)
108 if (queryStringParam != null && queryStringParam.StartsWith("?"))
110 queryStringParam = queryStringParam.Substring(1);
113 queryString = queryStringParam;
115 return this;
118 /// <summary>
119 /// Pendent
120 /// </summary>
121 /// <value>The query string.</value>
122 public NameValueCollection QueryString
126 if (queryStringDict == null)
128 queryStringDict = CreateQueryStringNameValueCollection(queryString);
131 return queryStringDict;
135 /// <summary>
136 /// Pendent
137 /// </summary>
138 /// <returns></returns>
139 public string QueryStringAsString()
141 if (queryStringDict != null)
143 queryString = CommonUtils.BuildQueryString(queryStringDict);
146 return queryString;
149 /// <summary>
150 /// Pendent
151 /// </summary>
152 /// <returns></returns>
153 public UrlParts ConvertPathInfoToDict()
155 if (pathInfoBuilder == null)
157 return this;
160 PathInfoDict.Parse(pathInfoBuilder.ToString());
162 pathInfoBuilder = null;
164 return this;
167 /// <summary>
168 /// Builds the path.
169 /// </summary>
170 /// <returns></returns>
171 public string BuildPath()
173 StringBuilder sb = new StringBuilder(url.ToString());
175 BuildPathInfo(sb);
177 if (queryStringDict != null && queryStringDict.Count != 0)
179 sb.Append('?');
180 sb.Append(QueryStringAsString());
182 else if (!string.IsNullOrEmpty(queryString))
184 sb.Append('?');
185 sb.Append(queryString);
188 return sb.ToString();
191 /// <summary>
192 /// Builds the path.
193 /// </summary>
194 /// <returns></returns>
195 public string BuildPathForLink(IServerUtility serverUtiliy)
197 StringBuilder sb = new StringBuilder(url.ToString());
199 BuildPathInfo(sb);
201 if (queryStringDict != null && queryStringDict.Count != 0)
203 sb.Append('?');
204 sb.Append(CommonUtils.BuildQueryString(serverUtiliy, QueryString, true));
206 else if (!string.IsNullOrEmpty(queryString))
208 sb.Append('?');
209 sb.Append(serverUtiliy.HtmlEncode(queryString));
212 return sb.ToString();
215 /// <summary>
216 /// Pendent
217 /// </summary>
218 /// <param name="piece">The piece.</param>
219 public UrlParts AppendPath(string piece)
221 if (piece.Length != 1 && piece.EndsWith("/"))
223 piece = piece.Substring(0, piece.Length - 1);
226 if (nextPathBelongsToPathInfo)
228 PathInfo.Add(piece);
230 else
232 if (!piece.StartsWith("/") && HasLastChar && LastChar != '/')
234 url.Append('/');
237 url.Append(piece);
240 if (HasLastChar && piece.IndexOf('.') != -1) // this is fragile!
242 nextPathBelongsToPathInfo = true;
245 return this;
248 private void AppendPaths(string[] pieces)
250 foreach (string piece in pieces)
252 AppendPath(piece);
256 private bool HasLastChar
258 get { return url.Length != 0; }
261 private char LastChar
265 if (url.Length != 0)
267 return url[url.Length - 1];
270 return '\0';
274 private void BuildPathInfo(StringBuilder sb)
276 if (pathInfoBuilder != null)
278 pathInfoBuilder.Build(sb);
280 if (pathInfoDictBuilder != null)
282 pathInfoDictBuilder.Build(sb);
286 private static UrlParts CreateForRelativePath(string url)
288 string path = url;
289 string qs = null;
290 string pathInfo = null;
292 int queryStringStartIndex = url.IndexOf('?');
293 int fileExtIndex = url.IndexOf('.');
295 if (queryStringStartIndex != -1)
297 qs = url.Substring(queryStringStartIndex);
298 path = url.Substring(0, queryStringStartIndex);
301 if (fileExtIndex != -1)
303 int pathInfoStartIndex = path.IndexOf('/', fileExtIndex);
305 if (pathInfoStartIndex != -1)
307 pathInfo = path.Substring(pathInfoStartIndex);
308 path = path.Substring(0, pathInfoStartIndex);
312 UrlParts parts = new UrlParts(path);
313 parts.SetQueryString(qs);
314 parts.PathInfoDict.Parse(pathInfo);
316 return parts;
319 private static UrlParts CreateForAbsolutePath(Uri uri)
321 string host = uri.AbsoluteUri.Substring(0, uri.AbsoluteUri.Length - uri.PathAndQuery.Length);
323 UrlParts parts = new UrlParts(host);
325 foreach (string segment in uri.Segments)
327 parts.AppendPath(segment);
330 parts.ConvertPathInfoToDict();
331 parts.SetQueryString(uri.Query);
333 return parts;
336 private static NameValueCollection CreateQueryStringNameValueCollection(string queryString)
338 NameValueCollection coll = new NameValueCollection(StringComparer.InvariantCultureIgnoreCase);
340 if (queryString == null)
342 return coll;
345 foreach(string valuePair in queryString.Split('&'))
347 string[] pairs = valuePair.Split(new char[] { '=' }, 2);
349 if (pairs.Length == 2)
351 coll.Add(pairs[0], pairs[1]);
353 else if (pairs.Length == 1)
355 coll.Add(pairs[0], string.Empty);
359 return coll;
362 /// <summary>
363 /// Pendent
364 /// </summary>
365 public class PathInfoBuilder
367 private readonly UrlParts parent;
368 private readonly List<string> pieces;
370 /// <summary>
371 /// Initializes a new instance of the <see cref="PathInfoBuilder"/> class.
372 /// </summary>
373 /// <param name="parent">The parent.</param>
374 public PathInfoBuilder(UrlParts parent)
376 this.parent = parent;
377 pieces = new List<string>();
380 /// <summary>
381 /// Adds a path info piece.
382 /// </summary>
383 /// <param name="pathInfoPiece">The path info piece.</param>
384 /// <returns></returns>
385 public PathInfoBuilder Add(string pathInfoPiece)
387 pieces.Add(pathInfoPiece);
388 return this;
391 /// <summary>
392 /// Returns to the previous builder context.
393 /// </summary>
394 public UrlParts Done
396 get { return parent; }
399 /// <summary>
400 /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
401 /// </summary>
403 /// <returns>
404 /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
405 /// </returns>
406 public override string ToString()
408 StringBuilder sb = new StringBuilder();
410 pieces.ForEach(delegate(string piece) { sb.Append(piece).Append('/'); });
412 return sb.ToString();
415 /// <summary>
416 /// Builds the specified URL.
417 /// </summary>
418 /// <param name="url">The URL.</param>
419 protected internal void Build(StringBuilder url)
421 if (pieces.Count == 0)
423 return;
426 foreach(string piece in pieces)
428 if (url[url.Length - 1] != '/')
430 url.Append('/');
433 url.Append(piece);
438 /// <summary>
439 /// Pendent
440 /// </summary>
441 public class PathInfoDictBuilder
443 private readonly UrlParts parent;
444 private readonly IDictionary<string, string> parameters;
446 /// <summary>
447 /// Initializes a new instance of the <see cref="PathInfoBuilder"/> class.
448 /// </summary>
449 /// <param name="parent">The parent.</param>
450 public PathInfoDictBuilder(UrlParts parent)
452 this.parent = parent;
453 parameters = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
456 /// <summary>
457 /// Gets or sets the <see cref="System.String"/> with the specified key.
458 /// </summary>
459 /// <value></value>
460 public string this[string key]
462 get { return parameters[key]; }
463 set { parameters[key] = value; }
466 /// <summary>
467 /// Gets the count.
468 /// </summary>
469 /// <value>The count.</value>
470 public int Count
472 get { return parameters.Count; }
475 /// <summary>
476 /// Adds a path info piece.
477 /// </summary>
478 /// <param name="key">The key.</param>
479 /// <param name="value">The value.</param>
480 /// <returns></returns>
481 public PathInfoDictBuilder Add(string key, string value)
483 parameters[key] = value;
484 return this;
487 /// <summary>
488 /// Parses the specified path info.
489 /// </summary>
490 /// <param name="pathInfo">The path info.</param>
491 public void Parse(string pathInfo)
493 if (string.IsNullOrEmpty(pathInfo))
495 return;
498 string key = null;
500 foreach(string piece in pathInfo.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries))
502 if (key == null)
504 key = piece;
506 else
508 this[key] = piece;
509 key = null;
513 if (key != null)
515 this[key] = string.Empty;
520 /// <summary>
521 /// Returns to the previous builder context.
522 /// </summary>
523 public UrlParts Done
525 get { return parent; }
528 /// <summary>
529 /// Builds the specified URL.
530 /// </summary>
531 /// <param name="url">The URL.</param>
532 protected internal void Build(StringBuilder url)
534 if (parameters.Count == 0)
536 return;
539 foreach(KeyValuePair<string, string> pair in parameters)
541 if (url[url.Length - 1] != '/')
543 url.Append('/');
546 url.Append(pair.Key);
548 if (pair.Value != string.Empty)
550 url.Append('/');
551 url.Append(pair.Value);