using System; using JetBrains.Core; using JetBrains.Lifetimes; namespace JetBrains.Collections.Viewable { public sealed class WriteOnceProperty : IViewableProperty { private readonly WriteOnceSignal mySignal = new WriteOnceSignal(); private readonly object myLock = new object(); private Maybe myMaybe; public ISource Change => mySignal; public Maybe Maybe { get { lock (myLock) // to avoid non-atomic read return myMaybe; } } public T Value { get { return Maybe.OrElseThrow(() => new InvalidOperationException("Not initialized")); } set { if (!SetIfEmpty(value)) throw new InvalidOperationException($"WriteOnceProperty is already set with `{value}`, but you're trying to rewrite it with `{value}`"); } } public bool SetIfEmpty(T newValue) { lock(myLock) { if (myMaybe.HasValue) return false; myMaybe = new Maybe(newValue); } mySignal.Fire(newValue); return true; } public void Advise(Lifetime lifetime, Action handler) { if (lifetime.IsNotAlive) return; Maybe local; lock (myLock) { local = myMaybe; if (!local.HasValue) // to avoid calling the handler twice { mySignal.Advise(lifetime, handler); return; } } handler(local.Value); } // for test internal void fireInternal(T value) => mySignal.Fire(value); private sealed class WriteOnceSignal : SignalBase { private readonly LifetimeDefinition myDef = new LifetimeDefinition(); public Lifetime Lifetime => myDef.Lifetime; public override void Advise(Lifetime lifetime, Action handler) { if (Lifetime.IsNotAlive || lifetime.IsNotAlive) return; var nestedLifetime = Lifetime.Intersect(lifetime); base.Advise(nestedLifetime, handler); } public override void Fire(T value) { try { base.Fire(value); } finally { myDef.Terminate(); } } } } }