/// <summary>
        ///     Recusivly builds a substream-per-stream ordered tree graph using the
        ///     join information supplied for outer joins and from the query graph (where clause).
        ///     <para />
        ///     Required streams are considered first and their lookup is placed first in the list
        ///     to gain performance.
        /// </summary>
        /// <param name="streamNum">is the root stream number that supplies the incoming event to build the tree for</param>
        /// <param name="queryGraph">contains where-clause stream relationship info</param>
        /// <param name="completedStreams">is a temporary holder for streams already considered</param>
        /// <param name="substreamsPerStream">is the ordered, tree-like structure to be filled</param>
        /// <param name="streamCallStack">the query plan call stack of streams available via cursor</param>
        /// <param name="dependencyGraph">dependencies between historical streams</param>
        /// <throws>ExprValidationException if the query planning failed</throws>
        protected internal static void RecursiveBuildInnerJoin(
            int streamNum,
            Stack<int> streamCallStack,
            QueryGraphForge queryGraph,
            ISet<int> completedStreams,
            IDictionary<int, int[]> substreamsPerStream,
            DependencyGraph dependencyGraph)
        {
            // add this stream to the set of completed streams
            completedStreams.Add(streamNum);

            // check if the dependencies have been satisfied
            if (dependencyGraph.HasDependency(streamNum)) {
                var dependencies = dependencyGraph.GetDependenciesForStream(streamNum);
                foreach (var dependentStream in dependencies) {
                    if (!streamCallStack.Contains(dependentStream)) {
                        throw new ExprValidationException(
                            "Historical stream " +
                            streamNum +
                            " parameter dependency originating in stream " +
                            dependentStream +
                            " cannot or may not be satisfied by the join");
                    }
                }
            }

            // Determine the streams we can navigate to from this stream
            var navigableStreams = queryGraph.GetNavigableStreams(streamNum);

            // remove streams with a dependency on other streams not yet processed
            var navigableStreamArr = navigableStreams.ToArray();
            foreach (int navigableStream in navigableStreamArr) {
                if (dependencyGraph.HasUnsatisfiedDependency(navigableStream, completedStreams)) {
                    navigableStreams.Remove(navigableStream);
                }
            }

            // remove those already done
            navigableStreams.RemoveAll(completedStreams);

            // if we are a leaf node, we are done
            if (navigableStreams.IsEmpty()) {
                substreamsPerStream.Put(streamNum, new int[0]);
                return;
            }

            // First the outer (required) streams to this stream, then the inner (optional) streams
            var substreams = new int[navigableStreams.Count];
            substreamsPerStream.Put(streamNum, substreams);
            var count = 0;
            foreach (var stream in navigableStreams) {
                substreams[count++] = stream;
                completedStreams.Add(stream);
            }

            foreach (var stream in navigableStreams) {
                streamCallStack.Push(stream);
                RecursiveBuildInnerJoin(
                    stream,
                    streamCallStack,
                    queryGraph,
                    completedStreams,
                    substreamsPerStream,
                    dependencyGraph);
                streamCallStack.Pop();
            }
        }
        /// <summary>
        ///     Recusivly builds a substream-per-stream ordered tree graph using the
        ///     join information supplied for outer joins and from the query graph (where clause).
        ///     <para />
        ///     Required streams are considered first and their lookup is placed first in the list
        ///     to gain performance.
        /// </summary>
        /// <param name="streamNum">is the root stream number that supplies the incoming event to build the tree for</param>
        /// <param name="queryGraph">contains where-clause stream relationship info</param>
        /// <param name="outerInnerGraph">contains the outer join stream relationship info</param>
        /// <param name="completedStreams">is a temporary holder for streams already considered</param>
        /// <param name="substreamsPerStream">is the ordered, tree-like structure to be filled</param>
        /// <param name="requiredPerStream">indicates which streams are required and which are optional</param>
        /// <param name="streamCallStack">the query plan call stack of streams available via cursor</param>
        /// <param name="dependencyGraph">dependencies between historical streams</param>
        /// <param name="innerJoinGraph">inner join graph</param>
        /// <throws>ExprValidationException if the query planning failed</throws>
        public static void RecursiveBuild(
            int streamNum,
            Stack<int> streamCallStack,
            QueryGraphForge queryGraph,
            OuterInnerDirectionalGraph outerInnerGraph,
            InnerJoinGraph innerJoinGraph,
            ISet<int> completedStreams,
            IDictionary<int, int[]> substreamsPerStream,
            bool[] requiredPerStream,
            DependencyGraph dependencyGraph
        )
        {
            // add this stream to the set of completed streams
            completedStreams.Add(streamNum);

            // check if the dependencies have been satisfied
            if (dependencyGraph.HasDependency(streamNum)) {
                var dependencies = dependencyGraph.GetDependenciesForStream(streamNum);
                foreach (var dependentStream in dependencies) {
                    if (!streamCallStack.Contains(dependentStream)) {
                        throw new ExprValidationException(
                            "Historical stream " +
                            streamNum +
                            " parameter dependency originating in stream " +
                            dependentStream +
                            " cannot or may not be satisfied by the join");
                    }
                }
            }

            // Determine the streams we can navigate to from this stream
            var navigableStreams = queryGraph.GetNavigableStreams(streamNum);

            // add unqualified navigable streams (since on-expressions in outer joins are optional)
            var unqualifiedNavigable = outerInnerGraph.UnqualifiedNavigableStreams.Get(streamNum);
            if (unqualifiedNavigable != null) {
                navigableStreams.AddAll(unqualifiedNavigable);
            }

            // remove those already done
            navigableStreams.RemoveAll(completedStreams);

            // Which streams are inner streams to this stream (optional), which ones are outer to the stream (required)
            var requiredStreams = GetOuterStreams(streamNum, navigableStreams, outerInnerGraph);

            // Add inner joins, if any, unless already completed for this stream
            innerJoinGraph.AddRequiredStreams(streamNum, requiredStreams, completedStreams);

            var optionalStreams = GetInnerStreams(
                streamNum,
                navigableStreams,
                outerInnerGraph,
                innerJoinGraph,
                completedStreams);

            // Remove from the required streams the optional streams which places 'full' joined streams
            // into the optional stream category
            requiredStreams.RemoveAll(optionalStreams);

            // if we are a leaf node, we are done
            if (navigableStreams.IsEmpty()) {
                substreamsPerStream.Put(streamNum, new int[0]);
                return;
            }

            // First the outer (required) streams to this stream, then the inner (optional) streams
            var substreams = new int[requiredStreams.Count + optionalStreams.Count];
            substreamsPerStream.Put(streamNum, substreams);
            var count = 0;
            foreach (var stream in requiredStreams) {
                substreams[count++] = stream;
                requiredPerStream[stream] = true;
            }

            foreach (var stream in optionalStreams) {
                substreams[count++] = stream;
            }

            // next we look at all the required streams and add their dependent streams
            foreach (var stream in requiredStreams) {
                completedStreams.Add(stream);
            }

            foreach (var stream in requiredStreams) {
                streamCallStack.Push(stream);
                RecursiveBuild(
                    stream,
                    streamCallStack,
                    queryGraph,
                    outerInnerGraph,
                    innerJoinGraph,
                    completedStreams,
                    substreamsPerStream,
                    requiredPerStream,
                    dependencyGraph);
                streamCallStack.Pop();
            }

            // look at all the optional streams and add their dependent streams
            foreach (var stream in optionalStreams) {
                streamCallStack.Push(stream);
                RecursiveBuild(
                    stream,
                    streamCallStack,
                    queryGraph,
                    outerInnerGraph,
                    innerJoinGraph,
                    completedStreams,
                    substreamsPerStream,
                    requiredPerStream,
                    dependencyGraph);
                streamCallStack.Pop();
            }
        }