move FrameworkName from corlib to System
[mcs.git] / class / System / System / UriParser.cs
blob300dafba14e4417da6712c5314d4d3286fd59ab3
1 //
2 // System.UriParser abstract class
3 //
4 // Author:
5 // Sebastien Pouliot <sebastien@ximian.com>
6 //
7 // Copyright (C) 2005 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.
29 using System.Collections;
30 using System.Globalization;
31 using System.Security.Permissions;
32 using System.Text;
33 using System.Text.RegularExpressions;
35 namespace System {
36 #if NET_2_0
37 public
38 #endif
39 abstract class UriParser {
41 static object lock_object = new object ();
42 static Hashtable table;
44 internal string scheme_name;
45 private int default_port;
47 // Regexp from RFC 2396
48 #if NET_2_1
49 readonly static Regex uri_regex = new Regex (@"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?");
50 #else
51 // Groups: 12 3 4 5 6 7 8 9
52 readonly static Regex uri_regex = new Regex (@"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?", RegexOptions.Compiled);
53 #endif
55 // Groups: 12 3 4 5
56 readonly static Regex auth_regex = new Regex (@"^(([^@]+)@)?(.*?)(:([0-9]+))?$");
58 protected UriParser ()
62 static Match ParseAuthority (Group g)
64 return auth_regex.Match (g.Value);
67 // protected methods
68 protected internal virtual string GetComponents (Uri uri, UriComponents components, UriFormat format)
70 if ((format < UriFormat.UriEscaped) || (format > UriFormat.SafeUnescaped))
71 throw new ArgumentOutOfRangeException ("format");
73 Match m = uri_regex.Match (uri.OriginalString);
75 string scheme = scheme_name;
76 int dp = default_port;
78 if ((scheme == null) || (scheme == "*")) {
79 scheme = m.Groups [2].Value;
80 dp = Uri.GetDefaultPort (scheme);
81 } else if (String.Compare (scheme, m.Groups [2].Value, true) != 0) {
82 throw new SystemException ("URI Parser: scheme mismatch: " + scheme + " vs. " + m.Groups [2].Value);
85 // it's easier to answer some case directly (as the output isn't identical
86 // when mixed with others components, e.g. leading slash, # ...)
87 switch (components) {
88 case UriComponents.Scheme:
89 return scheme;
90 case UriComponents.UserInfo:
91 return ParseAuthority (m.Groups [4]).Groups [2].Value;
92 case UriComponents.Host:
93 return ParseAuthority (m.Groups [4]).Groups [3].Value;
94 case UriComponents.Port: {
95 string p = ParseAuthority (m.Groups [4]).Groups [5].Value;
96 if (p != null && p != String.Empty && p != dp.ToString ())
97 return p;
98 return String.Empty;
100 case UriComponents.Path:
101 return Format (IgnoreFirstCharIf (m.Groups [5].Value, '/'), format);
102 case UriComponents.Query:
103 return Format (m.Groups [7].Value, format);
104 case UriComponents.Fragment:
105 return Format (m.Groups [9].Value, format);
106 case UriComponents.StrongPort: {
107 Group g = ParseAuthority (m.Groups [4]).Groups [5];
108 return g.Success ? g.Value : dp.ToString ();
110 case UriComponents.SerializationInfoString:
111 components = UriComponents.AbsoluteUri;
112 break;
115 Match am = ParseAuthority (m.Groups [4]);
117 // now we deal with multiple flags...
119 StringBuilder sb = new StringBuilder ();
120 if ((components & UriComponents.Scheme) != 0) {
121 sb.Append (scheme);
122 sb.Append (Uri.GetSchemeDelimiter (scheme));
125 if ((components & UriComponents.UserInfo) != 0)
126 sb.Append (am.Groups [1].Value);
128 if ((components & UriComponents.Host) != 0)
129 sb.Append (am.Groups [3].Value);
131 // for StrongPort always show port - even if -1
132 // otherwise only display if ut's not the default port
133 if ((components & UriComponents.StrongPort) != 0) {
134 Group g = am.Groups [4];
135 sb.Append (g.Success ? g.Value : ":" + dp);
138 if ((components & UriComponents.Port) != 0) {
139 string p = am.Groups [5].Value;
140 if (p != null && p != String.Empty && p != dp.ToString ())
141 sb.Append (am.Groups [4].Value);
144 if ((components & UriComponents.Path) != 0)
145 sb.Append (m.Groups [5]);
147 if ((components & UriComponents.Query) != 0)
148 sb.Append (m.Groups [6]);
150 if ((components & UriComponents.Fragment) != 0)
151 sb.Append (m.Groups [8]);
153 return Format (sb.ToString (), format);
156 protected internal virtual void InitializeAndValidate (Uri uri, out UriFormatException parsingError)
158 // bad boy, it should check null arguments.
159 if ((uri.Scheme != scheme_name) && (scheme_name != "*"))
160 // Here .NET seems to return "The Authority/Host could not be parsed", but it does not make sense.
161 parsingError = new UriFormatException ("The argument Uri's scheme does not match");
162 else
163 parsingError = null;
166 #if NET_2_0
167 protected internal virtual bool IsBaseOf (Uri baseUri, Uri relativeUri)
169 // compare, not case sensitive, the scheme, host and port (+ user informations)
170 if (Uri.Compare (baseUri, relativeUri, UriComponents.SchemeAndServer | UriComponents.UserInfo, UriFormat.Unescaped, StringComparison.InvariantCultureIgnoreCase) != 0)
171 return false;
173 string base_string = baseUri.LocalPath;
174 int last_slash = base_string.LastIndexOf ('/') + 1; // keep the slash
175 return (String.Compare (base_string, 0, relativeUri.LocalPath, 0, last_slash, StringComparison.InvariantCultureIgnoreCase) == 0);
178 protected internal virtual bool IsWellFormedOriginalString (Uri uri)
180 // well formed according to RFC2396 and RFC2732
181 // see Uri.IsWellFormedOriginalString for some docs
183 // Though this class does not seem to do anything. Even null arguments aren't checked :/
184 return uri.IsWellFormedOriginalString ();
186 #endif
187 protected internal virtual UriParser OnNewUri ()
189 // nice time for init
190 return this;
193 [MonoTODO]
194 protected virtual void OnRegister (string schemeName, int defaultPort)
196 // unit tests shows that schemeName and defaultPort aren't usable from here
199 [MonoTODO]
200 protected internal virtual string Resolve (Uri baseUri, Uri relativeUri, out UriFormatException parsingError)
202 // used by Uri.ctor and Uri.TryCreate
203 throw new NotImplementedException ();
206 // internal properties
208 internal string SchemeName {
209 get { return scheme_name; }
210 set { scheme_name = value; }
213 internal int DefaultPort {
214 get { return default_port; }
215 set { default_port = value; }
218 // private stuff
220 private string IgnoreFirstCharIf (string s, char c)
222 if (s.Length == 0)
223 return String.Empty;
224 if (s[0] == c)
225 return s.Substring (1);
226 return s;
229 private string Format (string s, UriFormat format)
231 if (s.Length == 0)
232 return String.Empty;
234 switch (format) {
235 case UriFormat.UriEscaped:
236 return Uri.EscapeString (s, false, true, true);
237 case UriFormat.SafeUnescaped:
238 // TODO subset of escape rules
239 s = Uri.Unescape (s, false);
240 return s; //Uri.EscapeString (s, false, true, true);
241 case UriFormat.Unescaped:
242 return Uri.Unescape (s, false);
243 default:
244 throw new ArgumentOutOfRangeException ("format");
248 // static methods
250 private static void CreateDefaults ()
252 if (table != null)
253 return;
255 Hashtable newtable = new Hashtable ();
256 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeFile, -1);
257 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeFtp, 21);
258 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeGopher, 70);
259 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeHttp, 80);
260 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeHttps, 443);
261 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeMailto, 25);
262 #if NET_2_0
263 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNetPipe, -1);
264 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNetTcp, -1);
265 #endif
266 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNews, 119);
267 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNntp, 119);
268 // not defined in Uri.UriScheme* but a parser class exists
269 InternalRegister (newtable, new DefaultUriParser (), "ldap", 389);
271 lock (lock_object) {
272 if (table == null)
273 table = newtable;
274 else
275 newtable = null;
279 public static bool IsKnownScheme (string schemeName)
281 if (schemeName == null)
282 throw new ArgumentNullException ("schemeName");
283 if (schemeName.Length == 0)
284 throw new ArgumentOutOfRangeException ("schemeName");
286 CreateDefaults ();
287 string lc = schemeName.ToLower (CultureInfo.InvariantCulture);
288 return (table [lc] != null);
291 // *no* check version
292 private static void InternalRegister (Hashtable table, UriParser uriParser, string schemeName, int defaultPort)
294 uriParser.SchemeName = schemeName;
295 uriParser.DefaultPort = defaultPort;
297 // FIXME: MS doesn't seems to call most inherited parsers
298 if (uriParser is GenericUriParser) {
299 table.Add (schemeName, uriParser);
300 } else {
301 DefaultUriParser parser = new DefaultUriParser ();
302 parser.SchemeName = schemeName;
303 parser.DefaultPort = defaultPort;
304 table.Add (schemeName, parser);
307 // note: we cannot set schemeName and defaultPort inside OnRegister
308 uriParser.OnRegister (schemeName, defaultPort);
311 [SecurityPermission (SecurityAction.Demand, Infrastructure = true)]
312 public static void Register (UriParser uriParser, string schemeName, int defaultPort)
314 if (uriParser == null)
315 throw new ArgumentNullException ("uriParser");
316 if (schemeName == null)
317 throw new ArgumentNullException ("schemeName");
318 if ((defaultPort < -1) || (defaultPort >= UInt16.MaxValue))
319 throw new ArgumentOutOfRangeException ("defaultPort");
321 CreateDefaults ();
323 string lc = schemeName.ToLower (CultureInfo.InvariantCulture);
324 if (table [lc] != null) {
325 string msg = Locale.GetText ("Scheme '{0}' is already registred.");
326 throw new InvalidOperationException (msg);
328 InternalRegister (table, uriParser, lc, defaultPort);
331 internal static UriParser GetParser (string schemeName)
333 if (schemeName == null)
334 return null;
336 CreateDefaults ();
338 string lc = schemeName.ToLower (CultureInfo.InvariantCulture);
339 return (UriParser) table [lc];