protected TUpInfo Traverse([NotNull] TItem root,
                            [NotNull] Dictionary <TItem, TDependency[]> incidentDependencies,
                            [NotNull][ItemCanBeNull] AbstractPathMatch <TDependency, TItem>[] expectedInnerPathMatches,
                            [CanBeNull] AbstractPathMatch <TDependency, TItem> endMatch, TDownInfo down)
 {
     return(Traverse(root, incidentDependencies, expectedInnerPathMatches, 0, endMatch, false, down));
 }
        private TUpInfo Traverse([NotNull] TItem tail,
                                 [NotNull] Dictionary <TItem, TDependency[]> incidentDependencies,
                                 [NotNull, ItemCanBeNull] AbstractPathMatch <TDependency, TItem>[] expectedInnerPathMatches,
                                 int expectedPathMatchIndex, AbstractPathMatch <TDependency, TItem> endMatch, bool parentIsEnd, TDownInfo rawDown)
        {
            _checkAbort();

            TUpInfo upSum;
            bool    visitSuccessors = ShouldVisitSuccessors(tail, _currentPath, expectedPathMatchIndex, out upSum);

            if (parentIsEnd && endMatch != null && !endMatch.MultipleOccurrencesAllowed)
            {
                // If the end match matched, and the end match may occur only once, we do not visit the children
            }
            else
            {
                if (visitSuccessors)
                {
                    // We are at this item for the first time - check whether we find a path to some defined end

                    TDependency[] dependencies;
                    if (!incidentDependencies.TryGetValue(tail, out dependencies))
                    {
                        dependencies = NO_DEPENDENCIES;
                    }
                    foreach (var nextDep in dependencies)
                    {
                        TItem nextTail = nextDep.UsedItem;

                        int newExpectedPathMatchIndex = expectedPathMatchIndex;

                        bool mayContinue = true;

                        AbstractPathMatch <TDependency, TItem> pathMatchOrNull = null;

                        if (newExpectedPathMatchIndex < expectedInnerPathMatches.Length)
                        {
                            AbstractPathMatch <TDependency, TItem> m = expectedInnerPathMatches[newExpectedPathMatchIndex];
                            PathMatchResult pathResult = m.Match(nextDep, nextTail);
                            switch (pathResult)
                            {
                            case PathMatchResult.Match:
                                newExpectedPathMatchIndex++;
                                pathMatchOrNull = m;
                                break;

                            case PathMatchResult.Stop:
                                mayContinue = false;
                                break;

                            case PathMatchResult.Continue:
                                break;

                            default:
                                throw new ArgumentOutOfRangeException();
                            }
                        }

                        bool isEnd;
                        if (newExpectedPathMatchIndex >= expectedInnerPathMatches.Length)
                        {
                            // We are at or behind the path match end; if the current item or dependency matches, we have a real path end!
                            // Check whether we are really at an end.
                            // If no end match was provided (i.e., only a start pattern given), all items are accepted as end items.
                            // Otherwise, we check whether the last item or dependency matches the end match.
                            isEnd = endMatch == null || endMatch.IsMatch(nextDep, nextTail);
                        }
                        else
                        {
                            isEnd = false;
                        }

                        if (!isEnd && newExpectedPathMatchIndex == expectedPathMatchIndex)
                        {
                            // Check that no "used up" non-multiple-occurrence positive path
                            // match matches - but only if we did not find a path match exactly here,
                            // and we are not at the end (which is also "finding a match", namely
                            // the endMatch).
                            // This, I hope & believe, captures what one expects to hold implicitly:
                            // "No loop backs" to previous positive single patterns
                            mayContinue &= !expectedInnerPathMatches
                                           .Take(expectedPathMatchIndex)
                                           .Where(m => m != null && m.MayContinue && !m.MultipleOccurrencesAllowed)
                                           .Any(m => m.IsMatch(nextDep, nextTail));
                        }

                        if (mayContinue)
                        {
                            _currentPath.Push(nextDep);

                            DownAndHere downAndHere = AfterPushDependency(_currentPath, expectedPathMatchIndex,
                                                                          pathMatchOrNull, isEnd, rawDown);

                            TUpInfo childUp = Traverse(nextTail, incidentDependencies, expectedInnerPathMatches,
                                                       newExpectedPathMatchIndex, endMatch, isEnd, downAndHere.Down);

                            upSum = BeforePopDependency(_currentPath, expectedPathMatchIndex, pathMatchOrNull,
                                                        isEnd, downAndHere.Save, upSum, childUp);

                            _currentPath.Pop();
                        }
                    }
                }
            }
            upSum = AfterVisitingSuccessors(visitSuccessors, tail, _currentPath, expectedPathMatchIndex, upSum);

            return(upSum);
        }
 protected abstract TUpInfo BeforePopDependency(Stack <TDependency> currentPath, int expectedPathMatchIndex,
                                                AbstractPathMatch <TDependency, TItem> pathMatchOrNull, bool isEnd,
                                                THereInfo here, TUpInfo upSum, TUpInfo childUp);
 protected abstract DownAndHere AfterPushDependency(Stack <TDependency> currentPath, int expectedPathMatchIndex,
                                                    AbstractPathMatch <TDependency, TItem> pathMatchOrNull, bool isEnd, TDownInfo down);