Merge pull request #309 from i59/patch-1
[mono.git] / mcs / class / System / System / UriBuilder.cs
1 //
2 // System.UriBuilder
3 //
4 // Author:
5 //   Lawrence Pit (loz@cable.a2000.nl)
6 //
7 // Copyright (C) 2005, 2010 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections;
31 using System.Runtime.Serialization;
32 using System.Text;
33
34 // See RFC 2396 for more info on URI's.
35
36 namespace System 
37 {
38         public class UriBuilder
39         {
40                 private string scheme;
41                 private string host;
42                 private int port;
43                 private string path;
44                 private string query;
45                 private string fragment;
46                 private string username;
47                 private string password;
48                 
49                 private Uri uri;
50                 private bool modified;
51                 
52                 
53                 // Constructors
54                 
55                 public UriBuilder ()
56                 {
57                         Initialize (Uri.UriSchemeHttp, "localhost", -1, String.Empty, String.Empty);
58                 }
59
60                 public UriBuilder (string uri)
61                 {
62                         if (uri == null)
63                                 throw new ArgumentNullException ("uriString");
64
65                         Uri u = null;
66                         if (Uri.TryCreate (uri, UriKind.Absolute, out u)) {
67                                 Initialize (u);
68                         } else if (!uri.Contains (Uri.SchemeDelimiter)) {
69                                 // second chance, UriBuilder parsing is more forgiving than Uri
70                                 Initialize (new Uri (Uri.UriSchemeHttp + Uri.SchemeDelimiter + uri));
71                         } else
72                                 throw new UriFormatException ();
73                 }
74                 
75                 public UriBuilder (Uri uri)
76                 {
77 #if NET_4_0
78                         if (uri == null)
79                                 throw new ArgumentNullException ("uri");
80 #endif
81                         Initialize (uri);
82                 }
83                 
84                 public UriBuilder (string schemeName, string hostName) 
85                 {
86                         Initialize (schemeName, hostName, -1, String.Empty, String.Empty);
87                 }
88
89                 public UriBuilder (string scheme, string host, int portNumber) 
90                 {
91                         Initialize (scheme, host, portNumber, String.Empty, String.Empty);
92                 }
93                 
94                 public UriBuilder (string scheme, string host, int port, string pathValue)
95                 {
96                         Initialize (scheme, host, port, pathValue, String.Empty);
97                 }
98
99                 public UriBuilder (string scheme, string host, int port, string path, string extraValue)
100                 {
101                         Initialize (scheme, host, port, path, extraValue);
102                 }
103
104                 private void Initialize (Uri uri)
105                 {
106                         Initialize (uri.Scheme, uri.Host, uri.Port, uri.AbsolutePath, String.Empty);
107                         fragment = uri.Fragment;
108                         query = uri.Query;
109                         username = uri.UserInfo;
110                         int pos = username.IndexOf (':');
111                         if (pos != -1) {
112                                 password = username.Substring (pos + 1);
113                                 username = username.Substring (0, pos);
114                         } else {
115                                 password = String.Empty;
116                         }
117                 }
118
119                 private void Initialize (string scheme, string host, int port, string pathValue, string extraValue)
120                 {
121                         modified = true;
122
123                         Scheme = scheme;
124                         Host = host;
125                         Port = port;
126                         Path = pathValue;
127                         query = String.Empty;
128                         fragment = String.Empty;
129                         Path = pathValue;
130                         username = String.Empty;
131                         password = String.Empty;
132
133                         if (String.IsNullOrEmpty (extraValue))
134                                 return;
135
136                         if (extraValue [0] == '#')
137                                 Fragment = extraValue.Remove (0, 1);
138                         else if (extraValue [0] == '?')
139                                 Query = extraValue.Remove (0, 1);
140                         else
141                                 throw new ArgumentException ("extraValue");
142                 }
143                 
144                 // Properties
145                 
146                 public string Fragment {
147                         get { return fragment; }
148                         set {
149                                 fragment = value;
150                                 if (fragment == null)
151                                         fragment = String.Empty;
152                                 else if (fragment.Length > 0)
153                                         fragment = "#" + value.Replace ("%23", "#");
154                                 modified = true;
155                         }
156                 }
157
158                 public string Host {
159                         get { return host; }
160                         set {
161                                 if (String.IsNullOrEmpty (value))
162                                         host = String.Empty;
163                                 else if ((value.IndexOf (':') != -1) && (value [0] != '[')) {
164                                         host = "[" + value + "]";
165                                 } else {
166                                         host = value;
167                                 }
168                                 modified = true;
169                         }
170                 }
171
172                 public string Password {
173                         get { return password; }
174                         set {
175                                 password = (value == null) ? String.Empty : value;
176                         }
177                 }
178                 
179                 public string Path {
180                         get { return path; }
181                         set {
182                                 if (value == null || value.Length == 0) {
183                                         path = "/";
184                                 } else {
185                                         path = Uri.EscapeString (value.Replace ('\\', '/'), Uri.EscapeCommonHexBracketsQuery);
186                                 }
187                                 modified = true;
188                         }
189                 }
190                 
191                 public int Port {
192                         get { return port; }
193                         set {
194                                 if (value < -1)
195                                         throw new ArgumentOutOfRangeException ("value");
196                                 // apparently it is
197                                 port = value;
198                                 modified = true;
199                         }
200                 }
201                 
202                 public string Query {
203                         get { return query; }
204                         set {
205                                 // LAMESPEC: it doesn't say to always prepend a 
206                                 // question mark to the value.. it does say this 
207                                 // for fragment.
208                                 if (value == null || value.Length == 0)
209                                         query = String.Empty;
210                                 else
211                                         query = "?" + value;
212                                 modified = true;
213                         }
214                 }
215                 
216                 public string Scheme {
217                         get { return scheme; }
218                         set {
219                                 if (value == null)
220                                         value = String.Empty;
221                                 int colonPos = value.IndexOf (':');
222                                 if (colonPos != -1)
223                                         value = value.Substring (0, colonPos);
224                                 scheme = value.ToLower ();
225                                 modified = true;
226                         }
227                 }
228                 
229                 public Uri Uri {
230                         get {
231                                 if (!modified) 
232                                         return uri;
233                                 uri = new Uri (ToString ());
234                                 // some properties are updated once the Uri is created - see unit tests
235                                 host = uri.Host;
236                                 path = uri.AbsolutePath;
237                                 modified = false;
238                                 return uri;
239                         }
240                 }
241                 
242                 public string UserName {
243                         get { return username; }
244                         set {
245                                 username = (value == null) ? String.Empty : value;
246                                 modified = true;
247                         }
248                 }
249
250                 // Methods
251                 
252                 public override bool Equals (object rparam) 
253                 {
254                         return (rparam == null) ? false : this.Uri.Equals (rparam.ToString ());
255                 }
256                 
257                 public override int GetHashCode ()
258                 {
259                         return this.Uri.GetHashCode ();
260                 }
261                 
262                 public override string ToString ()
263                 {
264                         StringBuilder builder = new StringBuilder ();
265
266                         builder.Append (scheme);
267                         // note: mailto and news use ':', not "://", as their delimiter
268                         builder.Append (Uri.GetSchemeDelimiter (scheme));
269
270                         if (username != String.Empty) {
271                                 builder.Append (username);
272                                 if (password != String.Empty)
273                                         builder.Append (":" + password);
274                                 builder.Append ('@');
275                         }
276
277                         if (host.Length > 0) {
278                                 builder.Append (host);
279                                 if (port > 0)
280                                         builder.Append (":" + port);
281                         }
282
283                         if (path != String.Empty &&
284                             builder [builder.Length - 1] != '/' &&
285                             path.Length > 0 && path [0] != '/')
286                                 builder.Append ('/');
287                         builder.Append (path);
288                         builder.Append (query);
289                         builder.Append (fragment);
290
291                         return builder.ToString ();
292                 }
293         }
294 }