< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.UriTemplate
Assembly: CoreWCF.WebHttp
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.WebHttp/src/CoreWCF/UriTemplate.cs
Line coverage
35%
Covered lines: 235
Uncovered lines: 436
Coverable lines: 671
Total lines: 1788
Line coverage: 35%
Branch coverage
29%
Covered branches: 138
Total branches: 466
Branch coverage: 29.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
.ctor(...)100%11100%
.ctor(...)100%110%
.ctor(...)59.67%626263.73%
BindByName(...)100%110%
BindByName(...)0%660%
BindByName(...)100%110%
BindByName(...)0%660%
BindByPosition(...)50%8850%
IsEquivalentTo(...)0%10100%
Match(...)0%18180%
ToString()100%110%
AddPathVariable(...)100%11100%
AddPathVariable(...)100%22100%
AddQueryVariable(...)100%22100%
CreateUriTemplateMatch(...)81.25%161692%
IsPathPartiallyEquivalentAt(...)0%880%
IsQueryEquivalent(...)0%880%
RewriteUri(...)0%660%
Bind(...)38.63%444445.65%
BindTerminalDefaults(...)100%44100%
IsCandidatePathMatch(...)0%24240%
IsPathFullyEquivalent(...)0%14140%
PrepareBindInformation(...)0%660%
PrepareBindInformation(...)0%660%
ProcessDefaultsAndCreateBindInfo(...)0%16160%
UnescapeDefaultValue(...)75%4480%
.ctor(...)100%110%
.ctor(...)100%11100%
.ctor(...)0%10100%
Add(...)100%110%
Add(...)100%110%
Clear()100%110%
Contains(...)100%110%
ContainsKey(...)100%110%
CopyTo(...)100%110%
GetEnumerator()100%110%
Remove(...)100%110%
Remove(...)100%110%
System.Collections.IEnumerable.GetEnumerator()100%110%
TryGetValue(...)100%110%
.ctor(...)100%11100%
AddDefaultValue(...)0%14140%
AddPathVariable(...)64.28%141465.21%
AddQueryVariable(...)50%8853.33%
LookupDefault(...)100%11100%
PrepareBindInformation(...)0%440%
PrepareBindInformation(...)0%440%
PrepareBindInformation(...)0%10100%
ValidateDefaults(...)58.82%343447.36%
AddAdditionalDefaults(...)0%660%
LoadDefaultsAndValidate(...)0%30300%
ParseVariableDeclaration(...)60%101060%
PrepareNormalizedParameters()0%220%
ProcessBindParameter(...)0%12120%
ProcessDefaultsAndCreateBindInfo(...)0%440%
RemoveAdditionalDefaults(...)0%10100%
.ctor(...)100%11100%
.ctor(...)50%2270%
Bind(...)0%440%
Lookup(...)100%66100%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.WebHttp/src/CoreWCF/UriTemplate.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.Collections.Concurrent;
 6using System.Collections.Generic;
 7using System.Collections.ObjectModel;
 8using System.Collections.Specialized;
 9using System.Globalization;
 10using System.Text;
 11using System.Threading;
 12using CoreWCF.Runtime;
 13
 14namespace CoreWCF
 15{
 16    public class UriTemplate
 17    {
 18        internal readonly int _firstOptionalSegment;
 19        internal readonly string _originalTemplate;
 20        internal readonly Dictionary<string, UriTemplateQueryValue> _queries; // keys are original case specified in Uri
 21        internal readonly List<UriTemplatePathSegment> _segments;
 22
 23        internal const string WildcardPath = "*";
 24
 25        private readonly Dictionary<string, string> _additionalDefaults; // keys are original case specified in UriTempl
 26        private readonly string _fragment;
 27        private const string NullableDefault = "null";
 28        private readonly WildcardInfo _wildcard;
 29        private IDictionary<string, string> _defaults;
 30        private ConcurrentDictionary<string, string> _unescapedDefaults;
 31
 32        private VariablesCollection _variables;
 33
 34        // constructors validates that template is well-formed
 35        public UriTemplate(string template)
 51236            : this(template, false)
 37        {
 51238        }
 39
 40        public UriTemplate(string template, bool ignoreTrailingSlash)
 51241            : this(template, ignoreTrailingSlash, null)
 42        {
 51243        }
 44
 45        public UriTemplate(string template, IDictionary<string, string> additionalDefaults)
 046            : this(template, false, additionalDefaults)
 47        {
 048        }
 49
 51250        public UriTemplate(string template, bool ignoreTrailingSlash, IDictionary<string, string> additionalDefaults)
 51        {
 51252            _originalTemplate = template ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(temp
 53
 51254            IgnoreTrailingSlash = ignoreTrailingSlash;
 51255            _segments = new List<UriTemplatePathSegment>();
 51256            _queries = new Dictionary<string, UriTemplateQueryValue>(StringComparer.OrdinalIgnoreCase);
 57
 58            // parse it
 59            string pathTemplate;
 60            string queryTemplate;
 61            // ignore a leading slash
 51262            if (template.StartsWith("/", StringComparison.Ordinal))
 63            {
 41664                template = template.Substring(1);
 65            }
 66
 67            // pull out fragment
 51268            int fragmentStart = template.IndexOf('#');
 51269            if (fragmentStart == -1)
 70            {
 51271                _fragment = "";
 72            }
 73            else
 74            {
 075                _fragment = template.Substring(fragmentStart + 1);
 076                template = template.Substring(0, fragmentStart);
 77            }
 78
 79            // pull out path and query
 51280            int queryStart = template.IndexOf('?');
 51281            if (queryStart == -1)
 82            {
 48583                queryTemplate = string.Empty;
 48584                pathTemplate = template;
 85            }
 86            else
 87            {
 2788                queryTemplate = template.Substring(queryStart + 1);
 2789                pathTemplate = template.Substring(0, queryStart);
 90            }
 91
 51292            template = null; // to ensure we don't accidentally reference this variable any more
 93
 94            // setup path template and validate
 51295            if (!string.IsNullOrEmpty(pathTemplate))
 96            {
 51297                int startIndex = 0;
 113698                while (startIndex < pathTemplate.Length)
 99                {
 100                    // Identify the next segment
 624101                    int endIndex = pathTemplate.IndexOf('/', startIndex);
 102                    string segment;
 624103                    if (endIndex != -1)
 104                    {
 112105                        segment = pathTemplate.Substring(startIndex, endIndex + 1 - startIndex);
 112106                        startIndex = endIndex + 1;
 107                    }
 108                    else
 109                    {
 512110                        segment = pathTemplate.Substring(startIndex);
 512111                        startIndex = pathTemplate.Length;
 112                    }
 113
 114                    // Checking for wildcard segment ("*") or ("{*<var name>}")
 624115                    if ((startIndex == pathTemplate.Length) &&
 624116                        UriTemplateHelpers.IsWildcardSegment(segment, out UriTemplatePartType wildcardType))
 117                    {
 118                        switch (wildcardType)
 119                        {
 120                            case UriTemplatePartType.Literal:
 24121                                _wildcard = new WildcardInfo(this);
 24122                                break;
 123
 124                            case UriTemplatePartType.Variable:
 24125                                _wildcard = new WildcardInfo(this, segment);
 24126                                break;
 127
 128                            default:
 129                                Fx.Assert("Error in identifying the type of the wildcard segment");
 130                                break;
 131                        }
 132                    }
 133                    else
 134                    {
 576135                        _segments.Add(UriTemplatePathSegment.CreateFromUriTemplate(segment, this));
 136                    }
 137                }
 138            }
 139
 140            // setup query template and validate
 512141            if (!string.IsNullOrEmpty(queryTemplate))
 142            {
 27143                int startIndex = 0;
 56144                while (startIndex < queryTemplate.Length)
 145                {
 146                    // Identify the next query part
 29147                    int endIndex = queryTemplate.IndexOf('&', startIndex);
 29148                    int queryPartStart = startIndex;
 149                    int queryPartEnd;
 29150                    if (endIndex != -1)
 151                    {
 2152                        queryPartEnd = endIndex;
 2153                        startIndex = endIndex + 1;
 2154                        if (startIndex >= queryTemplate.Length)
 155                        {
 0156                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.F
 0157                                SR.UTQueryCannotEndInAmpersand, _originalTemplate)));
 158                        }
 159                    }
 160                    else
 161                    {
 27162                        queryPartEnd = queryTemplate.Length;
 27163                        startIndex = queryTemplate.Length;
 164                    }
 165
 166                    // Checking query part type; identifying key and value
 29167                    int equalSignIndex = queryTemplate.IndexOf('=', queryPartStart, queryPartEnd - queryPartStart);
 168                    string key;
 169                    string value;
 29170                    if (equalSignIndex >= 0)
 171                    {
 29172                        key = queryTemplate.Substring(queryPartStart, equalSignIndex - queryPartStart);
 29173                        value = queryTemplate.Substring(equalSignIndex + 1, queryPartEnd - equalSignIndex - 1);
 174                    }
 175                    else
 176                    {
 0177                        key = queryTemplate.Substring(queryPartStart, queryPartEnd - queryPartStart);
 0178                        value = null;
 179                    }
 180
 29181                    if (string.IsNullOrEmpty(key))
 182                    {
 0183                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Forma
 0184                            SR.UTQueryCannotHaveEmptyName, _originalTemplate)));
 185                    }
 186
 29187                    if (UriTemplateHelpers.IdentifyPartType(key) != UriTemplatePartType.Literal)
 188                    {
 0189                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(template), SR.Format(
 0190                            SR.UTQueryMustHaveLiteralNames, _originalTemplate));
 191                    }
 192
 193                    // Adding a new entry to the queries dictionary
 29194                    key = UrlUtility.UrlDecode(key, Encoding.UTF8);
 29195                    if (_queries.ContainsKey(key))
 196                    {
 0197                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Forma
 0198                            SR.UTQueryNamesMustBeUnique, _originalTemplate)));
 199                    }
 200
 29201                    _queries.Add(key, UriTemplateQueryValue.CreateFromUriTemplate(value, this));
 202                }
 203            }
 204
 205            // Process additional defaults (if has some) :
 512206            if (additionalDefaults != null)
 207            {
 0208                if (_variables == null)
 209                {
 0210                    if (additionalDefaults.Count > 0)
 211                    {
 0212                        _additionalDefaults = new Dictionary<string, string>(additionalDefaults, StringComparer.OrdinalI
 213                    }
 214                }
 215                else
 216                {
 0217                    foreach (KeyValuePair<string, string> kvp in additionalDefaults)
 218                    {
 0219                        string uppercaseKey = kvp.Key.ToUpperInvariant();
 0220                        if ((_variables.DefaultValues != null) && _variables.DefaultValues.ContainsKey(uppercaseKey))
 221                        {
 0222                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(additionalDefaults),
 0223                                SR.Format(SR.UTAdditionalDefaultIsInvalid, kvp.Key, _originalTemplate));
 224                        }
 225
 0226                        if (_variables.PathSegmentVariableNames.Contains(uppercaseKey))
 227                        {
 0228                            _variables.AddDefaultValue(uppercaseKey, kvp.Value);
 229                        }
 0230                        else if (_variables.QueryValueVariableNames.Contains(uppercaseKey))
 231                        {
 0232                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 0233                                SR.Format(SR.UTDefaultValueToQueryVarFromAdditionalDefaults, _originalTemplate,
 0234                                uppercaseKey)));
 235                        }
 0236                        else if (string.Compare(kvp.Value, UriTemplate.NullableDefault, StringComparison.OrdinalIgnoreCa
 237                        {
 0238                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 0239                                SR.Format(SR.UTNullableDefaultAtAdditionalDefaults, _originalTemplate,
 0240                                uppercaseKey)));
 241                        }
 242                        else
 243                        {
 0244                            if (_additionalDefaults == null)
 245                            {
 0246                                _additionalDefaults = new Dictionary<string, string>(additionalDefaults.Count, StringCom
 247                            }
 248
 0249                            _additionalDefaults.Add(kvp.Key, kvp.Value);
 250                        }
 251                    }
 252                }
 253            }
 254
 255            // Validate defaults (if should)
 512256            if ((_variables != null) && (_variables.DefaultValues != null))
 257            {
 24258                _variables.ValidateDefaults(out _firstOptionalSegment);
 259            }
 260            else
 261            {
 488262                _firstOptionalSegment = _segments.Count;
 263            }
 488264        }
 265
 266        public IDictionary<string, string> Defaults
 267        {
 268            get
 269            {
 0270                if (_defaults == null)
 271                {
 0272                    Interlocked.CompareExchange(ref _defaults, new UriTemplateDefaults(this), null);
 273                }
 274
 0275                return _defaults;
 276            }
 277        }
 278
 278279        public bool IgnoreTrailingSlash { get; }
 280
 281        public ReadOnlyCollection<string> PathSegmentVariableNames
 282        {
 283            get
 284            {
 349285                if (_variables == null)
 286                {
 264287                    return VariablesCollection.EmptyCollection;
 288                }
 289                else
 290                {
 85291                    return _variables.PathSegmentVariableNames;
 292                }
 293            }
 294        }
 295
 296        public ReadOnlyCollection<string> QueryValueVariableNames
 297        {
 298            get
 299            {
 347300                if (_variables == null)
 301                {
 264302                    return VariablesCollection.EmptyCollection;
 303                }
 304                else
 305                {
 83306                    return _variables.QueryValueVariableNames;
 307                }
 308            }
 309        }
 310
 311        internal bool HasNoVariables
 312        {
 313            get
 314            {
 163315                return _variables == null;
 316            }
 317        }
 318
 319        internal bool HasWildcard
 320        {
 321            get
 322            {
 318323                return (_wildcard != null);
 324            }
 325        }
 326
 327        // make a Uri by subbing in the values, throw on bad input
 328        public Uri BindByName(Uri baseAddress, IDictionary<string, string> parameters)
 329        {
 0330            return BindByName(baseAddress, parameters, false);
 331        }
 332
 333        public Uri BindByName(Uri baseAddress, IDictionary<string, string> parameters, bool omitDefaults)
 334        {
 0335            if (baseAddress == null)
 336            {
 0337                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(baseAddress));
 338            }
 339
 0340            if (!baseAddress.IsAbsoluteUri)
 341            {
 0342                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(baseAddress), SR.Format(
 0343                    SR.UTBadBaseAddress));
 344            }
 345
 346            BindInformation bindInfo;
 0347            if (_variables == null)
 348            {
 0349                bindInfo = PrepareBindInformation(parameters, omitDefaults);
 350            }
 351            else
 352            {
 0353                bindInfo = _variables.PrepareBindInformation(parameters, omitDefaults);
 354            }
 355
 0356            return Bind(baseAddress, bindInfo, omitDefaults);
 357        }
 358
 359        public Uri BindByName(Uri baseAddress, NameValueCollection parameters)
 360        {
 0361            return BindByName(baseAddress, parameters, false);
 362        }
 363
 364        public Uri BindByName(Uri baseAddress, NameValueCollection parameters, bool omitDefaults)
 365        {
 0366            if (baseAddress == null)
 367            {
 0368                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(baseAddress));
 369            }
 370
 0371            if (!baseAddress.IsAbsoluteUri)
 372            {
 0373                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(baseAddress), SR.Format(
 0374                    SR.UTBadBaseAddress));
 375            }
 376
 377            BindInformation bindInfo;
 0378            if (_variables == null)
 379            {
 0380                bindInfo = PrepareBindInformation(parameters, omitDefaults);
 381            }
 382            else
 383            {
 0384                bindInfo = _variables.PrepareBindInformation(parameters, omitDefaults);
 385            }
 386
 0387            return Bind(baseAddress, bindInfo, omitDefaults);
 388        }
 389
 390        public Uri BindByPosition(Uri baseAddress, params string[] values)
 391        {
 115392            if (baseAddress == null)
 393            {
 0394                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(baseAddress));
 395            }
 396
 115397            if (!baseAddress.IsAbsoluteUri)
 398            {
 0399                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(baseAddress), SR.Format(
 0400                    SR.UTBadBaseAddress));
 401            }
 402
 403            BindInformation bindInfo;
 115404            if (_variables == null)
 405            {
 115406                if (values.Length > 0)
 407                {
 0408                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.Format(
 0409                        SR.UTBindByPositionNoVariables, _originalTemplate, values.Length)));
 410                }
 115411                bindInfo = new BindInformation(_additionalDefaults);
 412            }
 413            else
 414            {
 0415                bindInfo = _variables.PrepareBindInformation(values);
 416            }
 417
 115418            return Bind(baseAddress, bindInfo, false);
 419        }
 420
 421        // A note about UriTemplate equivalency:
 422        //  The introduction of defaults and, more over, terminal defaults, broke the simple
 423        //  intuitive notion of equivalency between templates. We will define equivalent
 424        //  templates as such based on the structure of them and not based on the set of uri
 425        //  that are matched by them. The result is that, even though they do not match the
 426        //  same set of uri's, the following templates are equivalent:
 427        //      - "/foo/{bar}"
 428        //      - "/foo/{bar=xyz}"
 429        //  A direct result from the support for 'terminal defaults' is that the IsPathEquivalentTo
 430        //  method, which was used both to determine the equivalence between templates, as
 431        //  well as verify that all the templates, combined together in the same PathEquivalentSet,
 432        //  are equivalent in their path is no longer valid for both purposes. We will break
 433        //  it to two distinct methods, each will be called in a different case.
 434        public bool IsEquivalentTo(UriTemplate other)
 435        {
 0436            if (other == null)
 437            {
 0438                return false;
 439            }
 440
 0441            if (other._segments == null || other._queries == null)
 442            {
 443                // they never are null, but PreSharp is complaining,
 444                // and warning suppression isn't working
 0445                return false;
 446            }
 447
 0448            if (!IsPathFullyEquivalent(other))
 449            {
 0450                return false;
 451            }
 452
 0453            if (!IsQueryEquivalent(other))
 454            {
 0455                return false;
 456            }
 457
 458            Fx.Assert(UriTemplateEquivalenceComparer.Instance.GetHashCode(this) == UriTemplateEquivalenceComparer.Instan
 0459            return true;
 460        }
 461
 462        public UriTemplateMatch Match(Uri baseAddress, Uri candidate)
 463        {
 0464            if (baseAddress == null)
 465            {
 0466                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(baseAddress));
 467            }
 468
 0469            if (!baseAddress.IsAbsoluteUri)
 470            {
 0471                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(baseAddress), SR.Format(
 0472                    SR.UTBadBaseAddress));
 473            }
 474
 0475            if (candidate == null)
 476            {
 0477                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(candidate));
 478            }
 479
 480            // ensure that the candidate is 'under' the base address
 0481            if (!candidate.IsAbsoluteUri)
 482            {
 0483                return null;
 484            }
 485
 0486            string basePath = UriTemplateHelpers.GetUriPath(baseAddress);
 0487            string candidatePath = UriTemplateHelpers.GetUriPath(candidate);
 0488            if (candidatePath.Length < basePath.Length)
 489            {
 0490                return null;
 491            }
 492
 0493            if (!candidatePath.StartsWith(basePath, StringComparison.OrdinalIgnoreCase))
 494            {
 0495                return null;
 496            }
 497
 498            // Identifying the relative segments \ checking matching to the path :
 0499            int numSegmentsInBaseAddress = baseAddress.Segments.Length;
 0500            string[] candidateSegments = candidate.Segments;
 0501            if (!IsCandidatePathMatch(numSegmentsInBaseAddress, candidateSegments,
 0502                out int numMatchedSegments, out Collection<string> relativeCandidateSegments))
 503            {
 0504                return null;
 505            }
 506
 507            // Checking matching to the query (if should) :
 0508            NameValueCollection candidateQuery = null;
 0509            if (!UriTemplateHelpers.CanMatchQueryTrivially(this))
 510            {
 0511                candidateQuery = UriTemplateHelpers.ParseQueryString(candidate.Query);
 0512                if (!UriTemplateHelpers.CanMatchQueryInterestingly(this, candidateQuery, false))
 513                {
 0514                    return null;
 515                }
 516            }
 517
 518            // We matched; lets build the UriTemplateMatch
 0519            return CreateUriTemplateMatch(baseAddress, candidate, null, numMatchedSegments,
 0520                relativeCandidateSegments, candidateQuery);
 521        }
 522
 0523        public override string ToString() => _originalTemplate;
 524
 525        internal string AddPathVariable(UriTemplatePartType sourceNature, string varDeclaration)
 526        {
 52527            return AddPathVariable(sourceNature, varDeclaration, out _);
 528        }
 529
 530        internal string AddPathVariable(UriTemplatePartType sourceNature, string varDeclaration,
 531            out bool hasDefaultValue)
 532        {
 124533            if (_variables == null)
 534            {
 98535                _variables = new VariablesCollection(this);
 536            }
 537
 124538            return _variables.AddPathVariable(sourceNature, varDeclaration, out hasDefaultValue);
 539        }
 540
 541        internal string AddQueryVariable(string varDeclaration)
 542        {
 29543            if (_variables == null)
 544            {
 27545                _variables = new VariablesCollection(this);
 546            }
 547
 29548            return _variables.AddQueryVariable(varDeclaration);
 549        }
 550
 551        internal UriTemplateMatch CreateUriTemplateMatch(Uri baseUri, Uri uri, object data,
 552            int numMatchedSegments, Collection<string> relativePathSegments, NameValueCollection uriQuery)
 553        {
 35554            UriTemplateMatch result = new UriTemplateMatch
 35555            {
 35556                RequestUri = uri,
 35557                BaseUri = baseUri
 35558            };
 559
 35560            if (uriQuery != null)
 561            {
 1562                result.SetQueryParameters(uriQuery);
 563            }
 564
 35565            result.SetRelativePathSegments(relativePathSegments);
 35566            result.Data = data;
 35567            result.Template = this;
 146568            for (int i = 0; i < numMatchedSegments; i++)
 569            {
 38570                _segments[i].Lookup(result.RelativePathSegments[i], result.BoundVariables);
 571            }
 572
 35573            if (_wildcard != null)
 574            {
 2575                _wildcard.Lookup(numMatchedSegments, result.RelativePathSegments,
 2576                    result.BoundVariables);
 577            }
 33578            else if (numMatchedSegments < _segments.Count)
 579            {
 1580                BindTerminalDefaults(numMatchedSegments, result.BoundVariables);
 581            }
 582
 35583            if (_queries.Count > 0)
 584            {
 4585                foreach (KeyValuePair<string, UriTemplateQueryValue> kvp in _queries)
 586                {
 1587                    kvp.Value.Lookup(result.QueryParameters[kvp.Key], result.BoundVariables);
 588                    //UriTemplateHelpers.AssertCanonical(varName);
 589                }
 590            }
 591
 35592            if (_additionalDefaults != null)
 593            {
 0594                foreach (KeyValuePair<string, string> kvp in _additionalDefaults)
 595                {
 0596                    result.BoundVariables.Add(kvp.Key, UnescapeDefaultValue(kvp.Value));
 597                }
 598            }
 599
 600            Fx.Assert(result.RelativePathSegments.Count - numMatchedSegments >= 0, "bad segment computation");
 35601            result.SetWildcardPathSegmentsStart(numMatchedSegments);
 602
 35603            return result;
 604        }
 605
 606        internal bool IsPathPartiallyEquivalentAt(UriTemplate other, int segmentsCount)
 607        {
 608            // Refer to the note on template equivalency at IsEquivalentTo
 609            // This method checks if any uri with given number of segments, which can be matched
 610            //  by this template, can be also matched by the other template.
 611            Fx.Assert(segmentsCount >= _firstOptionalSegment - 1, "How can that be? The Trie is constructed that way!");
 612            Fx.Assert(segmentsCount <= _segments.Count, "How can that be? The Trie is constructed that way!");
 613            Fx.Assert(segmentsCount >= other._firstOptionalSegment - 1, "How can that be? The Trie is constructed that w
 614            Fx.Assert(segmentsCount <= other._segments.Count, "How can that be? The Trie is constructed that way!");
 615
 0616            for (int i = 0; i < segmentsCount; ++i)
 617            {
 0618                if (!_segments[i].IsEquivalentTo(other._segments[i],
 0619                    ((i == segmentsCount - 1) && (IgnoreTrailingSlash || other.IgnoreTrailingSlash))))
 620                {
 0621                    return false;
 622                }
 623            }
 624
 0625            return true;
 626        }
 627
 628        internal bool IsQueryEquivalent(UriTemplate other)
 629        {
 0630            if (_queries.Count != other._queries.Count)
 631            {
 0632                return false;
 633            }
 634
 0635            foreach (string key in _queries.Keys)
 636            {
 0637                UriTemplateQueryValue utqv = _queries[key];
 0638                if (!other._queries.TryGetValue(key, out UriTemplateQueryValue otherUtqv))
 639                {
 0640                    return false;
 641                }
 642
 0643                if (!utqv.IsEquivalentTo(otherUtqv))
 644                {
 0645                    return false;
 646                }
 647            }
 648
 0649            return true;
 0650        }
 651
 652        internal static Uri RewriteUri(Uri uri, string host)
 653        {
 0654            if (!string.IsNullOrEmpty(host))
 655            {
 0656                string originalHostHeader = uri.Host + ((!uri.IsDefaultPort) ? ":" + uri.Port.ToString(CultureInfo.Invar
 0657                if (!string.Equals(originalHostHeader, host, StringComparison.OrdinalIgnoreCase))
 658                {
 0659                    var sourceUri = new Uri(string.Format(CultureInfo.InvariantCulture, "{0}://{1}", uri.Scheme, host));
 0660                    return (new UriBuilder(uri) { Host = sourceUri.Host, Port = sourceUri.Port }).Uri;
 661                }
 662            }
 663
 0664            return uri;
 665        }
 666
 667        private Uri Bind(Uri baseAddress, BindInformation bindInfo, bool omitDefaults)
 668        {
 115669            UriBuilder result = new UriBuilder(baseAddress);
 115670            int parameterIndex = 0;
 115671            int lastPathParameter = ((_variables == null) ? -1 : _variables.PathSegmentVariableNames.Count - 1);
 672            int lastPathParameterToBind;
 115673            if (lastPathParameter == -1)
 674            {
 115675                lastPathParameterToBind = -1;
 676            }
 0677            else if (omitDefaults)
 678            {
 0679                lastPathParameterToBind = bindInfo.LastNonDefaultPathParameter;
 680            }
 681            else
 682            {
 0683                lastPathParameterToBind = bindInfo.LastNonNullablePathParameter;
 684            }
 685
 115686            string[] parameters = bindInfo.NormalizedParameters;
 115687            IDictionary<string, string> extraQueryParameters = bindInfo.AdditionalParameters;
 688            // Binding the path :
 115689            StringBuilder pathString = new StringBuilder(result.Path);
 115690            if (pathString[pathString.Length - 1] != '/')
 691            {
 114692                pathString.Append('/');
 693            }
 694
 115695            if (lastPathParameterToBind < lastPathParameter)
 696            {
 697                // Binding all the parameters we need
 0698                int segmentIndex = 0;
 0699                while (parameterIndex <= lastPathParameterToBind)
 700                {
 701                    Fx.Assert(segmentIndex < _segments.Count,
 702                        "Calculation of LastNonDefaultPathParameter,lastPathParameter or parameterIndex failed");
 0703                    _segments[segmentIndex++].Bind(parameters, ref parameterIndex, pathString);
 704                }
 705                Fx.Assert(parameterIndex == lastPathParameterToBind + 1,
 706                    "That is the exit criteria from the loop");
 707                // Maybe we have some literals yet to bind
 708                Fx.Assert(segmentIndex < _segments.Count,
 709                    "Calculation of LastNonDefaultPathParameter,lastPathParameter or parameterIndex failed");
 0710                while (_segments[segmentIndex].Nature == UriTemplatePartType.Literal)
 711                {
 0712                    _segments[segmentIndex++].Bind(parameters, ref parameterIndex, pathString);
 713                    Fx.Assert(parameterIndex == lastPathParameterToBind + 1,
 714                        "We have moved the parameter index in a literal binding");
 715                    Fx.Assert(segmentIndex < _segments.Count,
 716                        "Calculation of LastNonDefaultPathParameter,lastPathParameter or parameterIndex failed");
 717                }
 718                // We're done; skip to the beggining of the query parameters
 0719                parameterIndex = lastPathParameter + 1;
 720            }
 115721            else if (_segments.Count > 0 || _wildcard != null)
 722            {
 468723                for (int i = 0; i < _segments.Count; i++)
 724                {
 119725                    _segments[i].Bind(parameters, ref parameterIndex, pathString);
 726                }
 727
 115728                if (_wildcard != null)
 729                {
 0730                    _wildcard.Bind(parameters, ref parameterIndex, pathString);
 731                }
 732            }
 733
 115734            if (IgnoreTrailingSlash && (pathString[pathString.Length - 1] == '/'))
 735            {
 0736                pathString.Remove(pathString.Length - 1, 1);
 737            }
 738
 115739            result.Path = pathString.ToString();
 740            // Binding the query :
 115741            if ((_queries.Count != 0) || (extraQueryParameters != null))
 742            {
 0743                StringBuilder query = new StringBuilder("");
 0744                foreach (string key in _queries.Keys)
 745                {
 0746                    _queries[key].Bind(key, parameters, ref parameterIndex, query);
 747                }
 748
 0749                if (extraQueryParameters != null)
 750                {
 0751                    foreach (string key in extraQueryParameters.Keys)
 752                    {
 0753                        if (_queries.ContainsKey(key.ToUpperInvariant()))
 754                        {
 755                            // This can only be if the key passed has the same name as some literal key
 0756                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(parameters), SR.Format(
 0757                                SR.UTBothLiteralAndNameValueCollectionKey, key));
 758                        }
 0759                        string value = extraQueryParameters[key];
 0760                        string escapedValue = (string.IsNullOrEmpty(value) ? string.Empty : UrlUtility.UrlEncode(value, 
 0761                        query.AppendFormat("&{0}={1}", UrlUtility.UrlEncode(key, Encoding.UTF8), escapedValue);
 762                    }
 763                }
 764
 0765                if (query.Length != 0)
 766                {
 0767                    query.Remove(0, 1); // remove extra leading '&'
 768                }
 769
 0770                result.Query = query.ToString();
 771            }
 772
 773            // Adding the fragment (if needed)
 115774            if (_fragment != null)
 775            {
 115776                result.Fragment = _fragment;
 777            }
 778
 115779            return result.Uri;
 780        }
 781
 782        private void BindTerminalDefaults(int numMatchedSegments, NameValueCollection boundParameters)
 783        {
 784            Fx.Assert(!HasWildcard, "There are no terminal default when ends with wildcard");
 785            Fx.Assert(numMatchedSegments < _segments.Count, "Otherwise - no defaults to bind");
 786            Fx.Assert(_variables != null, "Otherwise - no default values to bind");
 787            Fx.Assert(_variables.DefaultValues != null, "Otherwise - no default values to bind");
 788
 4789            for (int i = numMatchedSegments; i < _segments.Count; i++)
 790            {
 1791                switch (_segments[i].Nature)
 792                {
 793                    case UriTemplatePartType.Variable:
 794                        {
 1795                            UriTemplateVariablePathSegment vps = _segments[i] as UriTemplateVariablePathSegment;
 796                            Fx.Assert(vps != null, "How can that be? That its nature");
 1797                            _variables.LookupDefault(vps.VarName, boundParameters);
 798                        }
 799                        break;
 800
 801                    default:
 802                        Fx.Assert("We only support terminal defaults on Variable segments");
 803                        break;
 804                }
 805            }
 1806        }
 807
 808        private bool IsCandidatePathMatch(int numSegmentsInBaseAddress, string[] candidateSegments,
 809            out int numMatchedSegments, out Collection<string> relativeSegments)
 810        {
 0811            int numRelativeSegments = candidateSegments.Length - numSegmentsInBaseAddress;
 812            Fx.Assert(numRelativeSegments >= 0, "bad segments num");
 0813            relativeSegments = new Collection<string>();
 0814            bool isStillMatch = true;
 0815            int relativeSegmentsIndex = 0;
 816
 0817            while (isStillMatch && (relativeSegmentsIndex < numRelativeSegments))
 818            {
 0819                string segment = candidateSegments[relativeSegmentsIndex + numSegmentsInBaseAddress];
 820                // Mathcing to next regular segment in the template (if there is one); building the wire segment represe
 0821                if (relativeSegmentsIndex < _segments.Count)
 822                {
 0823                    bool ignoreSlash = (this.IgnoreTrailingSlash && (relativeSegmentsIndex == numRelativeSegments - 1));
 0824                    UriTemplateLiteralPathSegment lps = UriTemplateLiteralPathSegment.CreateFromWireData(segment);
 0825                    if (!_segments[relativeSegmentsIndex].IsMatch(lps, ignoreSlash))
 826                    {
 0827                        isStillMatch = false;
 0828                        break;
 829                    }
 830
 0831                    string relPathSeg = Uri.UnescapeDataString(segment);
 0832                    if (lps.EndsWithSlash)
 833                    {
 834                        Fx.Assert(relPathSeg.EndsWith("/", StringComparison.Ordinal), "problem with relative path segmen
 0835                        relPathSeg = relPathSeg.Substring(0, relPathSeg.Length - 1); // trim slash
 836                    }
 837
 0838                    relativeSegments.Add(relPathSeg);
 839                }
 840                // Checking if the template has a wild card ('*') or a final star var segment ("{*<var name>}"
 0841                else if (HasWildcard)
 842                {
 843                    break;
 844                }
 845                else
 846                {
 0847                    isStillMatch = false;
 0848                    break;
 849                }
 850
 0851                relativeSegmentsIndex++;
 852            }
 853
 0854            if (isStillMatch)
 855            {
 0856                numMatchedSegments = relativeSegmentsIndex;
 857                // building the wire representation to segments that were matched to a wild card
 0858                if (relativeSegmentsIndex < numRelativeSegments)
 859                {
 0860                    while (relativeSegmentsIndex < numRelativeSegments)
 861                    {
 0862                        string relPathSeg = Uri.UnescapeDataString(candidateSegments[relativeSegmentsIndex + numSegments
 0863                        if (relPathSeg.EndsWith("/", StringComparison.Ordinal))
 864                        {
 0865                            relPathSeg = relPathSeg.Substring(0, relPathSeg.Length - 1); // trim slash
 866                        }
 0867                        relativeSegments.Add(relPathSeg);
 0868                        relativeSegmentsIndex++;
 869                    }
 870                }
 871                // Checking if we matched all required segments already
 0872                else if (numMatchedSegments < _firstOptionalSegment)
 873                {
 0874                    isStillMatch = false;
 875                }
 876            }
 877            else
 878            {
 0879                numMatchedSegments = 0;
 880            }
 881
 0882            return isStillMatch;
 883        }
 884
 885        private bool IsPathFullyEquivalent(UriTemplate other)
 886        {
 887            // Refer to the note on template equivalency at IsEquivalentTo
 888            // This method checks if both templates has a fully equivalent path.
 0889            if (HasWildcard != other.HasWildcard)
 890            {
 0891                return false;
 892            }
 893
 0894            if (_segments.Count != other._segments.Count)
 895            {
 0896                return false;
 897            }
 898
 0899            for (int i = 0; i < _segments.Count; ++i)
 900            {
 0901                if (!_segments[i].IsEquivalentTo(other._segments[i],
 0902                    (i == _segments.Count - 1) && !HasWildcard && (IgnoreTrailingSlash || other.IgnoreTrailingSlash)))
 903                {
 0904                    return false;
 905                }
 906            }
 907
 0908            return true;
 909        }
 910
 911        private BindInformation PrepareBindInformation(IDictionary<string, string> parameters, bool omitDefaults)
 912        {
 0913            if (parameters == null)
 914            {
 0915                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(parameters));
 916            }
 917
 0918            IDictionary<string, string> extraParameters = new Dictionary<string, string>(UriTemplateHelpers.GetQueryKeyC
 0919            foreach (KeyValuePair<string, string> kvp in parameters)
 920            {
 0921                if (string.IsNullOrEmpty(kvp.Key))
 922                {
 0923                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(parameters),
 0924                        SR.Format(SR.UTBindByNameCalledWithEmptyKey));
 925                }
 926
 0927                extraParameters.Add(kvp);
 928            }
 929
 0930            ProcessDefaultsAndCreateBindInfo(omitDefaults, extraParameters, out BindInformation bindInfo);
 931
 0932            return bindInfo;
 933        }
 934
 935        private BindInformation PrepareBindInformation(NameValueCollection parameters, bool omitDefaults)
 936        {
 0937            if (parameters == null)
 938            {
 0939                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(parameters));
 940            }
 941
 0942            IDictionary<string, string> extraParameters = new Dictionary<string, string>(UriTemplateHelpers.GetQueryKeyC
 0943            foreach (string key in parameters.AllKeys)
 944            {
 0945                if (string.IsNullOrEmpty(key))
 946                {
 0947                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(parameters),
 0948                        SR.Format(SR.UTBindByNameCalledWithEmptyKey));
 949                }
 950
 0951                extraParameters.Add(key, parameters[key]);
 952            }
 953
 0954            ProcessDefaultsAndCreateBindInfo(omitDefaults, extraParameters, out BindInformation bindInfo);
 955
 0956            return bindInfo;
 957        }
 958
 959        private void ProcessDefaultsAndCreateBindInfo(bool omitDefaults, IDictionary<string, string> extraParameters,
 960            out BindInformation bindInfo)
 961        {
 962            Fx.Assert(extraParameters != null, "We are expected to create it at the calling PrepareBindInformation");
 963
 0964            if (_additionalDefaults != null)
 965            {
 0966                if (omitDefaults)
 967                {
 0968                    foreach (KeyValuePair<string, string> kvp in _additionalDefaults)
 969                    {
 0970                        if (extraParameters.TryGetValue(kvp.Key, out string extraParameter))
 971                        {
 0972                            if (string.Compare(extraParameter, kvp.Value, StringComparison.Ordinal) == 0)
 973                            {
 0974                                extraParameters.Remove(kvp.Key);
 975                            }
 976                        }
 977                    }
 978                }
 979                else
 980                {
 0981                    foreach (KeyValuePair<string, string> kvp in _additionalDefaults)
 982                    {
 0983                        if (!extraParameters.ContainsKey(kvp.Key))
 984                        {
 0985                            extraParameters.Add(kvp.Key, kvp.Value);
 986                        }
 987                    }
 988                }
 989            }
 990
 0991            if (extraParameters.Count == 0)
 992            {
 0993                extraParameters = null;
 994            }
 995
 0996            bindInfo = new BindInformation(extraParameters);
 0997        }
 998
 999        private string UnescapeDefaultValue(string escapedValue)
 1000        {
 11001            if (string.IsNullOrEmpty(escapedValue))
 1002            {
 01003                return escapedValue;
 1004            }
 1005
 11006            if (_unescapedDefaults == null)
 1007            {
 11008                _unescapedDefaults = new ConcurrentDictionary<string, string>(StringComparer.Ordinal);
 1009            }
 1010
 11011            return _unescapedDefaults.GetOrAdd(escapedValue, Uri.UnescapeDataString);
 1012        }
 1013
 1014        internal struct BindInformation
 1015        {
 1016            public BindInformation(string[] normalizedParameters, int lastNonDefaultPathParameter,
 1017                int lastNonNullablePathParameter, IDictionary<string, string> additionalParameters)
 1018            {
 01019                NormalizedParameters = normalizedParameters;
 01020                LastNonDefaultPathParameter = lastNonDefaultPathParameter;
 01021                LastNonNullablePathParameter = lastNonNullablePathParameter;
 01022                AdditionalParameters = additionalParameters;
 01023            }
 1024
 1025            public BindInformation(IDictionary<string, string> additionalParameters)
 1026            {
 1151027                NormalizedParameters = null;
 1151028                LastNonDefaultPathParameter = -1;
 1151029                LastNonNullablePathParameter = -1;
 1151030                AdditionalParameters = additionalParameters;
 1151031            }
 1032
 1151033            public IDictionary<string, string> AdditionalParameters { get; }
 01034            public int LastNonDefaultPathParameter { get; }
 01035            public int LastNonNullablePathParameter { get; }
 1151036            public string[] NormalizedParameters { get; }
 1037        }
 1038
 1039        internal class UriTemplateDefaults : IDictionary<string, string>
 1040        {
 1041            private readonly Dictionary<string, string> _defaults;
 1042            private readonly ReadOnlyCollection<string> _keys;
 1043            private readonly ReadOnlyCollection<string> _values;
 1044
 01045            public UriTemplateDefaults(UriTemplate template)
 1046            {
 01047                _defaults = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 01048                if ((template._variables != null) && (template._variables.DefaultValues != null))
 1049                {
 01050                    foreach (KeyValuePair<string, string> kvp in template._variables.DefaultValues)
 1051                    {
 01052                        _defaults.Add(kvp.Key, kvp.Value);
 1053                    }
 1054                }
 1055
 01056                if (template._additionalDefaults != null)
 1057                {
 01058                    foreach (KeyValuePair<string, string> kvp in template._additionalDefaults)
 1059                    {
 01060                        _defaults.Add(kvp.Key.ToUpperInvariant(), kvp.Value);
 1061                    }
 1062                }
 1063
 01064                _keys = new ReadOnlyCollection<string>(new List<string>(_defaults.Keys));
 01065                _values = new ReadOnlyCollection<string>(new List<string>(_defaults.Values));
 01066            }
 1067
 01068            public int Count => _defaults.Count;
 1069
 01070            public bool IsReadOnly => true;
 1071
 01072            public ICollection<string> Keys => _keys;
 1073
 01074            public ICollection<string> Values => _values;
 1075
 1076            public string this[string key]
 1077            {
 1078                get
 1079                {
 01080                    return _defaults[key];
 1081                }
 1082                set
 1083                {
 01084                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(
 01085                        SR.Format(SR.UTDefaultValuesAreImmutable)));
 1086                }
 1087            }
 1088
 1089            public void Add(string key, string value)
 1090            {
 01091                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(
 01092                    SR.Format(SR.UTDefaultValuesAreImmutable)));
 1093            }
 1094
 1095            public void Add(KeyValuePair<string, string> item)
 1096            {
 01097                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(
 01098                    SR.Format(SR.UTDefaultValuesAreImmutable)));
 1099            }
 1100
 1101            public void Clear()
 1102            {
 01103                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(
 01104                    SR.Format(SR.UTDefaultValuesAreImmutable)));
 1105            }
 1106
 1107            public bool Contains(KeyValuePair<string, string> item)
 1108            {
 01109                return (_defaults as ICollection<KeyValuePair<string, string>>).Contains(item);
 1110            }
 1111
 1112            public bool ContainsKey(string key)
 1113            {
 01114                return _defaults.ContainsKey(key);
 1115            }
 1116
 1117            public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
 1118            {
 01119                (_defaults as ICollection<KeyValuePair<string, string>>).CopyTo(array, arrayIndex);
 01120            }
 1121
 1122            public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
 1123            {
 01124                return _defaults.GetEnumerator();
 1125            }
 1126
 1127            public bool Remove(string key)
 1128            {
 01129                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(
 01130                    SR.Format(SR.UTDefaultValuesAreImmutable)));
 1131            }
 1132
 1133            public bool Remove(KeyValuePair<string, string> item)
 1134            {
 01135                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(
 01136                    SR.Format(SR.UTDefaultValuesAreImmutable)));
 1137            }
 1138
 1139            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
 1140            {
 01141                return _defaults.GetEnumerator();
 1142            }
 1143
 1144            public bool TryGetValue(string key, out string value)
 1145            {
 01146                return _defaults.TryGetValue(key, out value);
 1147            }
 1148        }
 1149
 1150        internal class VariablesCollection
 1151        {
 1152            private readonly UriTemplate _owner;
 1153            private static ReadOnlyCollection<string> s_emptyStringCollection = null;
 1154            private int _firstNullablePathVariable;
 1155            private readonly List<string> _pathSegmentVariableNames; // ToUpperInvariant, in order they occur in the ori
 1156            private ReadOnlyCollection<string> _pathSegmentVariableNamesSnapshot = null;
 1157            private readonly List<UriTemplatePartType> _pathSegmentVariableNature;
 1158            private List<string> _queryValueVariableNames; // ToUpperInvariant, in order they occur in the original temp
 1159            private ReadOnlyCollection<string> _queryValueVariableNamesSnapshot = null;
 1160
 1251161            public VariablesCollection(UriTemplate owner)
 1162            {
 1251163                _owner = owner;
 1251164                _pathSegmentVariableNames = new List<string>();
 1251165                _pathSegmentVariableNature = new List<UriTemplatePartType>();
 1251166                _queryValueVariableNames = new List<string>();
 1251167                _firstNullablePathVariable = -1;
 1251168            }
 1169
 1170            public static ReadOnlyCollection<string> EmptyCollection
 1171            {
 1172                get
 1173                {
 5281174                    if (s_emptyStringCollection == null)
 1175                    {
 11176                        s_emptyStringCollection = new ReadOnlyCollection<string>(new List<string>());
 1177                    }
 1178
 5281179                    return s_emptyStringCollection;
 1180                }
 1181            }
 1182
 2461183            public Dictionary<string, string> DefaultValues { get; private set; }
 1184
 1185            public ReadOnlyCollection<string> PathSegmentVariableNames
 1186            {
 1187                get
 1188                {
 851189                    if (_pathSegmentVariableNamesSnapshot == null)
 1190                    {
 851191                        Interlocked.CompareExchange(ref _pathSegmentVariableNamesSnapshot, new ReadOnlyCollection<string
 851192                            _pathSegmentVariableNames), null);
 1193                    }
 1194
 851195                    return _pathSegmentVariableNamesSnapshot;
 1196                }
 1197            }
 1198
 1199            public ReadOnlyCollection<string> QueryValueVariableNames
 1200            {
 1201                get
 1202                {
 831203                    if (_queryValueVariableNamesSnapshot == null)
 1204                    {
 831205                        Interlocked.CompareExchange(ref _queryValueVariableNamesSnapshot, new ReadOnlyCollection<string>
 831206                            _queryValueVariableNames), null);
 1207                    }
 1208
 831209                    return _queryValueVariableNamesSnapshot;
 1210                }
 1211            }
 1212
 1213            public void AddDefaultValue(string varName, string value)
 1214            {
 01215                int varIndex = _pathSegmentVariableNames.IndexOf(varName);
 1216                Fx.Assert(varIndex != -1, "Adding default value is restricted to path variables");
 01217                if ((_owner._wildcard != null) && _owner._wildcard.HasVariable &&
 01218                    (varIndex == _pathSegmentVariableNames.Count - 1))
 1219                {
 01220                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 01221                        SR.Format(SR.UTStarVariableWithDefaultsFromAdditionalDefaults,
 01222                        _owner._originalTemplate, varName)));
 1223                }
 1224
 01225                if (_pathSegmentVariableNature[varIndex] != UriTemplatePartType.Variable)
 1226                {
 01227                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 01228                        SR.Format(SR.UTDefaultValueToCompoundSegmentVarFromAdditionalDefaults,
 01229                        _owner._originalTemplate, varName)));
 1230                }
 1231
 01232                if (string.IsNullOrEmpty(value) ||
 01233                    (string.Compare(value, NullableDefault, StringComparison.OrdinalIgnoreCase) == 0))
 1234                {
 01235                    value = null;
 1236                }
 1237
 01238                if (DefaultValues == null)
 1239                {
 01240                    DefaultValues = new Dictionary<string, string>();
 1241                }
 1242
 01243                DefaultValues.Add(varName, value);
 01244            }
 1245
 1246            public string AddPathVariable(UriTemplatePartType sourceNature, string varDeclaration, out bool hasDefaultVa
 1247            {
 1248                Fx.Assert(sourceNature != UriTemplatePartType.Literal, "Literal path segments can't be the source for pa
 1249
 1241250                ParseVariableDeclaration(varDeclaration, out string varName, out string defaultValue);
 1241251                hasDefaultValue = (defaultValue != null);
 1241252                if (varName.IndexOf(WildcardPath, StringComparison.Ordinal) != -1)
 1253                {
 01254                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
 01255                        SR.Format(SR.UTInvalidWildcardInVariableOrLiteral, _owner._originalTemplate, WildcardPath)));
 1256                }
 1257
 1241258                string uppercaseVarName = varName.ToUpperInvariant();
 1241259                if (_pathSegmentVariableNames.Contains(uppercaseVarName) ||
 1241260                    _queryValueVariableNames.Contains(uppercaseVarName))
 1261                {
 01262                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 01263                        SR.Format(SR.UTVarNamesMustBeUnique, _owner._originalTemplate, varName)));
 1264                }
 1265
 1241266                _pathSegmentVariableNames.Add(uppercaseVarName);
 1241267                _pathSegmentVariableNature.Add(sourceNature);
 1241268                if (hasDefaultValue)
 1269                {
 241270                    if (defaultValue == string.Empty)
 1271                    {
 01272                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 01273                            SR.Format(SR.UTInvalidDefaultPathValue, _owner._originalTemplate,
 01274                            varDeclaration, varName)));
 1275                    }
 1276
 241277                    if (string.Compare(defaultValue, NullableDefault, StringComparison.OrdinalIgnoreCase) == 0)
 1278                    {
 01279                        defaultValue = null;
 1280                    }
 1281
 241282                    if (DefaultValues == null)
 1283                    {
 241284                        DefaultValues = new Dictionary<string, string>();
 1285                    }
 1286
 241287                    DefaultValues.Add(uppercaseVarName, defaultValue);
 1288                }
 1289
 1241290                return uppercaseVarName;
 1291            }
 1292
 1293            public string AddQueryVariable(string varDeclaration)
 1294            {
 291295                ParseVariableDeclaration(varDeclaration, out string varName, out string defaultValue);
 291296                if (varName.IndexOf(WildcardPath, StringComparison.Ordinal) != -1)
 1297                {
 01298                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
 01299                        SR.Format(SR.UTInvalidWildcardInVariableOrLiteral, _owner._originalTemplate, WildcardPath)));
 1300                }
 1301
 291302                if (defaultValue != null)
 1303                {
 01304                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 01305                        SR.Format(SR.UTDefaultValueToQueryVar, _owner._originalTemplate,
 01306                        varDeclaration, varName)));
 1307                }
 1308
 291309                string uppercaseVarName = varName.ToUpperInvariant();
 291310                if (_pathSegmentVariableNames.Contains(uppercaseVarName) ||
 291311                    _queryValueVariableNames.Contains(uppercaseVarName))
 1312                {
 01313                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 01314                        SR.Format(SR.UTVarNamesMustBeUnique, _owner._originalTemplate, varName)));
 1315                }
 291316                _queryValueVariableNames.Add(uppercaseVarName);
 1317
 291318                return uppercaseVarName;
 1319            }
 1320
 1321            public void LookupDefault(string varName, NameValueCollection boundParameters)
 1322            {
 1323                Fx.Assert(DefaultValues.ContainsKey(varName), "Otherwise, we don't have a value to bind");
 1324
 11325                boundParameters.Add(varName, _owner.UnescapeDefaultValue(DefaultValues[varName]));
 11326            }
 1327
 1328            public BindInformation PrepareBindInformation(IDictionary<string, string> parameters, bool omitDefaults)
 1329            {
 01330                if (parameters == null)
 1331                {
 01332                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(parameters));
 1333                }
 1334
 01335                string[] normalizedParameters = PrepareNormalizedParameters();
 01336                IDictionary<string, string> extraParameters = null;
 01337                foreach (string key in parameters.Keys)
 1338                {
 01339                    ProcessBindParameter(key, parameters[key], normalizedParameters, ref extraParameters);
 1340                }
 1341
 01342                ProcessDefaultsAndCreateBindInfo(omitDefaults, normalizedParameters, extraParameters, out BindInformatio
 1343
 01344                return bindInfo;
 1345            }
 1346
 1347            public BindInformation PrepareBindInformation(NameValueCollection parameters, bool omitDefaults)
 1348            {
 01349                if (parameters == null)
 1350                {
 01351                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(parameters));
 1352                }
 1353
 01354                string[] normalizedParameters = PrepareNormalizedParameters();
 01355                IDictionary<string, string> extraParameters = null;
 01356                foreach (string key in parameters.AllKeys)
 1357                {
 01358                    ProcessBindParameter(key, parameters[key], normalizedParameters, ref extraParameters);
 1359                }
 1360
 01361                ProcessDefaultsAndCreateBindInfo(omitDefaults, normalizedParameters, extraParameters, out BindInformatio
 1362
 01363                return bindInfo;
 1364            }
 1365
 1366            public BindInformation PrepareBindInformation(params string[] parameters)
 1367            {
 01368                if (parameters == null)
 1369                {
 01370                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(parameters));
 1371                }
 1372
 01373                if ((parameters.Length < _pathSegmentVariableNames.Count) ||
 01374                    (parameters.Length > _pathSegmentVariableNames.Count + _queryValueVariableNames.Count))
 1375                {
 01376                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
 01377                        SR.Format(SR.UTBindByPositionWrongCount, _owner._originalTemplate,
 01378                        _pathSegmentVariableNames.Count, _queryValueVariableNames.Count,
 01379                        parameters.Length)));
 1380                }
 1381
 1382                string[] normalizedParameters;
 01383                if (parameters.Length == _pathSegmentVariableNames.Count + _queryValueVariableNames.Count)
 1384                {
 01385                    normalizedParameters = parameters;
 1386                }
 1387                else
 1388                {
 01389                    normalizedParameters = new string[_pathSegmentVariableNames.Count + _queryValueVariableNames.Count];
 01390                    parameters.CopyTo(normalizedParameters, 0);
 01391                    for (int i = parameters.Length; i < normalizedParameters.Length; i++)
 1392                    {
 01393                        normalizedParameters[i] = null;
 1394                    }
 1395                }
 1396
 01397                LoadDefaultsAndValidate(normalizedParameters, out int lastNonDefaultPathParameter,
 01398                    out int lastNonNullablePathParameter);
 1399
 01400                return new BindInformation(normalizedParameters, lastNonDefaultPathParameter,
 01401                    lastNonNullablePathParameter, _owner._additionalDefaults);
 1402            }
 1403
 1404            public void ValidateDefaults(out int firstOptionalSegment)
 1405            {
 1406                Fx.Assert(DefaultValues != null, "We are checking this condition from the c'tor");
 1407                Fx.Assert(_pathSegmentVariableNames.Count > 0, "Otherwise, how can we have default values");
 1408
 1409                // Finding the first valid nullable defaults
 961410                for (int i = _pathSegmentVariableNames.Count - 1; (i >= 0) && (_firstNullablePathVariable == -1); i--)
 1411                {
 241412                    string varName = _pathSegmentVariableNames[i];
 241413                    if (!DefaultValues.TryGetValue(varName, out string defaultValue))
 1414                    {
 01415                        _firstNullablePathVariable = i + 1;
 1416                    }
 241417                    else if (defaultValue != null)
 1418                    {
 241419                        _firstNullablePathVariable = i + 1;
 1420                    }
 1421                }
 1422
 241423                if (_firstNullablePathVariable == -1)
 1424                {
 01425                    _firstNullablePathVariable = 0;
 1426                }
 1427
 1428                // Making sure that there are no nullables to the left of the first valid nullable
 241429                if (_firstNullablePathVariable > 1)
 1430                {
 01431                    for (int i = _firstNullablePathVariable - 2; i >= 0; i--)
 1432                    {
 01433                        string varName = _pathSegmentVariableNames[i];
 01434                        if (DefaultValues.TryGetValue(varName, out string defaultValue))
 1435                        {
 01436                            if (defaultValue == null)
 1437                            {
 01438                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 01439                                    SR.Format(SR.UTNullableDefaultMustBeFollowedWithNullables, _owner._originalTemplate,
 01440                                    varName, _pathSegmentVariableNames[i + 1])));
 1441                            }
 1442                        }
 1443                    }
 1444                }
 1445
 1446                // Making sure that there are no Literals\WildCards to the right
 1447                // Based on the fact that only Variable Path Segments support default values,
 1448                //  if firstNullablePathVariable=N and pathSegmentVariableNames.Count=M then
 1449                //  the nature of the last M-N path segments should be StringNature.Variable; otherwise,
 1450                //  there was a literal segment in between. Also, there shouldn't be a wildcard.
 241451                if (_firstNullablePathVariable < _pathSegmentVariableNames.Count)
 1452                {
 01453                    if (_owner.HasWildcard)
 1454                    {
 01455                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 01456                            SR.Format(SR.UTNullableDefaultMustNotBeFollowedWithWildcard,
 01457                            _owner._originalTemplate, _pathSegmentVariableNames[_firstNullablePathVariable])));
 1458                    }
 1459
 01460                    for (int i = _pathSegmentVariableNames.Count - 1; i >= _firstNullablePathVariable; i--)
 1461                    {
 01462                        int segmentIndex = _owner._segments.Count - (_pathSegmentVariableNames.Count - i);
 01463                        if (_owner._segments[segmentIndex].Nature != UriTemplatePartType.Variable)
 1464                        {
 01465                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 01466                                SR.Format(SR.UTNullableDefaultMustNotBeFollowedWithLiteral,
 01467                                _owner._originalTemplate, _pathSegmentVariableNames[_firstNullablePathVariable],
 01468                                _owner._segments[segmentIndex].OriginalSegment)));
 1469                        }
 1470                    }
 1471                }
 1472
 1473                // Now that we have the firstNullablePathVariable set, lets calculate the firstOptionalSegment.
 1474                //  We already knows that the last M-N path segments (when M=pathSegmentVariableNames.Count and
 1475                //  N=firstNullablePathVariable) are optional (see the previos comment). We will start there and
 1476                //  move to the left, stopping at the first segment, which is not a variable or is a variable
 1477                //  and doesn't have a default value.
 241478                int numNullablePathVariables = (_pathSegmentVariableNames.Count - _firstNullablePathVariable);
 241479                firstOptionalSegment = _owner._segments.Count - numNullablePathVariables;
 241480                if (!_owner.HasWildcard)
 1481                {
 481482                    while (firstOptionalSegment > 0)
 1483                    {
 481484                        UriTemplatePathSegment ps = _owner._segments[firstOptionalSegment - 1];
 481485                        if (ps.Nature != UriTemplatePartType.Variable)
 1486                        {
 1487                            break;
 1488                        }
 1489
 241490                        UriTemplateVariablePathSegment vps = (ps as UriTemplateVariablePathSegment);
 1491                        Fx.Assert(vps != null, "Should be; that's his nature");
 241492                        if (!DefaultValues.ContainsKey(vps.VarName))
 1493                        {
 1494                            break;
 1495                        }
 1496
 241497                        firstOptionalSegment--;
 1498                    }
 1499                }
 241500            }
 1501
 1502            private void AddAdditionalDefaults(ref IDictionary<string, string> extraParameters)
 1503            {
 01504                if (extraParameters == null)
 1505                {
 01506                    extraParameters = _owner._additionalDefaults;
 1507                }
 1508                else
 1509                {
 01510                    foreach (KeyValuePair<string, string> kvp in _owner._additionalDefaults)
 1511                    {
 01512                        if (!extraParameters.ContainsKey(kvp.Key))
 1513                        {
 01514                            extraParameters.Add(kvp.Key, kvp.Value);
 1515                        }
 1516                    }
 1517                }
 01518            }
 1519
 1520            private void LoadDefaultsAndValidate(string[] normalizedParameters, out int lastNonDefaultPathParameter,
 1521                out int lastNonNullablePathParameter)
 1522            {
 1523                // First step - loading defaults
 01524                for (int i = 0; i < _pathSegmentVariableNames.Count; i++)
 1525                {
 01526                    if (string.IsNullOrEmpty(normalizedParameters[i]) && (DefaultValues != null))
 1527                    {
 01528                        DefaultValues.TryGetValue(_pathSegmentVariableNames[i], out normalizedParameters[i]);
 1529                    }
 1530                }
 1531
 1532                // Second step - calculating bind constrains
 01533                lastNonDefaultPathParameter = _pathSegmentVariableNames.Count - 1;
 01534                if ((DefaultValues != null) &&
 01535                    (_owner._segments[_owner._segments.Count - 1].Nature != UriTemplatePartType.Literal))
 1536                {
 01537                    bool foundNonDefaultPathParameter = false;
 01538                    while (!foundNonDefaultPathParameter && (lastNonDefaultPathParameter >= 0))
 1539                    {
 01540                        if (DefaultValues.TryGetValue(_pathSegmentVariableNames[lastNonDefaultPathParameter],
 01541                            out string defaultValue))
 1542                        {
 01543                            if (string.Compare(normalizedParameters[lastNonDefaultPathParameter],
 01544                                defaultValue, StringComparison.Ordinal) != 0)
 1545                            {
 01546                                foundNonDefaultPathParameter = true;
 1547                            }
 1548                            else
 1549                            {
 01550                                lastNonDefaultPathParameter--;
 1551                            }
 1552                        }
 1553                        else
 1554                        {
 01555                            foundNonDefaultPathParameter = true;
 1556                        }
 1557                    }
 1558                }
 1559
 01560                if (_firstNullablePathVariable > lastNonDefaultPathParameter)
 1561                {
 01562                    lastNonNullablePathParameter = _firstNullablePathVariable - 1;
 1563                }
 1564                else
 1565                {
 01566                    lastNonNullablePathParameter = lastNonDefaultPathParameter;
 1567                }
 1568
 1569                // Third step - validate
 01570                for (int i = 0; i <= lastNonNullablePathParameter; i++)
 1571                {
 1572                    // Skip validation for terminating star variable segment :
 01573                    if (_owner.HasWildcard && _owner._wildcard.HasVariable &&
 01574                        (i == _pathSegmentVariableNames.Count - 1))
 1575                    {
 1576                        continue;
 1577                    }
 1578
 1579                    // Validate
 01580                    if (string.IsNullOrEmpty(normalizedParameters[i]))
 1581                    {
 01582                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(normalizedParameters),
 01583                            SR.Format(SR.BindUriTemplateToNullOrEmptyPathParam, _pathSegmentVariableNames[i]));
 1584                    }
 1585                }
 01586            }
 1587
 1588            private void ParseVariableDeclaration(string varDeclaration, out string varName, out string defaultValue)
 1589            {
 1531590                if ((varDeclaration.IndexOf('{') != -1) || (varDeclaration.IndexOf('}') != -1))
 1591                {
 01592                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
 01593                        SR.Format(SR.UTInvalidVarDeclaration, _owner._originalTemplate, varDeclaration)));
 1594                }
 1595
 1531596                int equalSignIndex = varDeclaration.IndexOf('=');
 1597                switch (equalSignIndex)
 1598                {
 1599                    case -1:
 1291600                        varName = varDeclaration;
 1291601                        defaultValue = null;
 1291602                        break;
 1603
 1604                    case 0:
 01605                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
 01606                            SR.Format(SR.UTInvalidVarDeclaration, _owner._originalTemplate, varDeclaration)));
 1607
 1608                    default:
 241609                        varName = varDeclaration.Substring(0, equalSignIndex);
 241610                        defaultValue = varDeclaration.Substring(equalSignIndex + 1);
 241611                        if (defaultValue.IndexOf('=') != -1)
 1612                        {
 01613                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
 01614                                SR.Format(SR.UTInvalidVarDeclaration, _owner._originalTemplate, varDeclaration)));
 1615                        }
 1616                        break;
 1617                }
 241618            }
 1619
 1620            private string[] PrepareNormalizedParameters()
 1621            {
 01622                string[] normalizedParameters = new string[_pathSegmentVariableNames.Count + _queryValueVariableNames.Co
 01623                for (int i = 0; i < normalizedParameters.Length; i++)
 1624                {
 01625                    normalizedParameters[i] = null;
 1626                }
 1627
 01628                return normalizedParameters;
 1629            }
 1630
 1631            private void ProcessBindParameter(string name, string value, string[] normalizedParameters,
 1632                ref IDictionary<string, string> extraParameters)
 1633            {
 01634                if (string.IsNullOrEmpty(name))
 1635                {
 01636                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(nameof(name),
 01637                        SR.Format(SR.UTBindByNameCalledWithEmptyKey));
 1638                }
 1639
 01640                string uppercaseVarName = name.ToUpperInvariant();
 01641                int pathVarIndex = _pathSegmentVariableNames.IndexOf(uppercaseVarName);
 01642                if (pathVarIndex != -1)
 1643                {
 01644                    normalizedParameters[pathVarIndex] = (string.IsNullOrEmpty(value) ? string.Empty : value);
 01645                    return;
 1646                }
 1647
 01648                int queryVarIndex = _queryValueVariableNames.IndexOf(uppercaseVarName);
 01649                if (queryVarIndex != -1)
 1650                {
 01651                    normalizedParameters[_pathSegmentVariableNames.Count + queryVarIndex] = (string.IsNullOrEmpty(value)
 01652                    return;
 1653                }
 1654
 01655                if (extraParameters == null)
 1656                {
 01657                    extraParameters = new Dictionary<string, string>(UriTemplateHelpers.GetQueryKeyComparer());
 1658                }
 1659
 01660                extraParameters.Add(name, value);
 01661            }
 1662
 1663            private void ProcessDefaultsAndCreateBindInfo(bool omitDefaults, string[] normalizedParameters,
 1664                IDictionary<string, string> extraParameters, out BindInformation bindInfo)
 1665            {
 01666                LoadDefaultsAndValidate(normalizedParameters, out int lastNonDefaultPathParameter,
 01667                    out int lastNonNullablePathParameter);
 01668                if (_owner._additionalDefaults != null)
 1669                {
 01670                    if (omitDefaults)
 1671                    {
 01672                        RemoveAdditionalDefaults(ref extraParameters);
 1673                    }
 1674                    else
 1675                    {
 01676                        AddAdditionalDefaults(ref extraParameters);
 1677                    }
 1678                }
 1679
 01680                bindInfo = new BindInformation(normalizedParameters, lastNonDefaultPathParameter,
 01681                    lastNonNullablePathParameter, extraParameters);
 01682            }
 1683
 1684            private void RemoveAdditionalDefaults(ref IDictionary<string, string> extraParameters)
 1685            {
 01686                if (extraParameters == null)
 1687                {
 01688                    return;
 1689                }
 1690
 01691                foreach (KeyValuePair<string, string> kvp in _owner._additionalDefaults)
 1692                {
 01693                    if (extraParameters.TryGetValue(kvp.Key, out string extraParameter))
 1694                    {
 01695                        if (string.Compare(extraParameter, kvp.Value, StringComparison.Ordinal) == 0)
 1696                        {
 01697                            extraParameters.Remove(kvp.Key);
 1698                        }
 1699                    }
 1700                }
 1701
 01702                if (extraParameters.Count == 0)
 1703                {
 01704                    extraParameters = null;
 1705                }
 01706            }
 1707        }
 1708
 1709        internal class WildcardInfo
 1710        {
 1711            private readonly UriTemplate _owner;
 1712            private readonly string _varName;
 1713
 241714            public WildcardInfo(UriTemplate owner)
 1715            {
 241716                _varName = null;
 241717                _owner = owner;
 241718            }
 1719
 241720            public WildcardInfo(UriTemplate owner, string segment)
 1721            {
 1722                Fx.Assert(!segment.EndsWith("/", StringComparison.Ordinal), "We are expecting to check this earlier");
 1723
 241724                _varName = owner.AddPathVariable(UriTemplatePartType.Variable,
 241725                    segment.Substring(1 + WildcardPath.Length, segment.Length - 2 - WildcardPath.Length),
 241726                    out bool hasDefault);
 1727
 1728                // Since this is a terminating star segment there shouldn't be a default
 241729                if (hasDefault)
 1730                {
 01731                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 01732                        SR. Format(SR.UTStarVariableWithDefaults, owner._originalTemplate,
 01733                        segment, _varName)));
 1734                }
 1735
 241736                _owner = owner;
 241737            }
 1738
 1739            internal bool HasVariable
 1740            {
 1741                get
 1742                {
 21743                    return (!string.IsNullOrEmpty(_varName));
 1744                }
 1745            }
 1746
 1747            public void Bind(string[] values, ref int valueIndex, StringBuilder path)
 1748            {
 01749                if (HasVariable)
 1750                {
 1751                    Fx.Assert(valueIndex < values.Length, "Not enough values to bind");
 01752                    if (string.IsNullOrEmpty(values[valueIndex]))
 1753                    {
 01754                        valueIndex++;
 1755                    }
 1756                    else
 1757                    {
 01758                        path.Append(values[valueIndex++]);
 1759                    }
 1760                }
 01761            }
 1762
 1763            public void Lookup(int numMatchedSegments, Collection<string> relativePathSegments,
 1764                NameValueCollection boundParameters)
 1765            {
 1766                Fx.Assert(numMatchedSegments == _owner._segments.Count, "We should have matched the other segments");
 1767
 21768                if (HasVariable)
 1769                {
 11770                    StringBuilder remainingPath = new StringBuilder();
 61771                    for (int i = numMatchedSegments; i < relativePathSegments.Count; i++)
 1772                    {
 21773                        if (i < relativePathSegments.Count - 1)
 1774                        {
 11775                            remainingPath.AppendFormat("{0}/", relativePathSegments[i]);
 1776                        }
 1777                        else
 1778                        {
 11779                            remainingPath.Append(relativePathSegments[i]);
 1780                        }
 1781                    }
 1782
 11783                    boundParameters.Add(_varName, remainingPath.ToString());
 1784                }
 21785            }
 1786        }
 1787    }
 1788}

