LiveElement CreateChildElement(SourceFragment sourceFragment)
        {
            var elm = new LiveElement(parent: this);

            elm.UpdateFrom(sourceFragment.ToXml());
            return(elm);
        }
예제 #2
0
        public static LiveDocument Open(
            AbsoluteFilePath path,
            IFileSystem fs,
            IObservable <ILookup <ObjectIdentifier, ObjectIdentifier> > metadata,
            BehaviorSubject <Dictionary <ObjectIdentifier, IElement> > idToElement,
            IObserver <IBinaryMessage> mutations,
            IScheduler scheduler)
        {
            IDocument <byte[]> fileOnDisk = new FileWatchingDocument(fs, path);


            var parsingErrors = new BehaviorSubject <Optional <Exception> >(Optional.None());

            var invalidated = new Subject <Unit>();

            var root = new LiveElement(
                file: path,
                metadata: metadata,
                isReadOnly: fileOnDisk.ErrorsDuringLoading.Or(parsingErrors)
                .Select(e => e.HasValue)
                .DistinctUntilChanged()
                .Replay(1).RefCount(),
                invalidated: invalidated,
                mutations: mutations,
                getElement: id =>
                idToElement.Value.TryGetValue(id).Or(Element.Empty));

            Optional <XElement> xElementForSaving = Optional.None <XElement>();

            var allElements = root.Subtree().Replay(1);

            var source = new ReplaySubject <SourceFragment>(1);

            return(new LiveDocument
            {
                _garbage = Disposable.Combine(

                    // Save on internal changes
                    invalidated
                    .Select(_ =>
                {
                    if (xElementForSaving.HasValue)
                    {
                        var sourceFragment = SourceFragment.FromXml(xElementForSaving.Value);
                        source.OnNext(sourceFragment);
                        return Optional.Some(sourceFragment);
                    }
                    return Optional.None();
                })
                    .NotNone()
                    .Throttle(TimeSpan.FromSeconds(0.5), scheduler)
                    .Select(sourceFragment =>
                            Observable.FromAsync(async() =>
                                                 await fileOnDisk.Save(sourceFragment.ToBytes())))
                    .Concat()
                    .Subscribe(),

                    // Load on external changes
                    fileOnDisk.ExternalChanges
                    .ObserveOn(Application.MainThread)
                    .Subscribe(reload =>
                {
                    var sourceFragment = SourceFragment.FromBytes(reload);
                    source.OnNext(sourceFragment);

                    try
                    {
                        var simulatorWasFaulted = parsingErrors.Value.HasValue;

                        var newDocument = sourceFragment.ToXml();
                        parsingErrors.OnNext(Optional.None());

                        xElementForSaving = Optional.None();
                        Console.WriteLine("Reloading " + path + " from disk...");
                        root.UpdateFrom(newDocument);                                                 // no known reasons to throw
                        xElementForSaving = Optional.Some(newDocument);

                        // hack to clear errors from the simulator,
                        // since UpdateFrom() doesn't know that the simulator
                        // have failed and will try to emit incremental updates
                        if (simulatorWasFaulted)
                        {
                            mutations.OnNext(new ReifyRequired());
                        }
                    }
                    catch (Exception e)
                    {
                        parsingErrors.OnNext(e);

                        // hack to get errors from the simulator
                        mutations.OnNext(new ReifyRequired());
                    }
                }),

                    // Share subscription to Eleements
                    allElements.Connect(),

                    // Dispose fileOnDisk when disposed
                    fileOnDisk),

                FilePath = Observable.Return(path),

                Errors = fileOnDisk.Errors.Or(parsingErrors),

                SimulatorIdPrefix = path.NativePath,

                Source = source,

                Root = root,
                Elements = allElements,

                _root = root,
                _path = path,
                _idToElement = idToElement,
            });
        }