Merge pull request #1502 from madrang/SafeHandle.CloseTestDispose
[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 (uri == null)
78                                 throw new ArgumentNullException ("uri");
79                         Initialize (uri);
80                 }
81                 
82                 public UriBuilder (string schemeName, string hostName) 
83                 {
84                         Initialize (schemeName, hostName, -1, String.Empty, String.Empty);
85                 }
86
87                 public UriBuilder (string scheme, string host, int portNumber) 
88                 {
89                         Initialize (scheme, host, portNumber, String.Empty, String.Empty);
90                 }
91                 
92                 public UriBuilder (string scheme, string host, int port, string pathValue)
93                 {
94                         Initialize (scheme, host, port, pathValue, String.Empty);
95                 }
96
97                 public UriBuilder (string scheme, string host, int port, string path, string extraValue)
98                 {
99                         Initialize (scheme, host, port, path, extraValue);
100                 }
101
102                 private void Initialize (Uri uri)
103                 {
104                         Initialize (uri.Scheme, uri.Host, uri.Port, uri.AbsolutePath, String.Empty);
105                         fragment = uri.Fragment;
106                         query = uri.Query;
107                         username = uri.UserInfo;
108                         int pos = username.IndexOf (':');
109                         if (pos != -1) {
110                                 password = username.Substring (pos + 1);
111                                 username = username.Substring (0, pos);
112                         } else {
113                                 password = String.Empty;
114                         }
115                 }
116
117                 private void Initialize (string scheme, string host, int port, string pathValue, string extraValue)
118                 {
119                         modified = true;
120
121                         Scheme = scheme;
122                         Host = host;
123                         Port = port;
124                         Path = pathValue;
125                         query = String.Empty;
126                         fragment = String.Empty;
127                         Path = pathValue;
128                         username = String.Empty;
129                         password = String.Empty;
130
131                         if (String.IsNullOrEmpty (extraValue))
132                                 return;
133
134                         if (extraValue [0] == '#')
135                                 Fragment = extraValue.Remove (0, 1);
136                         else if (extraValue [0] == '?')
137                                 Query = extraValue.Remove (0, 1);
138                         else
139                                 throw new ArgumentException ("extraValue");
140                 }
141                 
142                 // Properties
143                 
144                 public string Fragment {
145                         get { return fragment; }
146                         set {
147                                 fragment = value;
148                                 if (fragment == null)
149                                         fragment = String.Empty;
150                                 else if (fragment.Length > 0)
151                                         fragment = "#" + value.Replace ("%23", "#");
152                                 modified = true;
153                         }
154                 }
155
156                 public string Host {
157                         get { return host; }
158                         set {
159                                 if (String.IsNullOrEmpty (value))
160                                         host = String.Empty;
161                                 else if ((value.IndexOf (':') != -1) && (value [0] != '[')) {
162                                         host = "[" + value + "]";
163                                 } else {
164                                         host = value;
165                                 }
166                                 modified = true;
167                         }
168                 }
169
170                 public string Password {
171                         get { return password; }
172                         set {
173                                 password = (value == null) ? String.Empty : value;
174                         }
175                 }
176                 
177                 public string Path {
178                         get { return path; }
179                         set {
180                                 if (value == null || value.Length == 0) {
181                                         path = "/";
182                                 } else {
183                                         path = Uri.EscapeString (value.Replace ('\\', '/'), Uri.EscapeCommonHexBracketsQuery);
184                                 }
185                                 modified = true;
186                         }
187                 }
188                 
189                 public int Port {
190                         get { return port; }
191                         set {
192                                 if (value < -1)
193                                         throw new ArgumentOutOfRangeException ("value");
194                                 // apparently it is
195                                 port = value;
196                                 modified = true;
197                         }
198                 }
199                 
200                 public string Query {
201                         get { return query; }
202                         set {
203                                 // LAMESPEC: it doesn't say to always prepend a 
204                                 // question mark to the value.. it does say this 
205                                 // for fragment.
206                                 if (value == null || value.Length == 0)
207                                         query = String.Empty;
208                                 else
209                                         query = "?" + value;
210                                 modified = true;
211                         }
212                 }
213                 
214                 public string Scheme {
215                         get { return scheme; }
216                         set {
217                                 if (value == null)
218                                         value = String.Empty;
219                                 int colonPos = value.IndexOf (':');
220                                 if (colonPos != -1)
221                                         value = value.Substring (0, colonPos);
222                                 scheme = value.ToLower ();
223                                 modified = true;
224                         }
225                 }
226                 
227                 public Uri Uri {
228                         get {
229                                 if (!modified) 
230                                         return uri;
231                                 uri = new Uri (ToString ());
232                                 // some properties are updated once the Uri is created - see unit tests
233                                 host = uri.Host;
234                                 path = uri.AbsolutePath;
235                                 modified = false;
236                                 return uri;
237                         }
238                 }
239                 
240                 public string UserName {
241                         get { return username; }
242                         set {
243                                 username = (value == null) ? String.Empty : value;
244                                 modified = true;
245                         }
246                 }
247
248                 // Methods
249                 
250                 public override bool Equals (object rparam) 
251                 {
252                         return (rparam == null) ? false : this.Uri.Equals (rparam.ToString ());
253                 }
254                 
255                 public override int GetHashCode ()
256                 {
257                         return this.Uri.GetHashCode ();
258                 }
259                 
260                 public override string ToString ()
261                 {
262                         StringBuilder builder = new StringBuilder ();
263
264                         builder.Append (scheme);
265                         // note: mailto and news use ':', not "://", as their delimiter
266                         builder.Append (Uri.GetSchemeDelimiter (scheme));
267
268                         if (username != String.Empty) {
269                                 builder.Append (username);
270                                 if (password != String.Empty)
271                                         builder.Append (":" + password);
272                                 builder.Append ('@');
273                         }
274
275                         if (host.Length > 0) {
276                                 builder.Append (host);
277                                 if (port > 0)
278                                         builder.Append (":" + port);
279                         }
280
281                         if (path != String.Empty &&
282                             builder [builder.Length - 1] != '/' &&
283                             path.Length > 0 && path [0] != '/')
284                                 builder.Append ('/');
285                         builder.Append (path);
286                         builder.Append (query);
287                         builder.Append (fragment);
288
289                         return builder.ToString ();
290                 }
291         }
292 }