< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.UriTemplateTrieNode
Assembly: CoreWCF.WebHttp
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.WebHttp/src/CoreWCF/UriTemplateTrieNode.cs
Line coverage
59%
Covered lines: 187
Uncovered lines: 126
Coverable lines: 313
Total lines: 853
Line coverage: 59.7%
Branch coverage
61%
Covered branches: 130
Total branches: 213
Branch coverage: 61%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
Make(...)100%22100%
Match(...)100%11100%
Add(...)86.36%222285.18%
CheckMultipleMatches(...)0%880%
GetMatch(...)78.57%141478.57%
TryMatch(...)76.27%595962.16%
GetFailureLocationFromLocationsSet(...)100%110%
Validate(...)65.62%323263.88%
Validate(...)10%101018.18%
FindAnyUriTemplate(...)0%18180%
GetAnyDictionaryValue(...)100%110%
AddFinalCompoundSegment(...)100%44100%
AddFinalLiteralSegment(...)83.33%6687.5%
AddNextCompoundSegment(...)0%440%
AddNextLiteralSegment(...)83.33%6687.5%
AddNextVariableSegment()0%220%
.ctor(...)100%11100%
.ctor(...)100%110%
.ctor()100%11100%
Add(...)50%2270%
Find(...)16.66%6637.5%
Find(...)100%1010100%
GetAnyValue()0%220%
Lookup(...)50%22100%
.ctor(...)100%11100%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.WebHttp/src/CoreWCF/UriTemplateTrieNode.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.Generic;
 6using System.Collections.ObjectModel;
 7using CoreWCF.Runtime;
 8
 9namespace CoreWCF
 10{
 11    internal class UriTemplateTrieNode
 12    {
 13        private readonly int _depth; // relative segment depth (root = 0)
 14        private readonly UriTemplatePathPartiallyEquivalentSet _endOfPath; // matches the non-existent segment at the en
 15        private AscendingSortedCompoundSegmentsCollection<UriTemplatePathPartiallyEquivalentSet> _finalCompoundSegment; 
 16        private Dictionary<UriTemplateLiteralPathSegment, UriTemplatePathPartiallyEquivalentSet> _finalLiteralSegment; /
 17        private readonly UriTemplatePathPartiallyEquivalentSet _finalVariableSegment; // matches e.g. "{var}"
 18        private AscendingSortedCompoundSegmentsCollection<UriTemplateTrieLocation> _nextCompoundSegment; // all are Afte
 19        private Dictionary<UriTemplateLiteralPathSegment, UriTemplateTrieLocation> _nextLiteralSegment; // all are Befor
 20        private UriTemplateTrieLocation _nextVariableSegment; // is BeforeLiteral; matches e.g. "{var}/"
 21        private UriTemplateTrieLocation _onFailure; // points to parent, at 'after me'
 22        private readonly UriTemplatePathPartiallyEquivalentSet _star; // matches any "extra/path/segments" at the end
 23
 8324        private UriTemplateTrieNode(int depth)
 25        {
 8326            _depth = depth;
 8327            _nextLiteralSegment = null;
 8328            _nextCompoundSegment = null;
 8329            _finalLiteralSegment = null;
 8330            _finalCompoundSegment = null;
 8331            _finalVariableSegment = new UriTemplatePathPartiallyEquivalentSet(depth + 1);
 8332            _star = new UriTemplatePathPartiallyEquivalentSet(depth);
 8333            _endOfPath = new UriTemplatePathPartiallyEquivalentSet(depth);
 8334        }
 35
 36        public static UriTemplateTrieNode Make(IEnumerable<KeyValuePair<UriTemplate, object>> keyValuePairs,
 37            bool allowDuplicateEquivalentUriTemplates)
 38        {
 39            // given a UTT at MakeReadOnly time, build the trie
 40            // note that root.onFailure == null;
 4741            UriTemplateTrieNode root = new UriTemplateTrieNode(0);
 42042            foreach (KeyValuePair<UriTemplate, object> kvp in keyValuePairs)
 43            {
 16344                Add(root, kvp);
 45            }
 46
 4747            Validate(root, allowDuplicateEquivalentUriTemplates);
 4748            return root;
 49        }
 50
 51        public bool Match(UriTemplateLiteralPathSegment[] wireData, ICollection<UriTemplateTableMatchCandidate> candidat
 52        {
 12253            UriTemplateTrieLocation currentLocation = new UriTemplateTrieLocation(this, UriTemplateTrieIntraNodeLocation
 12254            return GetMatch(currentLocation, wireData, candidates);
 55        }
 56
 57        private static void Add(UriTemplateTrieNode root, KeyValuePair<UriTemplate, object> kvp)
 58        {
 59            // Currently UTT doesn't support teplates with ignoreTrailingSlash == true; thus we
 60            //  don't care about supporting it in the trie as well.
 16361            UriTemplateTrieNode current = root;
 16362            UriTemplate ut = kvp.Key;
 16363            bool needProcessingOnFinalNode = (ut._segments.Count == 0) || ut.HasWildcard ||
 16364                ut._segments[ut._segments.Count - 1].EndsWithSlash;
 69265            for (int i = 0; i < ut._segments.Count; ++i)
 66            {
 18367                if (i >= ut._firstOptionalSegment)
 68                {
 869                    current._endOfPath.Items.Add(kvp);
 70                }
 71
 18372                UriTemplatePathSegment ps = ut._segments[i];
 18373                if (!ps.EndsWithSlash)
 74                {
 75                    Fx.Assert(i == ut._segments.Count - 1, "only the last segment can !EndsWithSlash");
 76                    Fx.Assert(!ut.HasWildcard, "path star cannot have !EndsWithSlash");
 14777                    switch (ps.Nature)
 78                    {
 79                        case UriTemplatePartType.Literal:
 12380                            current.AddFinalLiteralSegment(ps as UriTemplateLiteralPathSegment, kvp);
 12381                            break;
 82
 83                        case UriTemplatePartType.Compound:
 884                            current.AddFinalCompoundSegment(ps as UriTemplateCompoundPathSegment, kvp);
 885                            break;
 86
 87                        case UriTemplatePartType.Variable:
 1688                            current._finalVariableSegment.Items.Add(kvp);
 1689                            break;
 90
 91                        default:
 92                            Fx.Assert("Invalid value as PathSegment.Nature");
 93                            break;
 94                    }
 95                }
 96                else
 97                {
 98                    Fx.Assert(ps.EndsWithSlash, "ps.EndsWithSlash");
 3699                    switch (ps.Nature)
 100                    {
 101                        case UriTemplatePartType.Literal:
 36102                            current = current.AddNextLiteralSegment(ps as UriTemplateLiteralPathSegment);
 36103                            break;
 104
 105                        case UriTemplatePartType.Compound:
 0106                            current = current.AddNextCompoundSegment(ps as UriTemplateCompoundPathSegment);
 0107                            break;
 108
 109                        case UriTemplatePartType.Variable:
 0110                            current = current.AddNextVariableSegment();
 111                            break;
 112
 113                        default:
 114                            Fx.Assert("Invalid value as PathSegment.Nature");
 115                            break;
 116                    }
 117                }
 118            }
 119
 163120            if (needProcessingOnFinalNode)
 121            {
 122                // if the last segment ended in a slash, there is still more to do
 16123                if (ut.HasWildcard)
 124                {
 125                    // e.g. "path1/path2/*"
 16126                    current._star.Items.Add(kvp);
 127                }
 128                else
 129                {
 130                    // e.g. "path1/path2/"
 0131                    current._endOfPath.Items.Add(kvp);
 132                }
 133            }
 147134        }
 135
 136        private static bool CheckMultipleMatches(IList<IList<UriTemplateTrieLocation>> locationsSet, UriTemplateLiteralP
 137            ICollection<UriTemplateTableMatchCandidate> candidates)
 138        {
 0139            bool result = false;
 0140            for (int i = 0; ((i < locationsSet.Count) && !result); i++)
 141            {
 0142                for (int j = 0; j < locationsSet[i].Count; j++)
 143                {
 0144                    if (GetMatch(locationsSet[i][j], wireData, candidates))
 145                    {
 0146                        result = true;
 147                    }
 148                }
 149            }
 150
 0151            return result;
 152        }
 153
 154        private static bool GetMatch(UriTemplateTrieLocation location, UriTemplateLiteralPathSegment[] wireData,
 155            ICollection<UriTemplateTableMatchCandidate> candidates)
 156        {
 122157            int initialDepth = location.Node._depth;
 158            do
 159            {
 130160                if (TryMatch(wireData, location, out UriTemplatePathPartiallyEquivalentSet answer, out SingleLocationOrL
 161                {
 121162                    if (answer != null)
 163                    {
 484164                        for (int i = 0; i < answer.Items.Count; i++)
 165                        {
 121166                            candidates.Add(new UriTemplateTableMatchCandidate(answer.Items[i].Key, answer.SegmentsCount,
 121167                                answer.Items[i].Value));
 168                        }
 169                    }
 170
 121171                    return true;
 172                }
 173
 9174                if (nextStep.IsSingle)
 175                {
 9176                    location = nextStep.SingleLocation;
 177                }
 178                else
 179                {
 180                    Fx.Assert(nextStep.LocationsSet != null, "This should be set to a valid value by TryMatch");
 0181                    if (CheckMultipleMatches(nextStep.LocationsSet, wireData, candidates))
 182                    {
 0183                        return true;
 184                    }
 0185                    location = GetFailureLocationFromLocationsSet(nextStep.LocationsSet);
 186                }
 9187            } while ((location != null) && (location.Node._depth >= initialDepth));
 188
 189            // we walked the whole trie down and found nothing
 1190            return false;
 191        }
 192
 193        private static bool TryMatch(UriTemplateLiteralPathSegment[] wireUriSegments, UriTemplateTrieLocation currentLoc
 194            out UriTemplatePathPartiallyEquivalentSet success, out SingleLocationOrLocationsSet nextStep)
 195        {
 196            // if returns true, success is set to answer
 197            // if returns false, nextStep is set to next place to look
 130198            success = null;
 130199            nextStep = new SingleLocationOrLocationsSet();
 200
 130201            if (wireUriSegments.Length <= currentLocation.Node._depth)
 202            {
 203                Fx.Assert(wireUriSegments.Length == 0 || wireUriSegments[wireUriSegments.Length - 1].EndsWithSlash,
 204                    "we should not have traversed this deep into the trie unless the wire path ended in a slash");
 205
 1206                if (currentLocation.Node._endOfPath.Items.Count != 0)
 207                {
 208                    // exact match of e.g. "path1/path2/"
 1209                    success = currentLocation.Node._endOfPath;
 1210                    return true;
 211                }
 0212                else if (currentLocation.Node._star.Items.Count != 0)
 213                {
 214                    // inexact match of e.g. WIRE("path1/path2/") against TEMPLATE("path1/path2/*")
 0215                    success = currentLocation.Node._star;
 0216                    return true;
 217                }
 218                else
 219                {
 0220                    nextStep = new SingleLocationOrLocationsSet(currentLocation.Node._onFailure);
 0221                    return false;
 222                }
 223            }
 224            else
 225            {
 129226                UriTemplateLiteralPathSegment curWireSeg = wireUriSegments[currentLocation.Node._depth];
 129227                bool considerLiteral = false;
 129228                bool considerCompound = false;
 129229                bool considerVariable = false;
 129230                bool considerStar = false;
 129231                switch (currentLocation.LocationWithin)
 232                {
 233                    case UriTemplateTrieIntraNodeLocation.BeforeLiteral:
 129234                        considerLiteral = true;
 129235                        considerCompound = true;
 129236                        considerVariable = true;
 129237                        considerStar = true;
 129238                        break;
 239                    case UriTemplateTrieIntraNodeLocation.AfterLiteral:
 0240                        considerLiteral = false;
 0241                        considerCompound = true;
 0242                        considerVariable = true;
 0243                        considerStar = true;
 0244                        break;
 245                    case UriTemplateTrieIntraNodeLocation.AfterCompound:
 0246                        considerLiteral = false;
 0247                        considerCompound = false;
 0248                        considerVariable = true;
 0249                        considerStar = true;
 0250                        break;
 251                    case UriTemplateTrieIntraNodeLocation.AfterVariable:
 0252                        considerLiteral = false;
 0253                        considerCompound = false;
 0254                        considerVariable = false;
 0255                        considerStar = true;
 256                        break;
 257                    default:
 258                        Fx.Assert("bad kind");
 259                        break;
 260                }
 261
 129262                if (curWireSeg.EndsWithSlash)
 263                {
 264
 9265                    if (considerLiteral && currentLocation.Node._nextLiteralSegment != null &&
 9266                        currentLocation.Node._nextLiteralSegment.ContainsKey(curWireSeg))
 267                    {
 8268                        nextStep = new SingleLocationOrLocationsSet(currentLocation.Node._nextLiteralSegment[curWireSeg]
 8269                        return false;
 270                    }
 1271                    else if (considerCompound && currentLocation.Node._nextCompoundSegment != null &&
 1272                        AscendingSortedCompoundSegmentsCollection<UriTemplateTrieLocation>.Lookup(currentLocation.Node._
 273                    {
 0274                        nextStep = new SingleLocationOrLocationsSet(compoundLocationsSet);
 0275                        return false;
 276                    }
 1277                    else if (considerVariable && currentLocation.Node._nextVariableSegment != null &&
 1278                        !curWireSeg.IsNullOrEmpty())
 279                    {
 0280                        nextStep = new SingleLocationOrLocationsSet(currentLocation.Node._nextVariableSegment);
 0281                        return false;
 282                    }
 1283                    else if (considerStar && currentLocation.Node._star.Items.Count != 0)
 284                    {
 285                        // matches e.g. WIRE("path1/path2/path3") and TEMPLATE("path1/*")
 1286                        success = currentLocation.Node._star;
 1287                        return true;
 288                    }
 289                    else
 290                    {
 0291                        nextStep = new SingleLocationOrLocationsSet(currentLocation.Node._onFailure);
 0292                        return false;
 293                    }
 294                }
 295                else
 296                {
 297                    Fx.Assert(!curWireSeg.EndsWithSlash, "!curWireSeg.EndsWithSlash");
 298                    Fx.Assert(!curWireSeg.IsNullOrEmpty(), "!curWireSeg.IsNullOrEmpty()");
 299
 120300                    if (considerLiteral && currentLocation.Node._finalLiteralSegment != null &&
 120301                        currentLocation.Node._finalLiteralSegment.ContainsKey(curWireSeg))
 302                    {
 303                        // matches e.g. WIRE("path1/path2") and TEMPLATE("path1/path2")
 116304                        success = currentLocation.Node._finalLiteralSegment[curWireSeg];
 116305                        return true;
 306                    }
 4307                    else if (considerCompound && currentLocation.Node._finalCompoundSegment != null &&
 4308                        AscendingSortedCompoundSegmentsCollection<UriTemplatePathPartiallyEquivalentSet>.Lookup(currentL
 309                    {
 310                        // matches e.g. WIRE("path1/path2") and TEMPLATE("path1/p{var}th2")
 311                        // we should take only the highest order match!
 312                        Fx.Assert(compoundPathEquivalentSets.Count >= 1, "Lookup is expected to return false otherwise")
 313                        Fx.Assert(compoundPathEquivalentSets[0].Count > 0, "Find shouldn't return empty sublists");
 314
 1315                        if (compoundPathEquivalentSets[0].Count == 1)
 316                        {
 1317                            success = compoundPathEquivalentSets[0][0];
 318                        }
 319                        else
 320                        {
 0321                            success = new UriTemplatePathPartiallyEquivalentSet(currentLocation.Node._depth + 1);
 0322                            for (int i = 0; i < compoundPathEquivalentSets[0].Count; i++)
 323                            {
 0324                                success.Items.AddRange(compoundPathEquivalentSets[0][i].Items);
 325                            }
 326                        }
 327
 1328                        return true;
 329                    }
 3330                    else if (considerVariable && currentLocation.Node._finalVariableSegment.Items.Count != 0)
 331                    {
 332                        // matches e.g. WIRE("path1/path2") and TEMPLATE("path1/{var}")
 1333                        success = currentLocation.Node._finalVariableSegment;
 334
 1335                        return true;
 336                    }
 2337                    else if (considerStar && currentLocation.Node._star.Items.Count != 0)
 338                    {
 339                        // matches e.g. WIRE("path1/path2") and TEMPLATE("path1/*")
 1340                        success = currentLocation.Node._star;
 341
 1342                        return true;
 343                    }
 344                    else
 345                    {
 1346                        nextStep = new SingleLocationOrLocationsSet(currentLocation.Node._onFailure);
 347
 1348                        return false;
 349                    }
 350                }
 351            }
 352        }
 353
 354        private static UriTemplateTrieLocation GetFailureLocationFromLocationsSet(IList<IList<UriTemplateTrieLocation>> 
 355        {
 356            Fx.Assert(locationsSet != null, "Shouldn't be called on null set");
 357            Fx.Assert(locationsSet.Count > 0, "Shouldn't be called on empty set");
 358            Fx.Assert(locationsSet[0] != null, "Shouldn't be called on a set with null sub-lists");
 359            Fx.Assert(locationsSet[0].Count > 0, "Shouldn't be called on a set with empty sub-lists");
 360
 0361            return locationsSet[0][0].Node._onFailure;
 362        }
 363
 364        private static void Validate(UriTemplateTrieNode root, bool allowDuplicateEquivalentUriTemplates)
 365        {
 366            // walk the entire tree, and ensure that each PathEquivalentSet is ok (no ambiguous queries),
 367            // verify the compound segments didn't add potentially multiple matches;
 368            // also Assert various data-structure invariants
 47369            Queue<UriTemplateTrieNode> nodesQueue = new Queue<UriTemplateTrieNode>();
 370
 47371            UriTemplateTrieNode current = root;
 36372            while (true)
 373            {
 374                // validate all the PathEquivalentSets that live in this node
 83375                Validate(current._endOfPath, allowDuplicateEquivalentUriTemplates);
 83376                Validate(current._finalVariableSegment, allowDuplicateEquivalentUriTemplates);
 83377                Validate(current._star, allowDuplicateEquivalentUriTemplates);
 83378                if (current._finalLiteralSegment != null)
 379                {
 324380                    foreach (KeyValuePair<UriTemplateLiteralPathSegment, UriTemplatePathPartiallyEquivalentSet> kvp in c
 381                    {
 123382                        Validate(kvp.Value, allowDuplicateEquivalentUriTemplates);
 383                    }
 384                }
 385
 83386                if (current._finalCompoundSegment != null)
 387                {
 8388                    IList<IList<UriTemplatePathPartiallyEquivalentSet>> pesLists = current._finalCompoundSegment.Values;
 32389                    for (int i = 0; i < pesLists.Count; i++)
 390                    {
 8391                        if (!allowDuplicateEquivalentUriTemplates && (pesLists[i].Count > 1))
 392                        {
 0393                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.F
 0394                                SR.UTTDuplicate, pesLists[i][0].Items[0].Key.ToString(), pesLists[i][1].Items[0].Key.ToS
 395                        }
 32396                        for (int j = 0; j < pesLists[i].Count; j++)
 397                        {
 8398                            Validate(pesLists[i][j], allowDuplicateEquivalentUriTemplates);
 399                        }
 400                    }
 401                }
 402
 403                // deal with children of this node
 83404                if (current._nextLiteralSegment != null)
 405                {
 96406                    foreach (KeyValuePair<UriTemplateLiteralPathSegment, UriTemplateTrieLocation> kvp in current._nextLi
 407                    {
 408                        Fx.Assert(kvp.Value.LocationWithin == UriTemplateTrieIntraNodeLocation.BeforeLiteral, "forward-p
 409                        Fx.Assert(kvp.Value.Node._depth == current._depth + 1, "kvp.Value.node.depth == current.depth + 
 410                        Fx.Assert(kvp.Value.Node._onFailure.Node == current, "back pointer should point back to here");
 411                        Fx.Assert(kvp.Value.Node._onFailure.LocationWithin == UriTemplateTrieIntraNodeLocation.AfterLite
 36412                        nodesQueue.Enqueue(kvp.Value.Node);
 413                    }
 414                }
 415
 83416                if (current._nextCompoundSegment != null)
 417                {
 0418                    IList<IList<UriTemplateTrieLocation>> locations = current._nextCompoundSegment.Values;
 0419                    for (int i = 0; i < locations.Count; i++)
 420                    {
 0421                        if (!allowDuplicateEquivalentUriTemplates && (locations[i].Count > 1))
 422                        {
 423                            // In the future we might ease up the restrictions and verify if there is realy
 424                            // a potential multiple match here; for now we are throwing.
 0425                            UriTemplate firstTemplate = FindAnyUriTemplate(locations[i][0].Node);
 0426                            UriTemplate secondTemplate = FindAnyUriTemplate(locations[i][1].Node);
 0427                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.F
 0428                                SR.UTTDuplicate, firstTemplate.ToString(), secondTemplate.ToString())));
 429                        }
 430
 0431                        for (int j = 0; j < locations[i].Count; j++)
 432                        {
 0433                            UriTemplateTrieLocation location = locations[i][j];
 434
 435                            Fx.Assert(location.LocationWithin == UriTemplateTrieIntraNodeLocation.BeforeLiteral, "forwar
 436                            Fx.Assert(location.Node._depth == current._depth + 1, "kvp.Value.node.depth == current.depth
 437                            Fx.Assert(location.Node._onFailure.Node == current, "back pointer should point back to here"
 438                            Fx.Assert(location.Node._onFailure.LocationWithin == UriTemplateTrieIntraNodeLocation.AfterC
 439
 0440                            nodesQueue.Enqueue(location.Node);
 441                        }
 442                    }
 443                }
 444
 83445                if (current._nextVariableSegment != null)
 446                {
 447                    Fx.Assert(current._nextVariableSegment.LocationWithin == UriTemplateTrieIntraNodeLocation.BeforeLite
 448                    Fx.Assert(current._nextVariableSegment.Node._depth == current._depth + 1, "current.nextVariableSegme
 449                    Fx.Assert(current._nextVariableSegment.Node._onFailure.Node == current, "back pointer should point b
 450                    Fx.Assert(current._nextVariableSegment.Node._onFailure.LocationWithin == UriTemplateTrieIntraNodeLoc
 451
 0452                    nodesQueue.Enqueue(current._nextVariableSegment.Node);
 453                }
 454
 455                // move on to next bit of work
 83456                if (nodesQueue.Count == 0)
 457                {
 458                    break;
 459                }
 460
 36461                current = nodesQueue.Dequeue();
 462            }
 47463        }
 464
 465        private static void Validate(UriTemplatePathPartiallyEquivalentSet pes, bool allowDuplicateEquivalentUriTemplate
 466        {
 467            // A set with 0 or 1 items is valid by definition
 380468            if (pes.Items.Count < 2)
 469            {
 380470                return;
 471            }
 472
 473            // Assert all paths are partially-equivalent
 0474            for (int i = 0; i < pes.Items.Count - 1; ++i)
 475            {
 476                Fx.Assert(pes.Items[i].Key.IsPathPartiallyEquivalentAt(pes.Items[i + 1].Key, pes.SegmentsCount),
 477                    "all elements of a PES must be path partially-equivalent");
 478            }
 479
 480            // We will check that the queries disambiguate only for templates, which are
 481            //  matched completely at the segments count; templates, which are match at
 482            //  that point due to terminal defaults, will be ruled out.
 0483            UriTemplate[] a = new UriTemplate[pes.Items.Count];
 0484            int arrayIndex = 0;
 0485            foreach (KeyValuePair<UriTemplate, object> kvp in pes.Items)
 486            {
 0487                if (pes.SegmentsCount < kvp.Key._segments.Count)
 488                {
 489                    continue;
 490                }
 491
 492                Fx.Assert(arrayIndex < a.Length, "We made enough room for all the items");
 493
 0494                a[arrayIndex++] = kvp.Key;
 495            }
 496
 497            // Ensure that queries disambiguate (if needed) :
 0498            if (arrayIndex > 0)
 499            {
 0500                UriTemplateHelpers.DisambiguateSamePath(a, 0, arrayIndex, allowDuplicateEquivalentUriTemplates);
 501            }
 0502        }
 503
 504        private static UriTemplate FindAnyUriTemplate(UriTemplateTrieNode node)
 505        {
 0506            while (node != null)
 507            {
 0508                if (node._endOfPath.Items.Count > 0)
 509                {
 0510                    return node._endOfPath.Items[0].Key;
 511                }
 512
 0513                if (node._finalVariableSegment.Items.Count > 0)
 514                {
 0515                    return node._finalVariableSegment.Items[0].Key;
 516                }
 517
 0518                if (node._star.Items.Count > 0)
 519                {
 0520                    return node._star.Items[0].Key;
 521                }
 522
 0523                if (node._finalLiteralSegment != null)
 524                {
 0525                    UriTemplatePathPartiallyEquivalentSet pes =
 0526                        GetAnyDictionaryValue(node._finalLiteralSegment);
 527
 528                    Fx.Assert(pes.Items.Count > 0, "Otherwise, why creating the dictionary?");
 529
 0530                    return pes.Items[0].Key;
 531                }
 532
 0533                if (node._finalCompoundSegment != null)
 534                {
 0535                    UriTemplatePathPartiallyEquivalentSet pes = node._finalCompoundSegment.GetAnyValue();
 536
 537                    Fx.Assert(pes.Items.Count > 0, "Otherwise, why creating the collection?");
 538
 0539                    return pes.Items[0].Key;
 540                }
 541
 0542                if (node._nextLiteralSegment != null)
 543                {
 0544                    UriTemplateTrieLocation location =
 0545                        GetAnyDictionaryValue(node._nextLiteralSegment);
 0546                    node = location.Node;
 547                }
 0548                else if (node._nextCompoundSegment != null)
 549                {
 0550                    UriTemplateTrieLocation location = node._nextCompoundSegment.GetAnyValue();
 0551                    node = location.Node;
 552                }
 0553                else if (node._nextVariableSegment != null)
 554                {
 0555                    node = node._nextVariableSegment.Node;
 556                }
 557                else
 558                {
 0559                    node = null;
 560                }
 561            }
 562
 563            Fx.Assert("How did we got here without finding a UriTemplate earlier?");
 564
 0565            return null;
 566        }
 567
 568        private static T GetAnyDictionaryValue<T>(IDictionary<UriTemplateLiteralPathSegment, T> dictionary)
 569        {
 0570            using (IEnumerator<T> valuesEnumerator = dictionary.Values.GetEnumerator())
 571            {
 0572                valuesEnumerator.MoveNext();
 0573                return valuesEnumerator.Current;
 574            }
 0575        }
 576
 577        private void AddFinalCompoundSegment(UriTemplateCompoundPathSegment cps, KeyValuePair<UriTemplate, object> kvp)
 578        {
 579            Fx.Assert(cps != null, "must be - based on the segment nature");
 580
 8581            if (_finalCompoundSegment == null)
 582            {
 8583                _finalCompoundSegment = new AscendingSortedCompoundSegmentsCollection<UriTemplatePathPartiallyEquivalent
 584            }
 585
 8586            UriTemplatePathPartiallyEquivalentSet pes = _finalCompoundSegment.Find(cps);
 8587            if (pes == null)
 588            {
 8589                pes = new UriTemplatePathPartiallyEquivalentSet(_depth + 1);
 8590                _finalCompoundSegment.Add(cps, pes);
 591            }
 592
 8593            pes.Items.Add(kvp);
 8594        }
 595
 596        private void AddFinalLiteralSegment(UriTemplateLiteralPathSegment lps, KeyValuePair<UriTemplate, object> kvp)
 597        {
 598            Fx.Assert(lps != null, "must be - based on the segment nature");
 599
 123600            if (_finalLiteralSegment != null && _finalLiteralSegment.ContainsKey(lps))
 601            {
 0602                _finalLiteralSegment[lps].Items.Add(kvp);
 603            }
 604            else
 605            {
 123606                if (_finalLiteralSegment == null)
 607                {
 39608                    _finalLiteralSegment = new Dictionary<UriTemplateLiteralPathSegment, UriTemplatePathPartiallyEquival
 609                }
 610
 123611                UriTemplatePathPartiallyEquivalentSet pes = new UriTemplatePathPartiallyEquivalentSet(_depth + 1);
 123612                pes.Items.Add(kvp);
 123613                _finalLiteralSegment.Add(lps, pes);
 614            }
 123615        }
 616
 617        private UriTemplateTrieNode AddNextCompoundSegment(UriTemplateCompoundPathSegment cps)
 618        {
 619            Fx.Assert(cps != null, "must be - based on the segment nature");
 620
 0621            if (_nextCompoundSegment == null)
 622            {
 0623                _nextCompoundSegment = new AscendingSortedCompoundSegmentsCollection<UriTemplateTrieLocation>();
 624            }
 625
 0626            UriTemplateTrieLocation nextLocation = _nextCompoundSegment.Find(cps);
 0627            if (nextLocation == null)
 628            {
 0629                UriTemplateTrieNode nextNode = new UriTemplateTrieNode(_depth + 1);
 0630                nextNode._onFailure = new UriTemplateTrieLocation(this, UriTemplateTrieIntraNodeLocation.AfterCompound);
 0631                nextLocation = new UriTemplateTrieLocation(nextNode, UriTemplateTrieIntraNodeLocation.BeforeLiteral);
 0632                _nextCompoundSegment.Add(cps, nextLocation);
 633            }
 634
 0635            return nextLocation.Node;
 636        }
 637
 638        private UriTemplateTrieNode AddNextLiteralSegment(UriTemplateLiteralPathSegment lps)
 639        {
 640            Fx.Assert(lps != null, "must be - based on the segment nature");
 641
 36642            if (_nextLiteralSegment != null && _nextLiteralSegment.ContainsKey(lps))
 643            {
 0644                return _nextLiteralSegment[lps].Node;
 645            }
 646            else
 647            {
 36648                if (_nextLiteralSegment == null)
 649                {
 12650                    _nextLiteralSegment = new Dictionary<UriTemplateLiteralPathSegment, UriTemplateTrieLocation>();
 651                }
 652
 36653                UriTemplateTrieNode newNode = new UriTemplateTrieNode(_depth + 1);
 36654                newNode._onFailure = new UriTemplateTrieLocation(this, UriTemplateTrieIntraNodeLocation.AfterLiteral);
 36655                _nextLiteralSegment.Add(lps, new UriTemplateTrieLocation(newNode, UriTemplateTrieIntraNodeLocation.Befor
 656
 36657                return newNode;
 658            }
 659        }
 660
 661        private UriTemplateTrieNode AddNextVariableSegment()
 662        {
 0663            if (_nextVariableSegment != null)
 664            {
 0665                return _nextVariableSegment.Node;
 666            }
 667            else
 668            {
 0669                UriTemplateTrieNode newNode = new UriTemplateTrieNode(_depth + 1);
 0670                newNode._onFailure = new UriTemplateTrieLocation(this, UriTemplateTrieIntraNodeLocation.AfterVariable);
 0671                _nextVariableSegment = new UriTemplateTrieLocation(newNode, UriTemplateTrieIntraNodeLocation.BeforeLiter
 672
 0673                return newNode;
 674            }
 675        }
 676
 677        internal struct SingleLocationOrLocationsSet
 678        {
 679            private readonly IList<IList<UriTemplateTrieLocation>> _locationsSet;
 680            private readonly UriTemplateTrieLocation _singleLocation;
 681
 682            public SingleLocationOrLocationsSet(UriTemplateTrieLocation singleLocation)
 683            {
 9684                IsSingle = true;
 9685                _singleLocation = singleLocation;
 9686                _locationsSet = null;
 9687            }
 688
 689            public SingleLocationOrLocationsSet(IList<IList<UriTemplateTrieLocation>> locationsSet)
 690            {
 0691                IsSingle = false;
 0692                _singleLocation = null;
 0693                _locationsSet = locationsSet;
 0694            }
 695
 9696            public bool IsSingle { get; }
 697
 698            public IList<IList<UriTemplateTrieLocation>> LocationsSet
 699            {
 700                get
 701                {
 702                    Fx.Assert(!IsSingle, "!this.isSingle");
 703
 0704                    return _locationsSet;
 705                }
 706            }
 707
 708            public UriTemplateTrieLocation SingleLocation
 709            {
 710                get
 711                {
 712                    Fx.Assert(IsSingle, "this.isSingle");
 713
 9714                    return _singleLocation;
 715                }
 716            }
 717        }
 718
 719        internal class AscendingSortedCompoundSegmentsCollection<T>
 720            where T : class
 721        {
 722            private readonly SortedList<UriTemplateCompoundPathSegment, Collection<CollectionItem>> _items;
 723
 8724            public AscendingSortedCompoundSegmentsCollection()
 725            {
 8726                _items = new SortedList<UriTemplateCompoundPathSegment, Collection<AscendingSortedCompoundSegmentsCollec
 8727            }
 728
 729            public IList<IList<T>> Values
 730            {
 731                get
 732                {
 8733                    IList<IList<T>> results = new List<IList<T>>(_items.Count);
 32734                    for (int i = 0; i < _items.Values.Count; i++)
 735                    {
 8736                        results.Add(new List<T>(_items.Values[i].Count));
 737                        Fx.Assert(results.Count == i + 1, "We are adding item for each values collection");
 32738                        for (int j = 0; j < _items.Values[i].Count; j++)
 739                        {
 8740                            results[i].Add(_items.Values[i][j].Value);
 741                            Fx.Assert(results[i].Count == j + 1, "We are adding item for each value in the collection");
 742                        }
 743
 744                        Fx.Assert(results[i].Count == _items.Values[i].Count, "We were supposed to add an item for each 
 745                    }
 746
 747                    Fx.Assert(results.Count == _items.Values.Count, "We were supposed to add a sub-list for each values 
 748
 8749                    return results;
 750                }
 751            }
 752
 753            public void Add(UriTemplateCompoundPathSegment segment, T value)
 754            {
 8755                int index = _items.IndexOfKey(segment);
 8756                if (index == -1)
 757                {
 8758                    Collection<CollectionItem> subItems = new Collection<CollectionItem>
 8759                    {
 8760                        new CollectionItem(segment, value)
 8761                    };
 8762                    _items.Add(segment, subItems);
 763                }
 764                else
 765                {
 0766                    Collection<CollectionItem> subItems = _items.Values[index];
 0767                    subItems.Add(new CollectionItem(segment, value));
 768                }
 0769            }
 770
 771            public T Find(UriTemplateCompoundPathSegment segment)
 772            {
 8773                int index = _items.IndexOfKey(segment);
 8774                if (index == -1)
 775                {
 8776                    return null;
 777                }
 778
 0779                Collection<CollectionItem> subItems = this._items.Values[index];
 0780                for (int i = 0; i < subItems.Count; i++)
 781                {
 0782                    if (subItems[i].Segment.IsEquivalentTo(segment, false))
 783                    {
 0784                        return subItems[i].Value;
 785                    }
 786                }
 787
 0788                return null;
 789            }
 790
 791            public IList<IList<T>> Find(UriTemplateLiteralPathSegment wireData)
 792            {
 1793                IList<IList<T>> results = new List<IList<T>>();
 4794                for (int i = 0; i < _items.Values.Count; i++)
 795                {
 1796                    List<T> sameOrderResults = null;
 4797                    for (int j = 0; j < _items.Values[i].Count; j++)
 798                    {
 1799                        if (_items.Values[i][j].Segment.IsMatch(wireData))
 800                        {
 1801                            if (sameOrderResults == null)
 802                            {
 1803                                sameOrderResults = new List<T>();
 804                            }
 1805                            sameOrderResults.Add(_items.Values[i][j].Value);
 806                        }
 807                    }
 808
 1809                    if (sameOrderResults != null)
 810                    {
 1811                        results.Add(sameOrderResults);
 812                    }
 813                }
 814
 1815                return results;
 816            }
 817
 818            public T GetAnyValue()
 819            {
 0820                if (_items.Values.Count > 0)
 821                {
 822                    Fx.Assert(_items.Values[0].Count > 0, "We are not adding a sub-list unless there is at list one item
 823
 0824                    return _items.Values[0][0].Value;
 825                }
 826                else
 827                {
 0828                    return null;
 829                }
 830            }
 831
 832            public static bool Lookup(AscendingSortedCompoundSegmentsCollection<T> collection,
 833                UriTemplateLiteralPathSegment wireData, out IList<IList<T>> results)
 834            {
 1835                results = collection.Find(wireData);
 1836                return (results != null) && (results.Count > 0);
 837            }
 838
 839            internal struct CollectionItem
 840            {
 841                public CollectionItem(UriTemplateCompoundPathSegment segment, T value)
 842                {
 8843                    Segment = segment;
 8844                    Value = value;
 8845                }
 846
 1847                public UriTemplateCompoundPathSegment Segment { get; }
 848
 9849                public T Value { get; }
 850            }
 851        }
 852    }
 853}

Methods/Properties

.ctor(System.Int32)
Make(System.Collections.Generic.IEnumerable`1<System.Collections.Generic.KeyValuePair`2<CoreWCF.UriTemplate,System.Object>>,System.Boolean)
Match(CoreWCF.UriTemplateLiteralPathSegment[],System.Collections.Generic.ICollection`1<CoreWCF.UriTemplateTableMatchCandidate>)
Add(CoreWCF.UriTemplateTrieNode,System.Collections.Generic.KeyValuePair`2<CoreWCF.UriTemplate,System.Object>)
CheckMultipleMatches(System.Collections.Generic.IList`1<System.Collections.Generic.IList`1<CoreWCF.UriTemplateTrieLocation>>,CoreWCF.UriTemplateLiteralPathSegment[],System.Collections.Generic.ICollection`1<CoreWCF.UriTemplateTableMatchCandidate>)
GetMatch(CoreWCF.UriTemplateTrieLocation,CoreWCF.UriTemplateLiteralPathSegment[],System.Collections.Generic.ICollection`1<CoreWCF.UriTemplateTableMatchCandidate>)
TryMatch(CoreWCF.UriTemplateLiteralPathSegment[],CoreWCF.UriTemplateTrieLocation,CoreWCF.UriTemplatePathPartiallyEquivalentSet&,CoreWCF.UriTemplateTrieNode/SingleLocationOrLocationsSet&)
GetFailureLocationFromLocationsSet(System.Collections.Generic.IList`1<System.Collections.Generic.IList`1<CoreWCF.UriTemplateTrieLocation>>)
Validate(CoreWCF.UriTemplateTrieNode,System.Boolean)
Validate(CoreWCF.UriTemplatePathPartiallyEquivalentSet,System.Boolean)
FindAnyUriTemplate(CoreWCF.UriTemplateTrieNode)
GetAnyDictionaryValue(System.Collections.Generic.IDictionary`2<CoreWCF.UriTemplateLiteralPathSegment,T>)
AddFinalCompoundSegment(CoreWCF.UriTemplateCompoundPathSegment,System.Collections.Generic.KeyValuePair`2<CoreWCF.UriTemplate,System.Object>)
AddFinalLiteralSegment(CoreWCF.UriTemplateLiteralPathSegment,System.Collections.Generic.KeyValuePair`2<CoreWCF.UriTemplate,System.Object>)
AddNextCompoundSegment(CoreWCF.UriTemplateCompoundPathSegment)
AddNextLiteralSegment(CoreWCF.UriTemplateLiteralPathSegment)
AddNextVariableSegment()
.ctor(CoreWCF.UriTemplateTrieLocation)
.ctor(System.Collections.Generic.IList`1<System.Collections.Generic.IList`1<CoreWCF.UriTemplateTrieLocation>>)
IsSingle()
LocationsSet()
SingleLocation()
.ctor()
Values()
Add(CoreWCF.UriTemplateCompoundPathSegment,T)
Find(CoreWCF.UriTemplateCompoundPathSegment)
Find(CoreWCF.UriTemplateLiteralPathSegment)
GetAnyValue()
Lookup(CoreWCF.UriTemplateTrieNode/AscendingSortedCompoundSegmentsCollection`1<T>,CoreWCF.UriTemplateLiteralPathSegment,System.Collections.Generic.IList`1<System.Collections.Generic.IList`1<T>>&)
.ctor(CoreWCF.UriTemplateCompoundPathSegment,T)
Segment()
Value()