Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / System.ServiceModel.Web / System / UriTemplate.cs
index cdd420d608b471622497134796eb782872d8172c..875acb2117ec61a67808e9e4c99958795ec84a7f 100644 (file)
@@ -5,6 +5,7 @@
 //     Atsushi Enomoto  <atsushi@ximian.com>
 //
 // Copyright (C) 2008 Novell, Inc (http://www.novell.com)
+// Copyright 2011 Xamarin Inc (http://www.xamarin.com).
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -32,10 +33,6 @@ using System.Collections.Specialized;
 using System.Globalization;
 using System.Text;
 
-#if NET_2_1
-using NameValueCollection = System.Object;
-#endif
-
 namespace System
 {
        public class UriTemplate
@@ -44,6 +41,7 @@ namespace System
 
                string template;
                ReadOnlyCollection<string> path, query;
+               string wild_path_name;
                Dictionary<string,string> query_params = new Dictionary<string,string> ();
 
                public UriTemplate (string template)
@@ -106,7 +104,6 @@ namespace System
 
                // Bind
 
-#if !MOONLIGHT
                public Uri BindByName (Uri baseAddress, NameValueCollection parameters)
                {
                        return BindByName (baseAddress, parameters, false);
@@ -116,7 +113,6 @@ namespace System
                {
                        return BindByNameCommon (baseAddress, parameters, null, omitDefaults);
                }
-#endif
 
                public Uri BindByName (Uri baseAddress, IDictionary<string,string> parameters)
                {
@@ -139,7 +135,7 @@ namespace System
                                return String.Empty;
                        
                        if (sb [0] == '/')
-                               sb.Remove (0, 1);
+                               return sb.ToString (1, sb.Length - 1);
 
                        return sb.ToString ();
                }
@@ -162,28 +158,35 @@ namespace System
 
                void BindByName (ref int src, StringBuilder sb, ReadOnlyCollection<string> names, NameValueCollection nvc, IDictionary<string,string> dic, bool omitDefaults, bool query)
                {
+                       if (query) {
+                               int idx = template.IndexOf ('?', src);
+                               if (idx > 0) {
+                                       sb.Append (template.Substring (src, idx - src));
+                                       src = idx;
+                                       // note that it doesn't append '?'. It is added only when there is actual parameter binding.
+                               }
+                       }
+
                        foreach (string name in names) {
                                int s = template.IndexOf ('{', src);
                                int e = template.IndexOf ('}', s + 1);
-#if NET_2_1
-                               string value = null;
-#else
                                string value = nvc != null ? nvc [name] : null;
-#endif
+
                                if (dic != null)
                                        dic.TryGetValue (name, out value);
+
                                if (query) {
                                        if (value != null || (!omitDefaults && Defaults.TryGetValue (name, out value))) {
                                                sb.Append (template.Substring (src, s - src));
                                                sb.Append (value);
                                        }
-                               } else
-                                       if (value == null && (omitDefaults || !Defaults.TryGetValue(name, out value)))
-                                               throw new ArgumentException(string.Format("The argument name value collection does not contain non-nul vaalue for '{0}'", name), "parameters");
-                                       else {
-                                               sb.Append (template.Substring (src, s - src));
-                                               sb.Append (value);
-                                       }
+                               } else {
+                                       if (value == null && (omitDefaults || !Defaults.TryGetValue (name, out value)))
+                                               throw new ArgumentException (string.Format("The argument name value collection does not contain non-null value for '{0}'", name), "parameters");
+
+                                       sb.Append (template.Substring (src, s - src));
+                                       sb.Append (value);
+                               }
                                src = e + 1;
                        }
                }
@@ -238,16 +241,19 @@ namespace System
 
                        var us = baseAddress.LocalPath;
                        if (us [us.Length - 1] != '/')
-                               baseAddress = new Uri (baseAddress.GetComponents (UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.Unescaped) + '/' + baseAddress.Query, baseAddress.IsAbsoluteUri ? UriKind.Absolute : UriKind.RelativeOrAbsolute);
+                               baseAddress = new Uri (
+                                       baseAddress.GetComponents (UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped) + '/' + baseAddress.Query,
+                                       baseAddress.IsAbsoluteUri ? UriKind.Absolute : UriKind.RelativeOrAbsolute
+                               );
                        if (IgnoreTrailingSlash) {
                                us = candidate.LocalPath;
                                if (us.Length > 0 && us [us.Length - 1] != '/')
-                                       candidate = new Uri(candidate.GetComponents (UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.Unescaped) + '/' + candidate.Query, candidate.IsAbsoluteUri ? UriKind.Absolute : UriKind.RelativeOrAbsolute);
+                                       candidate = new Uri (
+                                               candidate.GetComponents (UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped) + '/' + candidate.Query,
+                                               candidate.IsAbsoluteUri ? UriKind.Absolute : UriKind.RelativeOrAbsolute
+                                       );
                        }
 
-                       if (Uri.Compare (baseAddress, candidate, UriComponents.StrongAuthority, UriFormat.SafeUnescaped, StringComparison.Ordinal) != 0)
-                               return null;
-
                        int i = 0, c = 0;
                        UriTemplateMatch m = new UriTemplateMatch ();
                        m.BaseUri = baseAddress;
@@ -255,7 +261,11 @@ namespace System
                        m.RequestUri = candidate;
                        var vc = m.BoundVariables;
 
-                       string cp = Uri.UnescapeDataString (baseAddress.MakeRelativeUri (candidate).ToString ());
+                       string cp = baseAddress.MakeRelativeUri (new Uri (
+                               baseAddress,
+                               candidate.GetComponents (UriComponents.PathAndQuery, UriFormat.UriEscaped)
+                       ))
+                               .ToString ();
                        if (IgnoreTrailingSlash && cp [cp.Length - 1] == '/')
                                cp = cp.Substring (0, cp.Length - 1);
 
@@ -269,6 +279,10 @@ namespace System
                                c++;
 
                        foreach (string name in path) {
+                               if (name == wild_path_name) {
+                                       vc [name] = Uri.UnescapeDataString (cp.Substring (c)); // all remaining paths.
+                                       continue;
+                               }
                                int n = StringIndexOf (template, '{' + name + '}', i);
                                if (String.CompareOrdinal (cp, c, template, i, n - i) != 0)
                                        return null; // doesn't match before current template part.
@@ -278,18 +292,20 @@ namespace System
                                if (ce < 0)
                                        ce = cp.Length;
                                string value = cp.Substring (c, ce - c);
+                               string unescapedVaule = Uri.UnescapeDataString (value);
                                if (value.Length == 0)
                                        return null; // empty => mismatch
-                               vc [name] = value;
-                               m.RelativePathSegments.Add (value);
+                               vc [name] = unescapedVaule;
+                               m.RelativePathSegments.Add (unescapedVaule);
                                c += value.Length;
                        }
                        int tEnd = template.IndexOf ('?');
+                       int wildIdx = template.IndexOf ('*');
+                       bool wild = wildIdx >= 0;
                        if (tEnd < 0)
                                tEnd = template.Length;
-                       bool wild = (template [tEnd - 1] == '*');
                        if (wild)
-                               tEnd--;
+                               tEnd = Math.Max (wildIdx - 1, 0);
                        if (!wild && (cp.Length - c) != (tEnd - i) ||
                            String.CompareOrdinal (cp, c, template, i, tEnd - i) != 0)
                                return null; // suffix doesn't match
@@ -337,8 +353,9 @@ namespace System
                ReadOnlyCollection<string> ParsePathTemplate (string template, int index, int end)
                {
                        int widx = template.IndexOf ('*', index, end);
-                       if (widx >= 0 && widx != end - 1)
-                               throw new FormatException (String.Format ("Wildcard in UriTemplate is valid only if it is placed at the last part of the path: '{0}'", template));
+                       if (widx >= 0)
+                               if (widx != end - 1 && template.IndexOf ('}', widx) != end - 1)
+                                       throw new FormatException (String.Format ("Wildcard in UriTemplate is valid only if it is placed at the last part of the path: '{0}'", template));
                        List<string> list = null;
                        int prevEnd = -2;
                        for (int i = index; i <= end; ) {
@@ -356,6 +373,8 @@ namespace System
                                i++;
                                string name = template.Substring (i, e - i);
                                string uname = name.ToUpper (CultureInfo.InvariantCulture);
+                               if (uname [0] == '*')
+                                       uname = wild_path_name = uname.Substring (1);
                                if (list.Contains (uname) || (path != null && path.Contains (uname)))
                                        throw new InvalidOperationException (String.Format ("The URI template string contains duplicate template item {{'{0}'}}", name));
                                list.Add (uname);