Index: Ringtoets/Common/src/Ringtoets.Common.Data/Ringtoets.Common.Data.csproj
===================================================================
diff -u -rb1537a16c5961d9f66d5564c215f4bee59294f82 -r92d8502b4ffd316c6387a0b1378e4d097c43c58d
--- Ringtoets/Common/src/Ringtoets.Common.Data/Ringtoets.Common.Data.csproj (.../Ringtoets.Common.Data.csproj) (revision b1537a16c5961d9f66d5564c215f4bee59294f82)
+++ Ringtoets/Common/src/Ringtoets.Common.Data/Ringtoets.Common.Data.csproj (.../Ringtoets.Common.Data.csproj) (revision 92d8502b4ffd316c6387a0b1378e4d097c43c58d)
@@ -100,6 +100,7 @@
+
Index: Ringtoets/Common/src/Ringtoets.Common.Data/UpdateDataStrategies/UpdateDataStrategyBase.cs
===================================================================
diff -u
--- Ringtoets/Common/src/Ringtoets.Common.Data/UpdateDataStrategies/UpdateDataStrategyBase.cs (revision 0)
+++ Ringtoets/Common/src/Ringtoets.Common.Data/UpdateDataStrategies/UpdateDataStrategyBase.cs (revision 92d8502b4ffd316c6387a0b1378e4d097c43c58d)
@@ -0,0 +1,174 @@
+// 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 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 General Public License for more details.
+//
+// You should have received a copy of the GNU 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;
+using System.Linq;
+using Core.Common.Base;
+using Core.Common.Utils;
+using Ringtoets.Common.Data.FailureMechanism;
+
+namespace Ringtoets.Common.Data.UpdateDataStrategies
+{
+ ///
+ /// Strategy for updating the current collection and dependent data with imported data:
+ ///
+ /// - Adds imported items that are not part of the current collection.
+ /// - Removes items that are part of the current collection, but are not part of the imported item collection.
+ /// - Updates the items that are part of the current collection and are part of the imported item collection.
+ ///
+ ///
+ /// The target data type.
+ /// The feature of the target data that should be validated on for the uniqueness of elements.
+ /// The failure mechanism in which the target collection should be updated.
+ public abstract class UpdateDataStrategyBase
+ where TTargetData : class
+ where TFeature : class
+ where TFailureMechanism : IFailureMechanism
+ {
+ protected TFailureMechanism failureMechanism;
+ private readonly IEqualityComparer equalityComparer;
+
+ ///
+ /// Instantiates a object.
+ ///
+ /// The failure mechanism which needs to be updated.
+ /// The comparer which should be used to determine when two objects are equal
+ /// Thrown when any input parameter is null.
+ protected UpdateDataStrategyBase(TFailureMechanism failureMechanism, IEqualityComparer equalityComparer)
+ {
+ if (failureMechanism == null)
+ {
+ throw new ArgumentNullException(nameof(failureMechanism));
+ }
+ if (equalityComparer == null)
+ {
+ throw new ArgumentNullException(nameof(equalityComparer));
+ }
+
+ this.equalityComparer = equalityComparer;
+ this.failureMechanism = failureMechanism;
+ }
+
+ ///
+ /// Updates the (dependent) objects with new data from the imported data.
+ ///
+ /// Objects that need to be updated.
+ /// The data that was imported.
+ /// An with affected objects.
+ /// Thrown when duplicate items are found.
+ protected abstract IEnumerable UpdateData(IEnumerable objectsToUpdate,
+ IEnumerable importedDataCollection);
+
+ ///
+ /// Removes the objects and their dependent data.
+ ///
+ /// The objects that are removed.
+ /// An with affected objects.
+ protected abstract IEnumerable RemoveData(IEnumerable removedObjects);
+
+ ///
+ /// Updates the items and their associated data within the target collection with the data contained
+ /// in the imported data collection.
+ ///
+ /// The target collection that needs to be updated.
+ /// The imported data collection that is used to update
+ /// the .
+ /// The source file path.
+ /// A of affected objects.
+ /// Thrown when any of the input parameters are null.
+ /// Thrown when duplicate items are being added to the
+ /// .
+ /// Thrown when duplicate items are found during the
+ /// update of the items to be updatd in the .
+ protected IEnumerable UpdateTargetCollectionData(ObservableUniqueItemCollectionWithSourcePath targetDataCollection,
+ IEnumerable importedDataCollection,
+ string sourceFilePath)
+ {
+ if (targetDataCollection == null)
+ {
+ throw new ArgumentNullException(nameof(targetDataCollection));
+ }
+ if (importedDataCollection == null)
+ {
+ throw new ArgumentNullException(nameof(importedDataCollection));
+ }
+ if (sourceFilePath == null)
+ {
+ throw new ArgumentNullException(nameof(sourceFilePath));
+ }
+
+ return ModifyDataCollection(targetDataCollection, importedDataCollection, sourceFilePath);
+ }
+
+ ///
+ /// Identifies which models were changed, removed and added to the target collection
+ /// when compared with the imported data and performs the necessary operations for
+ /// the dependent data of the affected elements.
+ ///
+ /// The target data collection which needs to be updated.
+ /// The imported data collection which is used to update
+ /// the
+ /// The source file path.
+ /// A with affected objects.
+ /// Thrown when duplicate items are being added to the
+ /// .
+ /// Thrown when duplicate items are found during the
+ /// update of the items to be updatd in the .
+ private IEnumerable ModifyDataCollection(ObservableUniqueItemCollectionWithSourcePath targetDataCollection,
+ IEnumerable importedDataCollection,
+ string sourceFilePath)
+ {
+ TTargetData[] importedObjects = importedDataCollection.ToArray();
+ TTargetData[] addedObjects = GetAddedObjects(targetDataCollection, importedObjects).ToArray();
+ TTargetData[] removedObjects = GetRemovedObjects(targetDataCollection, importedObjects).ToArray();
+ TTargetData[] updatedObjects = GetUpdatedObjects(targetDataCollection, importedObjects).ToArray();
+
+ var affectedObjects = new List();
+ if (addedObjects.Any())
+ {
+ affectedObjects.Add(targetDataCollection);
+ }
+ affectedObjects.AddRange(UpdateData(targetDataCollection, importedObjects));
+ affectedObjects.AddRange(RemoveData(removedObjects));
+
+ targetDataCollection.Clear();
+ targetDataCollection.AddRange(addedObjects.Union(updatedObjects), sourceFilePath);
+
+ return affectedObjects.Distinct(new ReferenceEqualityComparer());
+ }
+
+ private IEnumerable GetRemovedObjects(IEnumerable existingCollection, IEnumerable importedDataOjects)
+ {
+ return existingCollection.Except(importedDataOjects, equalityComparer);
+ }
+
+ private IEnumerable GetUpdatedObjects(IEnumerable existingCollection, IEnumerable importedDataObjects)
+ {
+ return existingCollection.Intersect(importedDataObjects, equalityComparer);
+ }
+
+ private IEnumerable GetAddedObjects(IEnumerable existingCollection, IEnumerable importedDataObjects)
+ {
+ return importedDataObjects.Where(source => !existingCollection.Contains(source, equalityComparer));
+ }
+ }
+}
\ No newline at end of file
Index: Ringtoets/Common/test/Ringtoets.Common.Data.Test/Ringtoets.Common.Data.Test.csproj
===================================================================
diff -u -rb1537a16c5961d9f66d5564c215f4bee59294f82 -r92d8502b4ffd316c6387a0b1378e4d097c43c58d
--- Ringtoets/Common/test/Ringtoets.Common.Data.Test/Ringtoets.Common.Data.Test.csproj (.../Ringtoets.Common.Data.Test.csproj) (revision b1537a16c5961d9f66d5564c215f4bee59294f82)
+++ Ringtoets/Common/test/Ringtoets.Common.Data.Test/Ringtoets.Common.Data.Test.csproj (.../Ringtoets.Common.Data.Test.csproj) (revision 92d8502b4ffd316c6387a0b1378e4d097c43c58d)
@@ -95,6 +95,7 @@
+
Index: Ringtoets/Common/test/Ringtoets.Common.Data.Test/UpdateDataStrategies/UpdateDataStrategyBaseTest.cs
===================================================================
diff -u
--- Ringtoets/Common/test/Ringtoets.Common.Data.Test/UpdateDataStrategies/UpdateDataStrategyBaseTest.cs (revision 0)
+++ Ringtoets/Common/test/Ringtoets.Common.Data.Test/UpdateDataStrategies/UpdateDataStrategyBaseTest.cs (revision 92d8502b4ffd316c6387a0b1378e4d097c43c58d)
@@ -0,0 +1,266 @@
+// 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 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 General Public License for more details.
+//
+// You should have received a copy of the GNU 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;
+using System.Linq;
+using Core.Common.Base;
+using NUnit.Framework;
+using Ringtoets.Common.Data.TestUtil;
+using Ringtoets.Common.Data.UpdateDataStrategies;
+
+namespace Ringtoets.Common.Data.Test.UpdateDataStrategies
+{
+ [TestFixture]
+ public class UpdateDataStrategyBaseTest
+ {
+ private readonly Func getUniqueFeature = item => item.Name;
+ private const string typeDescriptor = "TestItem";
+ private const string featureDescription = "naam";
+
+ [Test]
+ public void DefaultConstructor_FailureMechanismNull_ThrowsArgumentNullException()
+ {
+ // Call
+ TestDelegate call = () => new ConcreteUpdateDataStrategy(null);
+
+ // Assert
+ string paramName = Assert.Throws(call).ParamName;
+ Assert.AreEqual("failureMechanism", paramName);
+ }
+
+ [Test]
+ public void DefaultConstructor_EqualityComparerNull_ThrowsArgumentNullException()
+ {
+ // Call
+ TestDelegate call = () => new ConcreteUpdateDataStrategy(new TestFailureMechanism(), null);
+
+ // Assert
+ string paramName = Assert.Throws(call).ParamName;
+ Assert.AreEqual("equalityComparer", paramName);
+ }
+
+ [Test]
+ public void DefaultConstructor_FailureMechanismNotNull_DoesNotThrowException()
+ {
+ // Call
+ TestDelegate call = () => new ConcreteUpdateDataStrategy(new TestFailureMechanism());
+
+ // Assert
+ Assert.DoesNotThrow(call);
+ }
+
+ [Test]
+ public void UpdateTargetCollectionData_TargetCollectionNull_ThrowsArgumentNullException()
+ {
+ // Setup
+ var strategy = new ConcreteUpdateDataStrategy(new TestFailureMechanism());
+
+ // Call
+ TestDelegate call = () => strategy.ConcreteUpdateData(null, Enumerable.Empty(), string.Empty);
+
+ // Assert
+ string paramName = Assert.Throws(call).ParamName;
+ Assert.AreEqual("targetDataCollection", paramName);
+ }
+
+ [Test]
+ public void UpdateTargetCollectionData_ImportedDataCollectionNull_ThrowsArgumentNullException()
+ {
+ // Setup
+ var strategy = new ConcreteUpdateDataStrategy(new TestFailureMechanism());
+ var collection = new ObservableUniqueItemCollectionWithSourcePath(
+ getUniqueFeature, typeDescriptor, featureDescription);
+
+ // Call
+ TestDelegate call = () => strategy.ConcreteUpdateData(collection, null, string.Empty);
+
+ // Assert
+ string paramName = Assert.Throws(call).ParamName;
+ Assert.AreEqual("importedDataCollection", paramName);
+ }
+
+ [Test]
+ public void UpdateTargetCollectionData_SourceFilePathNull_ThrowsArgumentNullException()
+ {
+ // Setup
+ var strategy = new ConcreteUpdateDataStrategy(new TestFailureMechanism());
+ var collection = new ObservableUniqueItemCollectionWithSourcePath(
+ getUniqueFeature, typeDescriptor, featureDescription);
+
+ // Call
+ TestDelegate call = () => strategy.ConcreteUpdateData(collection, Enumerable.Empty(), null);
+
+ // Assert
+ string paramName = Assert.Throws(call).ParamName;
+ Assert.AreEqual("sourceFilePath", paramName);
+ }
+
+ [Test]
+ public void UpdateTargetCollectionData_WithCollectionAndImportedDataEmpty_ClearsTargetCollection()
+ {
+ // Setup
+ var collection = new ObservableUniqueItemCollectionWithSourcePath(
+ getUniqueFeature, typeDescriptor, featureDescription);
+ const string filePath = "path";
+ collection.AddRange(new[]
+ {
+ new TestItem("Name A"),
+ new TestItem("Name B")
+ }, filePath);
+
+ var strategy = new ConcreteUpdateDataStrategy(new TestFailureMechanism());
+
+ // Call
+ strategy.ConcreteUpdateData(collection, Enumerable.Empty(), filePath);
+
+ // Assert
+ CollectionAssert.IsEmpty(collection);
+ Assert.AreEqual(filePath, collection.SourcePath);
+ }
+
+ [Test]
+ public void UpdateTargetCollectionData_Call_CallsFunctions()
+ {
+ // Setup
+ var collection = new ObservableUniqueItemCollectionWithSourcePath(
+ getUniqueFeature, typeDescriptor, featureDescription);
+
+ var strategy = new ConcreteUpdateDataStrategy(new TestFailureMechanism());
+
+ // Call
+ strategy.ConcreteUpdateData(collection, Enumerable.Empty(), "path");
+
+ // Assert
+ Assert.IsTrue(strategy.IsUpdateDataCalled);
+ Assert.IsTrue(strategy.IsRemoveDataCalled);
+ }
+
+ [Test]
+ public void UpdateTargetCollectionData_WithEmptyCollectionAndImportedDataCollectionNotEmpty_AddsNewItems()
+ {
+ // Setup
+ var collection = new ObservableUniqueItemCollectionWithSourcePath(
+ getUniqueFeature, typeDescriptor, featureDescription);
+ var importedCollection = new[]
+ {
+ new TestItem("Name A"),
+ new TestItem("Name B")
+ };
+
+ var strategy = new ConcreteUpdateDataStrategy(new TestFailureMechanism());
+
+ // Call
+ IEnumerable affectedObjects = strategy.ConcreteUpdateData(collection, importedCollection, "path");
+
+ // Assert
+ CollectionAssert.AreEqual(importedCollection, collection);
+ CollectionAssert.Contains(affectedObjects, collection);
+ Assert.AreEqual("path", collection.SourcePath);
+ }
+
+ [Test]
+ public void UpdateTargetCollectionData_ImportedDataContainsDuplicateData_ThrowsArgumentException()
+ {
+ // Setup
+ var collection = new ObservableUniqueItemCollectionWithSourcePath(
+ getUniqueFeature, typeDescriptor, featureDescription);
+
+ const string duplicateName = "Duplicate Name";
+ var importedCollection = new[]
+ {
+ new TestItem(duplicateName),
+ new TestItem(duplicateName)
+ };
+
+ var strategy = new ConcreteUpdateDataStrategy(new TestFailureMechanism());
+
+ // Call
+ TestDelegate call = () => strategy.ConcreteUpdateData(collection, importedCollection, "path");
+
+ // Assert
+ var exception = Assert.Throws(call);
+ string message = $"{typeDescriptor} moeten een unieke {featureDescription} hebben. Gevonden dubbele elementen: {duplicateName}.";
+ Assert.AreEqual(message, exception.Message);
+
+ CollectionAssert.IsEmpty(collection);
+ }
+
+ private class ConcreteUpdateDataStrategy : UpdateDataStrategyBase
+ {
+ public bool IsUpdateDataCalled { get; private set; }
+ public bool IsRemoveDataCalled { get; private set; }
+
+ public ConcreteUpdateDataStrategy(TestFailureMechanism failureMechanism, IEqualityComparer comparer)
+ : base(failureMechanism, comparer) {}
+
+ public ConcreteUpdateDataStrategy(TestFailureMechanism failureMechanism)
+ : base(failureMechanism, new NameComparer()) {}
+
+ public IEnumerable ConcreteUpdateData(ObservableUniqueItemCollectionWithSourcePath targetCollection,
+ IEnumerable importedDataCollection,
+ string sourceFilePath)
+ {
+ return UpdateTargetCollectionData(targetCollection, importedDataCollection, sourceFilePath);
+ }
+
+ protected override IEnumerable UpdateData(IEnumerable objectsToUpdate, IEnumerable importedDataCollection)
+ {
+ IsUpdateDataCalled = true;
+ return Enumerable.Empty();
+ }
+
+ protected override IEnumerable RemoveData(IEnumerable removedObjects)
+ {
+ IsRemoveDataCalled = true;
+ return Enumerable.Empty();
+ }
+
+ private class NameComparer : IEqualityComparer
+ {
+ public bool Equals(TestItem x, TestItem y)
+ {
+ return x.Name == y.Name;
+ }
+
+ public int GetHashCode(TestItem obj)
+ {
+ return obj.Name.GetHashCode();
+ }
+ }
+ }
+
+ private class TestItem
+ {
+ public string Name { get; }
+
+ public TestItem(string name)
+ {
+ Name = name;
+ }
+
+ public override string ToString()
+ {
+ return Name;
+ }
+ }
+ }
+}
\ No newline at end of file