Hey, In .NET 2.0, Uri parsing has been moved to designated classes all derived from UriParser. This makes it possible to have custom parsers for new Uri schemes. Currently mono doesn't support this properly.
What I think would be the best thing to do would be to change parsing under all profiles to use this new API. In the .NET 1.1 profile, we'd just mark these classes as internal, so this gives one codepath to maintain. In .NET 1.1 Uri.Parse () would would be replaced by calls to the new parser classes so that existing behaviour isn't changed. Under .NET 2.0, I believe an approach similar to what I've prototyped in the attached patch should be what is implemented. Basically each of the properties of Uri will call into the parser to get the required component. The results of this call could be cached locally for increased performance. The other benefit is that it should also reduce the shotgun approach to parsing, i.e. the special casing in the parse logic for different schemes. This might make things more maintainable. An example parser is also attached in Test.cs and is implemented as a console app. If you apply the above patch to mono, you can parse the "pack" schema. Note, the patch *will* break all other Uri parsing as i hardcoded a "pack" parser. Does anyone have any thoughts on this? Alan.
Index: System/Uri.cs =================================================================== --- System/Uri.cs (revision 117256) +++ System/Uri.cs (working copy) @@ -116,6 +116,7 @@ public Uri (string uriString) : this (uriString, false) { + Parser = UriParser.GetParser ("pack"); } protected Uri (SerializationInfo serializationInfo, @@ -405,22 +406,7 @@ public string AbsolutePath { get { #if NET_2_0 - EnsureAbsoluteUri (); - switch (Scheme) { - case "mailto": - case "file": - // faster (mailto) and special (file) cases - return path; - default: - if (path.Length == 0) { - string start = Scheme + SchemeDelimiter; - if (path.StartsWith (start)) - return "/"; - else - return String.Empty; - } - return path; - } + return GetComponents (UriComponents.Path | UriComponents.KeepDelimiter, UriFormat.Unescaped); #else return path; #endif @@ -428,7 +414,10 @@ } public string AbsoluteUri { - get { + get { +#if NET_2_0 + return GetComponents (UriComponents.AbsoluteUri, UriFormat.Unescaped); +#else EnsureAbsoluteUri (); if (cachedAbsoluteUri == null) { cachedAbsoluteUri = GetLeftPart (UriPartial.Path); @@ -438,28 +427,42 @@ cachedAbsoluteUri += fragment; } return cachedAbsoluteUri; +#endif } } public string Authority { - get { + get { +#if NET_2_0 + return GetComponents (UriComponents.HostAndPort, UriFormat.Unescaped); +#else EnsureAbsoluteUri (); return (GetDefaultPort (Scheme) == port) ? host : host + ":" + port; - } +#endif + } + } public string Fragment { - get { + get { +#if NET_2_0 + return GetComponents (UriComponents.Fragment | UriComponents.KeepDelimiter, UriFormat.Unescaped); +#else EnsureAbsoluteUri (); - return fragment; + return fragment; +#endif } } public string Host { - get { + get { +#if NET_2_0 + return GetComponents (UriComponents.Host, UriFormat.Unescaped); +#else EnsureAbsoluteUri (); - return host; + return host; +#endif } } @@ -485,8 +488,13 @@ public bool IsDefaultPort { get { +#if NET_2_0 + string s = GetComponents (UriComponents.Port, UriFormat.Unescaped); + return s == "" ? true : int.Parse (s) == Parser.DefaultPort; +#else EnsureAbsoluteUri (); return GetDefaultPort (Scheme) == port; +#endif } } @@ -590,29 +598,46 @@ public string PathAndQuery { get { +#if NET_2_0 + return GetComponents (UriComponents.PathAndQuery, UriFormat.Unescaped); +#else EnsureAbsoluteUri (); return path + Query; +#endif } } public int Port { - get { + get { +#if NET_2_0 + string s = GetComponents (UriComponents.Port, UriFormat.Unescaped); + return s == "" ? Parser.DefaultPort : int.Parse (s); +#else EnsureAbsoluteUri (); - return port; + return port; +#endif } } public string Query { - get { + get { +#if NET_2_0 + return GetComponents (UriComponents.Query | UriComponents.KeepDelimiter, UriFormat.Unescaped); +#else EnsureAbsoluteUri (); - return query; + return query; +#endif } } public string Scheme { - get { + get { +#if NET_2_0 + return GetComponents (UriComponents.Scheme, UriFormat.Unescaped); +#else EnsureAbsoluteUri (); - return scheme; + return scheme; +#endif } } @@ -1295,13 +1320,21 @@ // This parse method will throw exceptions on failure // private void Parse (UriKind kind, string uriString) - { + { +#if NET_2_0 + if (Parser == null) + { + if (UriParser.IsKnownScheme (uriString.Substring(0, uriString.IndexOf (':')))) + Parser = UriParser.GetParser (uriString.Substring(0, uriString.IndexOf (':'))); + } +#else if (uriString == null) throw new ArgumentNullException ("uriString"); string s = ParseNoExceptions (kind, uriString); if (s != null) throw new UriFormatException (s); +#endif } // @@ -1838,8 +1871,6 @@ private UriParser Parser { get { - if (parser == null) - parser = UriParser.GetParser (Scheme); return parser; } set { parser = value; }
using System; using System.Collections.Generic; using System.Text; namespace UriTests { class PackUriParser : System.GenericUriParser { const string SchemaName = "pack"; StringBuilder builder = new StringBuilder(); public PackUriParser () : base (GenericUriParserOptions.Default) { } protected override string GetComponents(Uri uri, UriComponents components, UriFormat format) { string s = uri.OriginalString; builder.Remove(0, builder.Length); if ((components & UriComponents.Scheme) == UriComponents.Scheme) { int start = 0; int end = s.IndexOf(':'); builder.Append(s, start, end - start); } if ((components & UriComponents.Host) == UriComponents.Host) { // Skip past pack:// int start = 7; int end = s.IndexOf('/', start); if (end == -1) end = s.Length; if (builder.Length > 0) builder.Append("://"); builder.Append(s, start, end - start); } // Port is always -1, so i think i can ignore both Port and StrongPort // Normally they'd get parsed here if ((components & UriComponents.Path) == UriComponents.Path) { // Skip past pack:// int start = s.IndexOf('/', 7); int end = s.IndexOf('?'); if (end == -1) end = s.IndexOf('#'); if (end == -1) end = s.Length; if ((components & UriComponents.KeepDelimiter) != UriComponents.KeepDelimiter && builder.Length == 0) start++; builder.Append(s, start, end - start); } if ((components & UriComponents.Query) == UriComponents.Query) { int index = s.IndexOf('?'); if (index == -1) return null; if ((components & UriComponents.KeepDelimiter) != UriComponents.KeepDelimiter && builder.Length == 0) index++; int fragIndex = s.IndexOf('#'); int end = fragIndex == -1 ? s.Length : fragIndex; builder.Append(s, index, end - index); } if ((components & UriComponents.Fragment) == UriComponents.Fragment) { int index = s.IndexOf('#'); if (index == -1) return null; if ((components & UriComponents.KeepDelimiter) != UriComponents.KeepDelimiter && builder.Length == 0) index++; builder.Append(s, index, s.Length - index); } return builder.ToString(); } protected override void InitializeAndValidate(Uri uri, out UriFormatException parsingError) { parsingError = null; } protected override UriParser OnNewUri() { return new PackUriParser(); } } class MainClass { public static void Main(string[] args) { UriParser.Register (new PackUriParser (), "pack", -1); string s = "pack://http:,,original.com,blah,/path/tfile.tmp?query=blah#fragment"; Uri u = new Uri(s); Console.WriteLine ("Port: {0}", u.Port); Console.WriteLine ("AbsoluteUri: {0}", u.AbsoluteUri); Console.WriteLine ("PathAndQuery: {0}", u.PathAndQuery); Console.WriteLine ("Query: {0}", u.Query); } } }
_______________________________________________ Mono-devel-list mailing list Mono-devel-list@lists.ximian.com http://lists.ximian.com/mailman/listinfo/mono-devel-list