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 UriElements elements = UriParseComponents.ParseComponents (uri.OriginalString.Trim ());
55 string scheme = scheme_name;
56 int dp = default_port;
58 if ((scheme == null) || (scheme == "*")) {
59 scheme = elements.scheme;
60 dp = Uri.GetDefaultPort (scheme);
61 } else if (String.Compare (scheme, elements.scheme, true) != 0) {
62 throw new SystemException ("URI Parser: scheme mismatch: " + scheme + " vs. " + elements.scheme);
65 // it's easier to answer some case directly (as the output isn't identical
66 // when mixed with others components, e.g. leading slash, # ...)
68 case UriComponents.Scheme:
70 case UriComponents.UserInfo:
72 case UriComponents.Host:
74 case UriComponents.Port: {
75 string p = elements.port;
76 if (p != null && p.Length != 0 && p != dp.ToString ())
80 case UriComponents.Path:
81 return Format (IgnoreFirstCharIf (elements.path, '/'), format);
82 case UriComponents.Query:
83 return Format (elements.query, format);
84 case UriComponents.Fragment:
85 return Format (elements.fragment, format);
86 case UriComponents.StrongPort: {
87 return elements.port.Length != 0 ? elements.port : dp.ToString ();
89 case UriComponents.SerializationInfoString:
90 components = UriComponents.AbsoluteUri;
94 // now we deal with multiple flags...
96 StringBuilder sb = new StringBuilder ();
98 if ((components & UriComponents.Scheme) != 0) {
100 sb.Append (Uri.GetSchemeDelimiter (scheme));
103 if ((components & UriComponents.UserInfo) != 0) {
104 string userinfo = elements.user;
105 if (!String.IsNullOrEmpty (userinfo)) {
106 sb.Append (elements.user);
111 if ((components & UriComponents.Host) != 0)
112 sb.Append (elements.host);
114 // for StrongPort always show port - even if -1
115 // otherwise only display if ut's not the default port
116 if ((components & UriComponents.StrongPort) != 0) {
118 if (elements.port.Length != 0) {
119 sb.Append (elements.port);
125 if ((components & UriComponents.Port) != 0) {
126 string p = elements.port;
127 if (p != null && p.Length != 0 && p != dp.ToString ()) {
129 sb.Append (elements.port);
133 if ((components & UriComponents.Path) != 0) {
134 if ((components & UriComponents.PathAndQuery) != 0 &&
135 (elements.path.Length == 0 || !elements.path.StartsWith ("/")))
137 sb.Append (elements.path);
140 if ((components & UriComponents.Query) != 0) {
141 string query = elements.query;
142 if (!String.IsNullOrEmpty (query)) {
144 sb.Append (elements.query);
148 string result = Format (sb.ToString (), format);
149 if ((components & UriComponents.Fragment) != 0) {
150 string f = elements.fragment;
151 if (!String.IsNullOrEmpty (f)) {
152 result += "#" + Format (f, format);
158 protected internal virtual void InitializeAndValidate (Uri uri, out UriFormatException parsingError)
160 // bad boy, it should check null arguments.
161 if ((uri.Scheme != scheme_name) && (scheme_name != "*"))
162 // Here .NET seems to return "The Authority/Host could not be parsed", but it does not make sense.
163 parsingError = new UriFormatException ("The argument Uri's scheme does not match");
168 protected internal virtual bool IsBaseOf (Uri baseUri, Uri relativeUri)
170 // compare, not case sensitive, the scheme, host and port (+ user informations)
171 if (Uri.Compare (baseUri, relativeUri, UriComponents.SchemeAndServer | UriComponents.UserInfo, UriFormat.Unescaped, StringComparison.InvariantCultureIgnoreCase) != 0)
174 string base_string = baseUri.LocalPath;
175 int last_slash = base_string.LastIndexOf ('/') + 1; // keep the slash
176 return (String.Compare (base_string, 0, relativeUri.LocalPath, 0, last_slash, StringComparison.InvariantCultureIgnoreCase) == 0);
179 protected internal virtual bool IsWellFormedOriginalString (Uri uri)
181 // well formed according to RFC2396 and RFC2732
182 // see Uri.IsWellFormedOriginalString for some docs
184 // Though this class does not seem to do anything. Even null arguments aren't checked :/
185 return uri.IsWellFormedOriginalString ();
187 protected internal virtual UriParser OnNewUri ()
189 // nice time for init
194 protected virtual void OnRegister (string schemeName, int defaultPort)
196 // unit tests shows that schemeName and defaultPort aren't usable from here
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; }
220 private string IgnoreFirstCharIf (string s, char c)
225 return s.Substring (1);
229 private string Format (string s, UriFormat format)
235 case UriFormat.UriEscaped:
236 return Uri.EscapeString (s, Uri.EscapeCommonHexBrackets);
237 case UriFormat.SafeUnescaped:
238 return Uri.UnescapeDataString (s, true);
239 case UriFormat.Unescaped:
240 return Uri.Unescape (s, false);
242 throw new ArgumentOutOfRangeException ("format");
248 private static void CreateDefaults ()
253 Hashtable newtable = new Hashtable ();
254 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeFile, -1);
255 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeFtp, 21);
256 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeGopher, 70);
257 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeHttp, 80);
258 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeHttps, 443);
259 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeMailto, 25);
260 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNetPipe, -1);
261 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNetTcp, -1);
262 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNews, -1);
263 InternalRegister (newtable, new DefaultUriParser (), Uri.UriSchemeNntp, 119);
264 // not defined in Uri.UriScheme* but a parser class exists
265 InternalRegister (newtable, new DefaultUriParser (), "ldap", 389);
275 public static bool IsKnownScheme (string schemeName)
277 if (schemeName == null)
278 throw new ArgumentNullException ("schemeName");
279 if (schemeName.Length == 0)
280 throw new ArgumentOutOfRangeException ("schemeName");
283 string lc = schemeName.ToLower (CultureInfo.InvariantCulture);
284 return (table [lc] != null);
287 // *no* check version
288 private static void InternalRegister (Hashtable table, UriParser uriParser, string schemeName, int defaultPort)
290 uriParser.SchemeName = schemeName;
291 uriParser.DefaultPort = defaultPort;
293 // FIXME: MS doesn't seems to call most inherited parsers
294 if (uriParser is GenericUriParser) {
295 table.Add (schemeName, uriParser);
297 DefaultUriParser parser = new DefaultUriParser ();
298 parser.SchemeName = schemeName;
299 parser.DefaultPort = defaultPort;
300 table.Add (schemeName, parser);
303 // note: we cannot set schemeName and defaultPort inside OnRegister
304 uriParser.OnRegister (schemeName, defaultPort);
307 [SecurityPermission (SecurityAction.Demand, Infrastructure = true)]
308 public static void Register (UriParser uriParser, string schemeName, int defaultPort)
310 if (uriParser == null)
311 throw new ArgumentNullException ("uriParser");
312 if (schemeName == null)
313 throw new ArgumentNullException ("schemeName");
314 if ((defaultPort < -1) || (defaultPort >= UInt16.MaxValue))
315 throw new ArgumentOutOfRangeException ("defaultPort");
319 string lc = schemeName.ToLower (CultureInfo.InvariantCulture);
320 if (table [lc] != null) {
321 string msg = Locale.GetText ("Scheme '{0}' is already registred.");
322 throw new InvalidOperationException (msg);
324 InternalRegister (table, uriParser, lc, defaultPort);
327 internal static UriParser GetParser (string schemeName)
329 if (schemeName == null)
334 string lc = schemeName.ToLower (CultureInfo.InvariantCulture);
335 return (UriParser) table [lc];