예제 #1
0
파일: Group.cs 프로젝트: trimonovds/Rxx
        private static IObservable <IParseResult <IObservable <TSource> > > AmbiguousGroupInternal <TSource>(
            IObservableCursor <TSource> source,
            IObservableParser <TSource, TSource> open,
            IObservableParser <TSource, TSource> close)
        {
            Contract.Requires(source != null);
            Contract.Requires(open != null);
            Contract.Requires(close != null);
            Contract.Ensures(Contract.Result <IObservable <IParseResult <IObservable <TSource> > > >() != null);

            return(Observable.Create <IParseResult <IObservable <TSource> > >(
                       observer =>
            {
                var branch = source.Branch();

                var disposables = new CompositeDisposable(branch);

                bool hasOpenResult = false;

                disposables.Add(open.Parse(source).SubscribeSafe(
                                    openResult =>
                {
                    hasOpenResult = true;

                    int openCount = 1;

                    var openSinks = new List <Action <IParseResult <TSource> > >();
                    var closeSinks = new List <Action <IParseResult <TSource> > >();
                    var contentSinks = new List <Action <IParseResult <TSource> > >();
                    var bufferedResults = new List <IEnumerable <IParseResult <IObservable <TSource> > > >();

                    Func <List <IParseResult <IObservable <TSource> > > > addBuffer = () =>
                    {
                        var buffer = new List <IParseResult <IObservable <TSource> > >();
                        bufferedResults.Add(buffer);
                        return buffer;
                    };

                    Action installSinks = () =>
                    {
                        var buffer = addBuffer();
                        var content = new List <TSource>();

                        openSinks.Add(result => content.Add(result.Value));
                        contentSinks.Add(result => content.Add(result.Value));
                        closeSinks.Add(result =>
                        {
                            // copy the content list to create a new branch
                            var branchResult = result.Yield(new List <TSource>(content).ToObservable(Scheduler.Immediate));

                            buffer.Add(branchResult);

                            if (openCount > 0)
                            {
                                content.Add(result.Value);
                            }
                        });
                    };

                    // base sinks must be installed first - openCount must be incremented before other sinks are executed
                    openSinks.Add(_ => { openCount++; installSinks(); });
                    closeSinks.Add(_ => { openCount--; });

                    // now we can install the sinks for the first open (matched in the foreach above)
                    installSinks();

                    var innerBranch = branch.Remainder(openResult.Length);

                    bool hasInnerResult = false;
                    IObservableParser <TSource, TSource> current = open;

                    var recursion = new SingleAssignmentDisposable();

                    disposables.Add(recursion);

                    recursion.Disposable = Scheduler.Immediate.Schedule(
                        self =>
                    {
                        var capturedBranch = innerBranch;

                        var innerParser =
                            open.OnSuccess(innerOpenResult =>
                        {
                            hasInnerResult = true;

                            var clone = openSinks.ToList();

                            // the sinks list is modified when open is matched, so we must run a clone
                            clone.ForEach(sink => sink(innerOpenResult));

                            innerBranch = capturedBranch.Remainder(innerOpenResult.Length);

                            current = open;
                        })
                            .Or(
                                close.OnSuccess(closeResult =>
                        {
                            hasInnerResult = true;

                            closeSinks.ForEach(sink => sink(closeResult));

                            innerBranch = capturedBranch.Remainder(closeResult.Length);

                            current = close;
                        }))
                            .Or(
                                current.Next.OnSuccess(content =>
                        {
                            hasInnerResult = true;

                            contentSinks.ForEach(sink => sink(content));

                            innerBranch = capturedBranch.Remainder(content.Length);
                        }));

                        var innerSubscription = new SingleAssignmentDisposable();

                        disposables.Add(innerSubscription);

                        innerSubscription.Disposable = innerParser.Parse(capturedBranch).SubscribeSafe(
                            _ => { },
                            observer.OnError,
                            () =>
                        {
                            if (openCount > 0 && hasInnerResult)
                            {
                                self();
                            }
                            else
                            {
                                innerBranch.Dispose();

                                disposables.Remove(innerSubscription);
                                disposables.Remove(recursion);

                                if (hasInnerResult)
                                {
                                    try
                                    {
                                        bufferedResults.Concat().ForEach(observer.OnNext);
                                    }
                                    catch (Exception ex)
                                    {
                                        observer.OnError(ex);
                                    }

                                    observer.OnCompleted();
                                }
                                else
                                {
                                    // completing without results is failure
                                    observer.OnCompleted();
                                }
                            }
                        });
                    });
                },
                                    observer.OnError,
                                    () =>
                {
                    if (!hasOpenResult)
                    {
                        observer.OnCompleted();
                    }
                }));

                return disposables;
            }));
        }