1 // Copyright 2004-2007 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
18 using System
.Collections
.Generic
;
19 using System
.Collections
.Specialized
;
26 public class UrlPartsBuilder
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="UrlPartsBuilder"/> class.
38 /// <param name="pathPieces">The path pieces.</param>
39 public UrlPartsBuilder(params string[] pathPieces
)
41 url
= new StringBuilder();
43 AppendPaths(pathPieces
);
49 /// <param name="url">The URL.</param>
50 /// <returns></returns>
51 public static UrlPartsBuilder
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 UrlPartsBuilder
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 UrlPartsBuilder
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));
207 return sb
.ToString();
213 /// <param name="piece">The piece.</param>
214 public UrlPartsBuilder
AppendPath(string piece
)
216 if (piece
.EndsWith("/"))
218 piece
= piece
.Substring(0, piece
.Length
- 1);
221 if (nextPathBelongsToPathInfo
)
227 if (!piece
.StartsWith("/") && HasLastChar
&& LastChar
!= '/')
235 if (HasLastChar
&& piece
.IndexOf('.') != -1) // this is fragile!
237 nextPathBelongsToPathInfo
= true;
243 private void AppendPaths(string[] pieces
)
245 foreach (string piece
in pieces
)
251 private bool HasLastChar
253 get { return url.Length != 0; }
256 private char LastChar
262 return url
[url
.Length
- 1];
269 private void BuildPathInfo(StringBuilder sb
)
271 if (pathInfoBuilder
!= null)
273 pathInfoBuilder
.Build(sb
);
275 if (pathInfoDictBuilder
!= null)
277 pathInfoDictBuilder
.Build(sb
);
281 private static UrlPartsBuilder
CreateForRelativePath(string url
)
285 string pathInfo
= null;
287 int queryStringStartIndex
= url
.IndexOf('?');
288 int fileExtIndex
= url
.IndexOf('.');
290 if (queryStringStartIndex
!= -1)
292 qs
= url
.Substring(queryStringStartIndex
);
293 path
= url
.Substring(0, queryStringStartIndex
);
296 if (fileExtIndex
!= -1)
298 int pathInfoStartIndex
= path
.IndexOf('/', fileExtIndex
);
300 if (pathInfoStartIndex
!= -1)
302 pathInfo
= path
.Substring(pathInfoStartIndex
);
303 path
= path
.Substring(0, pathInfoStartIndex
);
307 UrlPartsBuilder parts
= new UrlPartsBuilder(path
);
308 parts
.SetQueryString(qs
);
309 parts
.PathInfoDict
.Parse(pathInfo
);
314 private static UrlPartsBuilder
CreateForAbsolutePath(Uri uri
)
316 string host
= uri
.AbsoluteUri
.Substring(0, uri
.AbsoluteUri
.Length
- uri
.PathAndQuery
.Length
);
318 UrlPartsBuilder parts
= new UrlPartsBuilder(host
);
320 foreach (string segment
in uri
.Segments
)
322 parts
.AppendPath(segment
);
325 parts
.ConvertPathInfoToDict();
326 parts
.SetQueryString(uri
.Query
);
331 private static NameValueCollection
CreateQueryStringNameValueCollection(string queryString
)
333 NameValueCollection coll
= new NameValueCollection(StringComparer
.InvariantCultureIgnoreCase
);
335 if (queryString
== null)
340 foreach(string valuePair
in queryString
.Split('&'))
342 string[] pairs
= valuePair
.Split(new char[] { '=' }
, 2);
344 if (pairs
.Length
== 2)
346 coll
.Add(pairs
[0], pairs
[1]);
348 else if (pairs
.Length
== 1)
350 coll
.Add(pairs
[0], string.Empty
);
360 public class PathInfoBuilder
362 private readonly UrlPartsBuilder parent
;
363 private readonly List
<string> pieces
;
366 /// Initializes a new instance of the <see cref="PathInfoBuilder"/> class.
368 /// <param name="parent">The parent.</param>
369 public PathInfoBuilder(UrlPartsBuilder parent
)
371 this.parent
= parent
;
372 pieces
= new List
<string>();
376 /// Adds a path info piece.
378 /// <param name="pathInfoPiece">The path info piece.</param>
379 /// <returns></returns>
380 public PathInfoBuilder
Add(string pathInfoPiece
)
382 pieces
.Add(pathInfoPiece
);
387 /// Returns to the previous builder context.
389 public UrlPartsBuilder Done
391 get { return parent; }
395 /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
399 /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
401 public override string ToString()
403 StringBuilder sb
= new StringBuilder();
405 pieces
.ForEach(delegate(string piece
) { sb.Append(piece).Append('/'); }
);
407 return sb
.ToString();
411 /// Builds the specified URL.
413 /// <param name="url">The URL.</param>
414 protected internal void Build(StringBuilder url
)
416 if (pieces
.Count
== 0)
421 foreach(string piece
in pieces
)
423 if (url
[url
.Length
- 1] != '/')
436 public class PathInfoDictBuilder
438 private readonly UrlPartsBuilder parent
;
439 private readonly IDictionary
<string, string> parameters
;
442 /// Initializes a new instance of the <see cref="PathInfoBuilder"/> class.
444 /// <param name="parent">The parent.</param>
445 public PathInfoDictBuilder(UrlPartsBuilder parent
)
447 this.parent
= parent
;
448 parameters
= new Dictionary
<string, string>(StringComparer
.InvariantCultureIgnoreCase
);
452 /// Gets or sets the <see cref="System.String"/> with the specified key.
455 public string this[string key
]
457 get { return parameters[key]; }
458 set { parameters[key] = value; }
464 /// <value>The count.</value>
467 get { return parameters.Count; }
471 /// Adds a path info piece.
473 /// <param name="key">The key.</param>
474 /// <param name="value">The value.</param>
475 /// <returns></returns>
476 public PathInfoDictBuilder
Add(string key
, string value)
478 parameters
[key
] = value;
483 /// Parses the specified path info.
485 /// <param name="pathInfo">The path info.</param>
486 public void Parse(string pathInfo
)
488 if (string.IsNullOrEmpty(pathInfo
))
495 foreach(string piece
in pathInfo
.Split(new char[] { '/' }
, StringSplitOptions
.RemoveEmptyEntries
))
510 this[key
] = string.Empty
;
516 /// Returns to the previous builder context.
518 public UrlPartsBuilder Done
520 get { return parent; }
524 /// Builds the specified URL.
526 /// <param name="url">The URL.</param>
527 protected internal void Build(StringBuilder url
)
529 if (parameters
.Count
== 0)
534 foreach(KeyValuePair
<string, string> pair
in parameters
)
536 if (url
[url
.Length
- 1] != '/')
541 url
.Append(pair
.Key
);
543 if (pair
.Value
!= string.Empty
)
546 url
.Append(pair
.Value
);