Index: Ringtoets/Common/src/Ringtoets.Common.Data/UpdateDataStrategies/UpdateDataStrategyBase.cs =================================================================== diff -u -rc6b6a1a125535151416a5426d75472d045943d1a -r4e8fa8a9a88207029ec813bcf15f1eff9e0ab118 --- Ringtoets/Common/src/Ringtoets.Common.Data/UpdateDataStrategies/UpdateDataStrategyBase.cs (.../UpdateDataStrategyBase.cs) (revision c6b6a1a125535151416a5426d75472d045943d1a) +++ Ringtoets/Common/src/Ringtoets.Common.Data/UpdateDataStrategies/UpdateDataStrategyBase.cs (.../UpdateDataStrategyBase.cs) (revision 4e8fa8a9a88207029ec813bcf15f1eff9e0ab118) @@ -95,6 +95,8 @@ /// The source file path. /// A of affected objects. /// Thrown when an error occurred while updating the data. + /// Thrown when contains + /// null elements. protected IEnumerable UpdateTargetCollectionData(ObservableUniqueItemCollectionWithSourcePath targetDataCollection, IEnumerable importedDataCollection, string sourceFilePath) @@ -122,7 +124,7 @@ /// /// The target data collection which needs to be updated. /// The imported data collection which is used to update - /// the + /// the . /// The source file path. /// A with affected objects. /// Thrown when: @@ -131,13 +133,17 @@ /// duplicate items are found in the . /// /// + /// Thrown when either + /// or is null. + /// Thrown when contains + /// null elements. private IEnumerable ModifyDataCollection(ObservableUniqueItemCollectionWithSourcePath targetDataCollection, IEnumerable importedDataCollection, string sourceFilePath) { TTargetData[] importedObjects = importedDataCollection.ToArray(); - var modification = new Modification(targetDataCollection, importedObjects, equalityComparer); + var modification = new CollectionModification(targetDataCollection, importedObjects, equalityComparer); var affectedObjects = new List(); if (modification.HasUpdates()) @@ -209,20 +215,45 @@ /// /// Inner class for obtaining the modifications of an update action. /// - private class Modification + private class CollectionModification { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The current collection of objects. - /// The collection of objects that were imported. + /// The collection containing objects that were imported + /// and thus could contain updates for the existing objects. /// The comparer to test whether elements in /// have a matching element in /// . - public Modification(IEnumerable existingObjects, + /// Thrown if any parameter is null. + /// Thrown when + /// or contains a null element. + public CollectionModification(IEnumerable existingObjects, IEnumerable updatedObjects, IEqualityComparer equalityComparer) { + if (existingObjects == null) + { + throw new ArgumentNullException(nameof(existingObjects)); + } + if (updatedObjects == null) + { + throw new ArgumentNullException(nameof(updatedObjects)); + } + if (equalityComparer == null) + { + throw new ArgumentNullException(nameof(equalityComparer)); + } + if (existingObjects.Contains(null)) + { + throw new ArgumentException(@"Cannot determine modifications from a collection which contain null.", nameof(existingObjects)); + } + if (updatedObjects.Contains(null)) + { + throw new ArgumentException(@"Cannot determine modifications with a collection which contain null.", nameof(updatedObjects)); + } + TTargetData[] existingArray = existingObjects.ToArray(); var index = 0; @@ -256,68 +287,38 @@ /// /// Gets a collection of updated objects from the existing object collection and the - /// added objects from the imported object collection, in the same order that was + /// added objects from the imported object collection in the same order that was /// found in the imported object collection. /// /// An ordered collection of updated and added elements. public IEnumerable GetModifiedCollection() { - TTargetData[] remainingObjects = ObjectsToBeUpdated.Values.Union(ObjectsToBeAdded.Values).ToArray(); - int[] indices = GetElementOrder(); + KeyValuePair[] remainingObjects = ObjectsToBeUpdated.Union(ObjectsToBeAdded).ToArray(); - foreach (int i in Enumerable.Range(0, remainingObjects.Length)) + var orderedObjects = new TTargetData[remainingObjects.Length]; + + foreach (KeyValuePair remainingObject in remainingObjects) { - TTargetData x = remainingObjects[i]; - int j = i; - while (true) - { - int k = indices[j]; - indices[j] = j; - if (k == i) - { - break; - } - remainingObjects[j] = remainingObjects[k]; - j = k; - } - remainingObjects[j] = x; + orderedObjects[remainingObject.Key] = remainingObject.Value; } - return remainingObjects; + return orderedObjects; } /// /// Finds out whether there was a difference between the existing and the imported /// object collections. /// - /// + /// true if there were any updates, false otherwise. public bool HasUpdates() { return ObjectsToBeRemoved.Any() || ObjectsToBeAdded.Any() || ObjectsToBeUpdated.Any(); } private Dictionary ObjectsToBeAdded { get; } = new Dictionary(); - - private int[] GetElementOrder() - { - int[] keys = ObjectsToBeUpdated.Keys.Union(ObjectsToBeAdded.Keys).ToArray(); - - int orderLength = keys.Length; - var order = new int[orderLength]; - for (var valueToInsert = 0; valueToInsert < orderLength; valueToInsert++) - { - order[keys[valueToInsert]] = valueToInsert; - } - - return order; - } - + private static int FindIndex(TTargetData[] collectionToLookIn, TTargetData objectToFind, IEqualityComparer equalityComparer) { - if (objectToFind == null) - { - throw new ArgumentNullException(nameof(objectToFind)); - } for (var i = 0; i < collectionToLookIn.Length; i++) { TTargetData targetData = collectionToLookIn[i]; Index: Ringtoets/Common/test/Ringtoets.Common.Data.Test/UpdateDataStrategies/UpdateDataStrategyBaseTest.cs =================================================================== diff -u -rc6b6a1a125535151416a5426d75472d045943d1a -r4e8fa8a9a88207029ec813bcf15f1eff9e0ab118 --- Ringtoets/Common/test/Ringtoets.Common.Data.Test/UpdateDataStrategies/UpdateDataStrategyBaseTest.cs (.../UpdateDataStrategyBaseTest.cs) (revision c6b6a1a125535151416a5426d75472d045943d1a) +++ Ringtoets/Common/test/Ringtoets.Common.Data.Test/UpdateDataStrategies/UpdateDataStrategyBaseTest.cs (.../UpdateDataStrategyBaseTest.cs) (revision 4e8fa8a9a88207029ec813bcf15f1eff9e0ab118) @@ -86,17 +86,33 @@ { // Setup var strategy = new TestUpdateDataStrategy(new TestFailureMechanism()); - var collection = new TestUniqueItemCollection(); // Call - TestDelegate call = () => strategy.ConcreteUpdateData(collection, null, string.Empty); + TestDelegate call = () => strategy.ConcreteUpdateData(new TestUniqueItemCollection(), null, string.Empty); // Assert string paramName = Assert.Throws(call).ParamName; Assert.AreEqual("importedDataCollection", paramName); } [Test] + public void UpdateTargetCollectionData_ImportedDataCollectionContainsNullElement_ThrowsArgumentException() + { + // Setup + var strategy = new TestUpdateDataStrategy(new TestFailureMechanism()); + + // Call + TestDelegate call = () => strategy.ConcreteUpdateData(new TestUniqueItemCollection(), new TestItem[] + { + null + }, string.Empty); + + // Assert + string paramName = Assert.Throws(call).ParamName; + Assert.AreEqual("updatedObjects", paramName); + } + + [Test] public void UpdateTargetCollectionData_SourceFilePathNull_ThrowsArgumentNullException() { // Setup @@ -475,6 +491,36 @@ } [Test] + public void UpdateTargetCollectionData_CollectionOrderChangedAndElementRemoved_UpdatesCollectionInCorrectOrder() + { + // Setup + var currentCollection = new[] + { + new TestItem("Item one"), + new TestItem("Item two"), + new TestItem("Item three") + }; + var collection = new TestUniqueItemCollection(); + collection.AddRange(currentCollection, sourceFilePath); + + var importedItems = new[] + { + new TestItem("Item three"), + new TestItem("Item one") + }; + + var strategy = new TestUpdateDataStrategy(new TestFailureMechanism()); + + // Call + strategy.ConcreteUpdateData(collection, + importedItems, + sourceFilePath); + + // Assert + CollectionAssert.AreEqual(importedItems, collection); + } + + [Test] public void UpdateTargetCollectionData_CollectionNotEmptyAndImportedDataHasDuplicateDefinitions_ThrowsUpdateDataException() { // Setup