1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
18 using System
.Collections
.Generic
;
19 using System
.Collections
.Specialized
;
21 using Castle
.MonoRail
.Framework
.Internal
;
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
;
36 /// Initializes a new instance of the <see cref="UrlParts"/> class.
38 /// <param name="pathPieces">The path pieces.</param>
39 public UrlParts(params string[] pathPieces
)
41 url
= new StringBuilder();
43 AppendPaths(pathPieces
);
49 /// <param name="url">The URL.</param>
50 /// <returns></returns>
51 public static UrlParts
Parse(string url
)
55 throw new ArgumentNullException("url");
58 Uri uri
= new Uri(url
, UriKind
.RelativeOrAbsolute
);
60 if (uri
.IsAbsoluteUri
)
62 return CreateForAbsolutePath(uri
);
66 return CreateForRelativePath(url
);
71 /// Gets the path info builder.
73 /// <value>The path info.</value>
74 public PathInfoBuilder PathInfo
78 if (pathInfoBuilder
== null)
80 pathInfoBuilder
= new PathInfoBuilder(this);
82 return pathInfoBuilder
;
87 /// Gets the path info builder with dictionary api.
89 /// <value>The path info.</value>
90 public PathInfoDictBuilder PathInfoDict
94 if (pathInfoDictBuilder
== null)
96 pathInfoDictBuilder
= new PathInfoDictBuilder(this);
98 return pathInfoDictBuilder
;
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
;
121 /// <value>The query string.</value>
122 public NameValueCollection QueryString
126 if (queryStringDict
== null)
128 queryStringDict
= CreateQueryStringNameValueCollection(queryString
);
131 return queryStringDict
;
138 /// <returns></returns>
139 public string QueryStringAsString()
141 if (queryStringDict
!= null)
143 queryString
= CommonUtils
.BuildQueryString(queryStringDict
);
152 /// <returns></returns>
153 public UrlParts
ConvertPathInfoToDict()
155 if (pathInfoBuilder
== null)
160 PathInfoDict
.Parse(pathInfoBuilder
.ToString());
162 pathInfoBuilder
= null;
170 /// <returns></returns>
171 public string BuildPath()
173 StringBuilder sb
= new StringBuilder(url
.ToString());
177 if (queryStringDict
!= null && queryStringDict
.Count
!= 0)
180 sb
.Append(QueryStringAsString());
182 else if (!string.IsNullOrEmpty(queryString
))
185 sb
.Append(queryString
);
188 return sb
.ToString();
194 /// <returns></returns>
195 public string BuildPathForLink(IServerUtility serverUtiliy
)
197 StringBuilder sb
= new StringBuilder(url
.ToString());
201 if (queryStringDict
!= null && queryStringDict
.Count
!= 0)
204 sb
.Append(CommonUtils
.BuildQueryString(serverUtiliy
, QueryString
, true));
206 else if (!string.IsNullOrEmpty(queryString
))
209 sb
.Append(serverUtiliy
.HtmlEncode(queryString
));
212 return sb
.ToString();
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
)
232 if (!piece
.StartsWith("/") && HasLastChar
&& LastChar
!= '/')
240 if (HasLastChar
&& piece
.IndexOf('.') != -1) // this is fragile!
242 nextPathBelongsToPathInfo
= true;
248 private void AppendPaths(string[] pieces
)
250 foreach (string piece
in pieces
)
256 private bool HasLastChar
258 get { return url.Length != 0; }
261 private char LastChar
267 return url
[url
.Length
- 1];
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
)
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
);
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
);
336 private static NameValueCollection
CreateQueryStringNameValueCollection(string queryString
)
338 NameValueCollection coll
= new NameValueCollection(StringComparer
.InvariantCultureIgnoreCase
);
340 if (queryString
== null)
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
);
365 public class PathInfoBuilder
367 private readonly UrlParts parent
;
368 private readonly List
<string> pieces
;
371 /// Initializes a new instance of the <see cref="PathInfoBuilder"/> class.
373 /// <param name="parent">The parent.</param>
374 public PathInfoBuilder(UrlParts parent
)
376 this.parent
= parent
;
377 pieces
= new List
<string>();
381 /// Adds a path info piece.
383 /// <param name="pathInfoPiece">The path info piece.</param>
384 /// <returns></returns>
385 public PathInfoBuilder
Add(string pathInfoPiece
)
387 pieces
.Add(pathInfoPiece
);
392 /// Returns to the previous builder context.
396 get { return parent; }
400 /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
404 /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
406 public override string ToString()
408 StringBuilder sb
= new StringBuilder();
410 pieces
.ForEach(delegate(string piece
) { sb.Append(piece).Append('/'); }
);
412 return sb
.ToString();
416 /// Builds the specified URL.
418 /// <param name="url">The URL.</param>
419 protected internal void Build(StringBuilder url
)
421 if (pieces
.Count
== 0)
426 foreach(string piece
in pieces
)
428 if (url
[url
.Length
- 1] != '/')
441 public class PathInfoDictBuilder
443 private readonly UrlParts parent
;
444 private readonly IDictionary
<string, string> parameters
;
447 /// Initializes a new instance of the <see cref="PathInfoBuilder"/> class.
449 /// <param name="parent">The parent.</param>
450 public PathInfoDictBuilder(UrlParts parent
)
452 this.parent
= parent
;
453 parameters
= new Dictionary
<string, string>(StringComparer
.InvariantCultureIgnoreCase
);
457 /// Gets or sets the <see cref="System.String"/> with the specified key.
460 public string this[string key
]
462 get { return parameters[key]; }
463 set { parameters[key] = value; }
469 /// <value>The count.</value>
472 get { return parameters.Count; }
476 /// Adds a path info piece.
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;
488 /// Parses the specified path info.
490 /// <param name="pathInfo">The path info.</param>
491 public void Parse(string pathInfo
)
493 if (string.IsNullOrEmpty(pathInfo
))
500 foreach(string piece
in pathInfo
.Split(new char[] { '/' }
, StringSplitOptions
.RemoveEmptyEntries
))
515 this[key
] = string.Empty
;
521 /// Returns to the previous builder context.
525 get { return parent; }
529 /// Builds the specified URL.
531 /// <param name="url">The URL.</param>
532 protected internal void Build(StringBuilder url
)
534 if (parameters
.Count
== 0)
539 foreach(KeyValuePair
<string, string> pair
in parameters
)
541 if (url
[url
.Length
- 1] != '/')
546 url
.Append(pair
.Key
);
548 if (pair
.Value
!= string.Empty
)
551 url
.Append(pair
.Value
);