Methods/Properties

.ctor(System.String)
.ctor(System.String,System.Boolean)
.ctor(System.String,System.Collections.Generic.IDictionary`2<System.String,System.String>)
.ctor(System.String,System.Boolean,System.Collections.Generic.IDictionary`2<System.String,System.String>)
Defaults()
IgnoreTrailingSlash()
PathSegmentVariableNames()
QueryValueVariableNames()
HasNoVariables()
HasWildcard()
BindByName(System.Uri,System.Collections.Generic.IDictionary`2<System.String,System.String>)
BindByName(System.Uri,System.Collections.Generic.IDictionary`2<System.String,System.String>,System.Boolean)
BindByName(System.Uri,System.Collections.Specialized.NameValueCollection)
BindByName(System.Uri,System.Collections.Specialized.NameValueCollection,System.Boolean)
BindByPosition(System.Uri,System.String[])
IsEquivalentTo(CoreWCF.UriTemplate)
Match(System.Uri,System.Uri)
ToString()
AddPathVariable(CoreWCF.UriTemplatePartType,System.String)
AddPathVariable(CoreWCF.UriTemplatePartType,System.String,System.Boolean&)
AddQueryVariable(System.String)
CreateUriTemplateMatch(System.Uri,System.Uri,System.Object,System.Int32,System.Collections.ObjectModel.Collection`1<System.String>,System.Collections.Specialized.NameValueCollection)
IsPathPartiallyEquivalentAt(CoreWCF.UriTemplate,System.Int32)
IsQueryEquivalent(CoreWCF.UriTemplate)
RewriteUri(System.Uri,System.String)
Bind(System.Uri,CoreWCF.UriTemplate/BindInformation,System.Boolean)
BindTerminalDefaults(System.Int32,System.Collections.Specialized.NameValueCollection)
IsCandidatePathMatch(System.Int32,System.String[],System.Int32&,System.Collections.ObjectModel.Collection`1<System.String>&)
IsPathFullyEquivalent(CoreWCF.UriTemplate)
PrepareBindInformation(System.Collections.Generic.IDictionary`2<System.String,System.String>,System.Boolean)
PrepareBindInformation(System.Collections.Specialized.NameValueCollection,System.Boolean)
ProcessDefaultsAndCreateBindInfo(System.Boolean,System.Collections.Generic.IDictionary`2<System.String,System.String>,CoreWCF.UriTemplate/BindInformation&)
UnescapeDefaultValue(System.String)
.ctor(System.String[],System.Int32,System.Int32,System.Collections.Generic.IDictionary`2<System.String,System.String>)
.ctor(System.Collections.Generic.IDictionary`2<System.String,System.String>)
AdditionalParameters()
LastNonDefaultPathParameter()
LastNonNullablePathParameter()
NormalizedParameters()
.ctor(CoreWCF.UriTemplate)
Count()
IsReadOnly()
Keys()
Values()
Item(System.String)
Item(System.String,System.String)
Add(System.String,System.String)
Add(System.Collections.Generic.KeyValuePair`2<System.String,System.String>)
Clear()
Contains(System.Collections.Generic.KeyValuePair`2<System.String,System.String>)
ContainsKey(System.String)
CopyTo(System.Collections.Generic.KeyValuePair`2<System.String,System.String>[],System.Int32)
GetEnumerator()
Remove(System.String)
Remove(System.Collections.Generic.KeyValuePair`2<System.String,System.String>)
System.Collections.IEnumerable.GetEnumerator()
TryGetValue(System.String,System.String&)
.ctor(CoreWCF.UriTemplate)
EmptyCollection()
DefaultValues()
PathSegmentVariableNames()
QueryValueVariableNames()
AddDefaultValue(System.String,System.String)
AddPathVariable(CoreWCF.UriTemplatePartType,System.String,System.Boolean&)
AddQueryVariable(System.String)
LookupDefault(System.String,System.Collections.Specialized.NameValueCollection)
PrepareBindInformation(System.Collections.Generic.IDictionary`2<System.String,System.String>,System.Boolean)
PrepareBindInformation(System.Collections.Specialized.NameValueCollection,System.Boolean)
PrepareBindInformation(System.String[])
ValidateDefaults(System.Int32&)
AddAdditionalDefaults(System.Collections.Generic.IDictionary`2<System.String,System.String>&)
LoadDefaultsAndValidate(System.String[],System.Int32&,System.Int32&)
ParseVariableDeclaration(System.String,System.String&,System.String&)
PrepareNormalizedParameters()
ProcessBindParameter(System.String,System.String,System.String[],System.Collections.Generic.IDictionary`2<System.String,System.String>&)
ProcessDefaultsAndCreateBindInfo(System.Boolean,System.String[],System.Collections.Generic.IDictionary`2<System.String,System.String>,CoreWCF.UriTemplate/BindInformation&)
RemoveAdditionalDefaults(System.Collections.Generic.IDictionary`2<System.String,System.String>&)
.ctor(CoreWCF.UriTemplate)
.ctor(CoreWCF.UriTemplate,System.String)
HasVariable()
Bind(System.String[],System.Int32&,System.Text.StringBuilder)
Lookup(System.Int32,System.Collections.ObjectModel.Collection`1<System.String>,System.Collections.Specialized.NameValueCollection)