Index: Core/Common/src/Core.Common.Base/Core.Common.Base.csproj =================================================================== diff -u -r0dbf681489bfe1d279eef99030f9ee13abebbb23 -r07b0e83704b56684d617cd6e8b8570417654912a --- Core/Common/src/Core.Common.Base/Core.Common.Base.csproj (.../Core.Common.Base.csproj) (revision 0dbf681489bfe1d279eef99030f9ee13abebbb23) +++ Core/Common/src/Core.Common.Base/Core.Common.Base.csproj (.../Core.Common.Base.csproj) (revision 07b0e83704b56684d617cd6e8b8570417654912a) @@ -98,6 +98,7 @@ + Index: Core/Common/src/Core.Common.Base/Observer.cs =================================================================== diff -u -r0dbf681489bfe1d279eef99030f9ee13abebbb23 -r07b0e83704b56684d617cd6e8b8570417654912a --- Core/Common/src/Core.Common.Base/Observer.cs (.../Observer.cs) (revision 0dbf681489bfe1d279eef99030f9ee13abebbb23) +++ Core/Common/src/Core.Common.Base/Observer.cs (.../Observer.cs) (revision 07b0e83704b56684d617cd6e8b8570417654912a) @@ -24,7 +24,7 @@ namespace Core.Common.Base { /// - /// Class that implements the pattern. + /// Class that implements in a way that a single can be observed. /// /// /// The being observed by instances of this class can be dynamically changed. @@ -75,10 +75,7 @@ public void Dispose() { - if (observable != null) - { - observable.Detach(this); - } + Observable = null; } } } \ No newline at end of file Index: Core/Common/src/Core.Common.Base/RecursiveObserver.cs =================================================================== diff -u --- Core/Common/src/Core.Common.Base/RecursiveObserver.cs (revision 0) +++ Core/Common/src/Core.Common.Base/RecursiveObserver.cs (revision 07b0e83704b56684d617cd6e8b8570417654912a) @@ -0,0 +1,119 @@ +// Copyright (C) Stichting Deltares 2016. All rights reserved. +// +// This file is part of Ringtoets. +// +// Ringtoets is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// +// All names, logos, and references to "Deltares" are registered trademarks of +// Stichting Deltares and remain full property of Stichting Deltares at all times. +// All rights reserved. + +using System; +using System.Collections.Generic; + +namespace Core.Common.Base +{ + /// + /// Class that implements in a way that a hierarchy of objects can be observed recursively. + /// + /// + /// The root being observed by instances of this class can be dynamically changed. + /// + /// The type of objects to observe recursively. + public class RecursiveObserver : IObserver, IDisposable where T : class, IObservable + { + private T rootObservable; + private readonly Action updateObserverAction; + private readonly Func> getChildObservables; + private readonly IList observedObjects = new List(); + + /// + /// Creates a new instance of the class. + /// + /// The action to perform on notifications coming from one of the items of the hierarchy of observed objects. + /// The method used for recursively obtaining the objects to observe. + public RecursiveObserver(Action updateObserverAction, Func> getChildObservables) + { + this.updateObserverAction = updateObserverAction; + this.getChildObservables = getChildObservables; + } + + /// + /// Gets or sets the root object to observe. + /// + public T Observable + { + get + { + return rootObservable; + } + set + { + rootObservable = value; + + UpdateObservedObjects(); + } + } + + public void UpdateObserver() + { + updateObserverAction(); + + // Update the list of observed objects as observables might have been added/removed + UpdateObservedObjects(); + } + + private void UpdateObservedObjects() + { + // Detach from the currently observed objects + foreach (var observedObject in observedObjects) + { + observedObject.Detach(this); + } + + // Clear the list of observed objects + observedObjects.Clear(); + + // If relevant, start observing objects again + if (rootObservable != null) + { + foreach (var objectToObserve in GetObservablesRecursive(rootObservable)) + { + objectToObserve.Attach(this); + observedObjects.Add(objectToObserve); + } + } + } + + private IEnumerable GetObservablesRecursive(T observable) + { + var observables = new List + { + observable + }; + + foreach (var childObservable in getChildObservables(observable)) + { + observables.AddRange(GetObservablesRecursive(childObservable)); + } + + return observables; + } + + public void Dispose() + { + Observable = null; + } + } +} Index: Core/Common/test/Core.Common.Base.Test/Core.Common.Base.Test.csproj =================================================================== diff -u -r0dbf681489bfe1d279eef99030f9ee13abebbb23 -r07b0e83704b56684d617cd6e8b8570417654912a --- Core/Common/test/Core.Common.Base.Test/Core.Common.Base.Test.csproj (.../Core.Common.Base.Test.csproj) (revision 0dbf681489bfe1d279eef99030f9ee13abebbb23) +++ Core/Common/test/Core.Common.Base.Test/Core.Common.Base.Test.csproj (.../Core.Common.Base.Test.csproj) (revision 07b0e83704b56684d617cd6e8b8570417654912a) @@ -93,6 +93,7 @@ + Index: Core/Common/test/Core.Common.Base.Test/ObserverTest.cs =================================================================== diff -u -r0dbf681489bfe1d279eef99030f9ee13abebbb23 -r07b0e83704b56684d617cd6e8b8570417654912a --- Core/Common/test/Core.Common.Base.Test/ObserverTest.cs (.../ObserverTest.cs) (revision 0dbf681489bfe1d279eef99030f9ee13abebbb23) +++ Core/Common/test/Core.Common.Base.Test/ObserverTest.cs (.../ObserverTest.cs) (revision 07b0e83704b56684d617cd6e8b8570417654912a) @@ -29,12 +29,19 @@ [Test] public void DefaultConstructor_DefaultValues() { + // Setup + var counter = 0; + // Call - var observer = new Observer(() => { }); + var observer = new Observer(() => { counter++; }); // Assert Assert.IsInstanceOf(observer); Assert.IsNull(observer.Observable); + Assert.AreEqual(0, counter); + + observer.UpdateObserver(); + Assert.AreEqual(1, counter); } [Test] Index: Core/Common/test/Core.Common.Base.Test/RecursiveObserverTest.cs =================================================================== diff -u --- Core/Common/test/Core.Common.Base.Test/RecursiveObserverTest.cs (revision 0) +++ Core/Common/test/Core.Common.Base.Test/RecursiveObserverTest.cs (revision 07b0e83704b56684d617cd6e8b8570417654912a) @@ -0,0 +1,169 @@ +// Copyright (C) Stichting Deltares 2016. All rights reserved. +// +// This file is part of Ringtoets. +// +// Ringtoets is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// +// All names, logos, and references to "Deltares" are registered trademarks of +// Stichting Deltares and remain full property of Stichting Deltares at all times. +// All rights reserved. + +using System.Collections.Generic; +using NUnit.Framework; + +namespace Core.Common.Base.Test +{ + [TestFixture] + public class RecursiveObserverTest + { + [Test] + public void DefaultConstructor_DefaultValues() + { + // Setup + var counter = 0; + + // Call + var recursiveObserver = new RecursiveObserver(() => { counter++; }, GetChildObservables); + + // Assert + Assert.IsInstanceOf(recursiveObserver); + Assert.IsNull(recursiveObserver.Observable); + Assert.AreEqual(0, counter); + + recursiveObserver.UpdateObserver(); + Assert.AreEqual(1, counter); + } + + [TestCase(0)] + [TestCase(1)] + [TestCase(2)] + [TestCase(100)] + public void RecursiveObserver_WithObservableHierarchy_NotifyObserversAtSpecifiedLevelResultsInPerformingUpdateObserversAction(int nestingLevel) + { + // Setup + var counter = 0; + var rootObservable = new TestObservable(); + + var currentNestedObservable = rootObservable; + + for (var i = 0; i < nestingLevel; i++) + { + var newObservable = new TestObservable(); + + currentNestedObservable.ChildTestObservables.Add(newObservable); + + currentNestedObservable = newObservable; + } + + var recursiveObserver = new RecursiveObserver(() => counter++, GetChildObservables) + { + Observable = rootObservable + }; + + // Call + currentNestedObservable.NotifyObservers(); + + // Assert + Assert.AreEqual(1, counter); + } + + [TestCase(0)] + [TestCase(1)] + [TestCase(2)] + [TestCase(100)] + public void RecursiveObserver_WithObservableHierarchySetAndThenUnset_NotifyObserversNoLongerResultsInPerformingUpdateObserversAction(int nestingLevel) + { + // Setup + var counter = 0; + var rootObservable = new TestObservable(); + + var currentNestedObservable = rootObservable; + + for (var i = 0; i < nestingLevel; i++) + { + var newObservable = new TestObservable(); + + currentNestedObservable.ChildTestObservables.Add(newObservable); + + currentNestedObservable = newObservable; + } + + var recursiveObserver = new RecursiveObserver(() => counter++, GetChildObservables) + { + Observable = rootObservable + }; + + recursiveObserver.Observable = null; + + // Call + currentNestedObservable.NotifyObservers(); + + // Assert + Assert.AreEqual(0, counter); + } + + [TestCase(0)] + [TestCase(1)] + [TestCase(2)] + [TestCase(100)] + public void RecursiveObserver_WithObservableHierarchySetAndThenDisposed_NotifyObserversNoLongerResultsInPerformingUpdateObserversAction(int nestingLevel) + { + // Setup + var counter = 0; + var rootObservable = new TestObservable(); + + var currentNestedObservable = rootObservable; + + for (var i = 0; i < nestingLevel; i++) + { + var newObservable = new TestObservable(); + + currentNestedObservable.ChildTestObservables.Add(newObservable); + + currentNestedObservable = newObservable; + } + + var recursiveObserver = new RecursiveObserver(() => counter++, GetChildObservables) + { + Observable = rootObservable + }; + + recursiveObserver.Dispose(); + + // Call + currentNestedObservable.NotifyObservers(); + + // Assert + Assert.AreEqual(0, counter); + } + + private class TestObservable : Observable + { + private readonly IList childTestObservables = new List(); + + public IList ChildTestObservables + { + get + { + return childTestObservables; + } + } + } + + private IEnumerable GetChildObservables(TestObservable testObservable) + { + return testObservable.ChildTestObservables; + } + } +} Index: Ringtoets/Piping/src/Ringtoets.Piping.Forms/Views/PipingCalculationsView.cs =================================================================== diff -u -r0dbf681489bfe1d279eef99030f9ee13abebbb23 -r07b0e83704b56684d617cd6e8b8570417654912a --- Ringtoets/Piping/src/Ringtoets.Piping.Forms/Views/PipingCalculationsView.cs (.../PipingCalculationsView.cs) (revision 0dbf681489bfe1d279eef99030f9ee13abebbb23) +++ Ringtoets/Piping/src/Ringtoets.Piping.Forms/Views/PipingCalculationsView.cs (.../PipingCalculationsView.cs) (revision 07b0e83704b56684d617cd6e8b8570417654912a) @@ -19,7 +19,6 @@ // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. -using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; @@ -228,78 +227,6 @@ #region Nested types - private class RecursiveObserver : IObserver where T : IObservable - { - private T observable; - private readonly Action updateObserverAction; - private readonly Func> getChildObservables; - private readonly IList observedObjects = new List(); - - public RecursiveObserver(Action updateObserverAction, Func> getChildObservables) - { - this.updateObserverAction = updateObserverAction; - this.getChildObservables = getChildObservables; - } - - public T Observable - { - get - { - return observable; - } - set - { - observable = value; - - UpdateObservedObjects(); - } - } - - public void UpdateObserver() - { - updateObserverAction(); - - UpdateObservedObjects(); - } - - private void UpdateObservedObjects() - { - // Detach from the currently attached observable items - foreach (var observedObject in observedObjects) - { - observedObject.Detach(this); - } - - // Clear the list - observedObjects.Clear(); - - // If relevant, start observing objects again - if (observable != null) - { - foreach (var objectToObserve in GetObservablesRecursive(observable)) - { - objectToObserve.Attach(this); - observedObjects.Add(objectToObserve); - } - } - } - - private IEnumerable GetObservablesRecursive(T parentObservable) - { - var observables = new List - { - parentObservable - }; - - foreach (var childObservable in getChildObservables(parentObservable)) - { - observables.AddRange(GetObservablesRecursive(childObservable)); - } - - return observables; - } - } - private class PipingCalculationRow { private readonly PipingCalculation pipingCalculation;