2 // System.UriParser abstract class
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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;
35 public abstract class UriParser {
37 static object lock_object = new object ();
38 static Hashtable table;
40 internal string scheme_name;
41 private int default_port;
43 protected UriParser ()
48 protected internal virtual string GetComponents (Uri uri, UriComponents components, UriFormat format)
50 if ((format < UriFormat.UriEscaped) || (format > UriFormat.SafeUnescaped))
51 throw new ArgumentOutOfRangeException ("format");
53 if ((components & UriComponents.SerializationInfoString) != 0) {
54 if (components != UriComponents.SerializationInfoString)
55 throw new ArgumentOutOfRangeException ("components", "UriComponents.SerializationInfoString must not be combined with other UriComponents.");
57 if (!uri.IsAbsoluteUri)
58 return UriHelper.FormatRelative (uri.OriginalString, "", format);
60 components |= UriComponents.AbsoluteUri;
63 return GetComponentsHelper (uri, components, format);
66 internal string GetComponentsHelper (Uri uri, UriComponents components, UriFormat format)
68 UriElements elements = UriParseComponents.ParseComponents (uri.OriginalString.Trim (), UriKind.Absolute);
70 string scheme = scheme_name;
71 int dp = default_port;
73 if ((scheme == null) || (scheme == "*")) {
74 scheme = elements.scheme;
75 dp = Uri.GetDefaultPort (scheme);
76 } else if (String.Compare (scheme, elements.scheme, true) != 0) {
77 throw new SystemException ("URI Parser: scheme mismatch: " + scheme + " vs. " + elements.scheme);
80 var formatFlags = UriHelper.FormatFlags.None;
81 if (UriHelper.HasCharactersToNormalize (uri.OriginalString))
82 formatFlags |= UriHelper.FormatFlags.HasUriCharactersToNormalize;
85 formatFlags |= UriHelper.FormatFlags.UserEscaped;
87 if (!string.IsNullOrEmpty (elements.host))
88 formatFlags |= UriHelper.FormatFlags.HasHost;
90 // it's easier to answer some case directly (as the output isn't identical
91 // when mixed with others components, e.g. leading slash, # ...)
93 case UriComponents.Scheme:
95 case UriComponents.UserInfo:
96 return elements.user ?? "";
97 case UriComponents.Host:
99 case UriComponents.Port: {
100 int p = elements.port;
101 if (p >= 0 && p != dp)
102 return p.ToString (CultureInfo.InvariantCulture);
105 case UriComponents.Path:
106 var path = elements.path;
107 if (scheme != Uri.UriSchemeMailto && scheme != Uri.UriSchemeNews)
108 path = IgnoreFirstCharIf (elements.path, '/');
109 return UriHelper.FormatAbsolute (path, scheme, UriComponents.Path, format, formatFlags);
110 case UriComponents.Query:
111 return UriHelper.FormatAbsolute (elements.query, scheme, UriComponents.Query, format, formatFlags);
112 case UriComponents.Fragment:
113 return UriHelper.FormatAbsolute (elements.fragment, scheme, UriComponents.Fragment, format, formatFlags);
114 case UriComponents.StrongPort: {
115 return elements.port >= 0
116 ? elements.port.ToString (CultureInfo.InvariantCulture)
117 : dp.ToString (CultureInfo.InvariantCulture);
119 case UriComponents.SerializationInfoString:
120 components = UriComponents.AbsoluteUri;
124 // now we deal with multiple flags...
126 StringBuilder sb = new StringBuilder ();
128 if ((components & UriComponents.Scheme) != 0) {
130 sb.Append (elements.delimiter);
133 if ((components & UriComponents.UserInfo) != 0) {
134 string userinfo = elements.user;
135 if (userinfo != null) {
136 sb.Append (elements.user);
141 if ((components & UriComponents.Host) != 0)
142 sb.Append (elements.host);
144 // for StrongPort always show port - even if -1
145 // otherwise only display if ut's not the default port
146 if ((components & UriComponents.StrongPort) != 0) {
148 if (elements.port >= 0) {
149 sb.Append (elements.port);
155 if ((components & UriComponents.Port) != 0) {
156 int p = elements.port;
157 if (p >= 0 && p != dp) {
159 sb.Append (elements.port);
163 if ((components & UriComponents.Path) != 0) {
164 string path = elements.path;
165 if ((components & UriComponents.PathAndQuery) != 0 &&
166 (path.Length == 0 || !path.StartsWith ("/", StringComparison.Ordinal)) &&
167 elements.delimiter == Uri.SchemeDelimiter)
169 sb.Append (UriHelper.FormatAbsolute (path, scheme, UriComponents.Path, format, formatFlags));
172 if ((components & UriComponents.Query) != 0) {
173 string query = elements.query;
176 sb.Append (UriHelper.FormatAbsolute (query, scheme, UriComponents.Query, format, formatFlags));
180 if ((components & UriComponents.Fragment) != 0) {
181 string f = elements.fragment;
184 sb.Append (UriHelper.FormatAbsolute (f, scheme, UriComponents.Fragment, format, formatFlags));
187 return sb.ToString ();
190 protected internal virtual void InitializeAndValidate (Uri uri, out UriFormatException parsingError)
192 // bad boy, it should check null arguments.
193 if ((uri.Scheme != scheme_name) && (scheme_name != "*"))
194 // Here .NET seems to return "The Authority/Host could not be parsed", but it does not make sense.
195 parsingError = new UriFormatException ("The argument Uri's scheme does not match");
200 protected internal virtual bool IsBaseOf (Uri baseUri, Uri relativeUri)
203 throw new ArgumentNullException ("baseUri");
204 if (relativeUri == null)
205 throw new ArgumentNullException ("relativeUri");
207 // compare, not case sensitive, the scheme, host and port (+ user informations)
208 if (Uri.Compare (baseUri, relativeUri, UriComponents.SchemeAndServer | UriComponents.UserInfo, UriFormat.Unescaped, StringComparison.InvariantCultureIgnoreCase) != 0)
211 string base_string = baseUri.LocalPath;
212 int last_slash = base_string.LastIndexOf ('/') + 1; // keep the slash
213 return (String.Compare (base_string, 0, relativeUri.LocalPath, 0, last_slash, StringComparison.InvariantCultureIgnoreCase) == 0);
216 protected internal virtual bool IsWellFormedOriginalString (Uri uri)
218 // well formed according to RFC2396 and RFC2732
219 // see Uri.IsWellFormedOriginalString for some docs
221 // Though this class does not seem to do anything. Even null arguments aren't checked :/
222 return uri.IsWellFormedOriginalString ();
224 protected internal virtual UriParser OnNewUri ()
226 // nice time for init
231 protected virtual void OnRegister (string schemeName, int defaultPort)
233 // unit tests shows that schemeName and defaultPort aren't usable from here
237 protected internal virtual string Resolve (Uri baseUri, Uri relativeUri, out UriFormatException parsingError)
239 // used by Uri.ctor and Uri.TryCreate
240 throw new NotImplementedException ();
243 // internal properties
245 internal string SchemeName {
246 get { return scheme_name; }
247 set { scheme_name = value; }
250 internal int DefaultPort {
251 get { return default_port; }
252 set { default_port = value; }
257 private string IgnoreFirstCharIf (string s, char c)
262 return s.Substring (1);
268 private static void CreateDefaults ()
273 Hashtable newtable = new Hashtable ();
274 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeFile, -1);
275 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeFtp, 21);
276 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeGopher, 70);
277 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeHttp, 80);
278 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeHttps, 443);
279 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeMailto, 25);
280 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNetPipe, -1);
281 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNetTcp, -1);
282 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNews, -1);
283 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNntp, 119);
284 // not defined in Uri.UriScheme* but a parser class exists
285 InternalRegister (newtable, new DefaultUriParser (), "ldap", 389);
295 public static bool IsKnownScheme (string schemeName)
297 if (schemeName == null)
298 throw new ArgumentNullException ("schemeName");
299 if (schemeName.Length == 0)
300 throw new ArgumentOutOfRangeException ("schemeName");
303 string lc = schemeName.ToLower (CultureInfo.InvariantCulture);
304 return (table [lc] != null);
307 // *no* check version
308 private static void InternalRegister (Hashtable table, UriParser uriParser, string schemeName, int defaultPort)
310 uriParser.SchemeName = schemeName;
311 uriParser.DefaultPort = defaultPort;
313 // FIXME: MS doesn't seems to call most inherited parsers
314 if (uriParser is GenericUriParser) {
315 table.Add (schemeName, uriParser);
317 DefaultUriParser parser = new DefaultUriParser ();
318 parser.SchemeName = schemeName;
319 parser.DefaultPort = defaultPort;
320 table.Add (schemeName, parser);
323 // note: we cannot set schemeName and defaultPort inside OnRegister
324 uriParser.OnRegister (schemeName, defaultPort);
327 [SecurityPermission (SecurityAction.Demand, Infrastructure = true)]
328 public static void Register (UriParser uriParser, string schemeName, int defaultPort)
330 if (uriParser == null)
331 throw new ArgumentNullException ("uriParser");
332 if (schemeName == null)
333 throw new ArgumentNullException ("schemeName");
334 if ((defaultPort < -1) || (defaultPort >= UInt16.MaxValue))
335 throw new ArgumentOutOfRangeException ("defaultPort");
339 string lc = schemeName.ToLower (CultureInfo.InvariantCulture);
340 if (table [lc] != null) {
341 string msg = Locale.GetText ("Scheme '{0}' is already registred.");
342 throw new InvalidOperationException (msg);
344 InternalRegister (table, uriParser, lc, defaultPort);
347 internal static UriParser GetParser (string schemeName)
349 if (schemeName == null)
354 string lc = schemeName.ToLower (CultureInfo.InvariantCulture);
355 return (UriParser) table [lc];