From 1790e3a80d58472a508dfb5778f6eecf7f58061c Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Mon, 20 Nov 2023 13:49:52 +0100 Subject: [PATCH 1/5] Abstracting some method in `ValidationUtils`. --- .../DuplicateEntitiesException.java | 27 +++++ .../ie3/datamodel/utils/ExceptionUtils.java | 13 ++ .../GridContainerValidationUtils.java | 114 ++++++------------ .../utils/validation/ValidationUtils.java | 48 ++++++++ .../validation/ValidationUtilsTest.groovy | 111 +++++++++-------- 5 files changed, 186 insertions(+), 127 deletions(-) create mode 100644 src/main/java/edu/ie3/datamodel/exceptions/DuplicateEntitiesException.java diff --git a/src/main/java/edu/ie3/datamodel/exceptions/DuplicateEntitiesException.java b/src/main/java/edu/ie3/datamodel/exceptions/DuplicateEntitiesException.java new file mode 100644 index 000000000..abf5f1d92 --- /dev/null +++ b/src/main/java/edu/ie3/datamodel/exceptions/DuplicateEntitiesException.java @@ -0,0 +1,27 @@ +/* + * © 2023. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation +*/ +package edu.ie3.datamodel.exceptions; + +import edu.ie3.datamodel.models.UniqueEntity; +import edu.ie3.datamodel.utils.ExceptionUtils; +import java.util.Collection; + +public class DuplicateEntitiesException extends ValidationException { + + protected DuplicateEntitiesException(String s) { + super(s); + } + + protected DuplicateEntitiesException(String s, String entities) { + super(s + entities); + } + + public DuplicateEntitiesException(String fieldName, Collection entities) { + this( + "The following entities have duplicate '" + fieldName + "':", + ExceptionUtils.combine(entities)); + } +} diff --git a/src/main/java/edu/ie3/datamodel/utils/ExceptionUtils.java b/src/main/java/edu/ie3/datamodel/utils/ExceptionUtils.java index 018580d20..1569adf0e 100644 --- a/src/main/java/edu/ie3/datamodel/utils/ExceptionUtils.java +++ b/src/main/java/edu/ie3/datamodel/utils/ExceptionUtils.java @@ -5,7 +5,10 @@ */ package edu.ie3.datamodel.utils; +import edu.ie3.datamodel.models.UniqueEntity; +import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; public class ExceptionUtils { private ExceptionUtils() { @@ -24,4 +27,14 @@ public static String getMessages(List exceptions) { .reduce("", (a, b) -> a + "\n " + b) .replaceFirst("\n ", ""); } + + /** + * Combines multiple {@link UniqueEntity} into a string. + * + * @param entities to be combined + * @return a string + */ + public static String combine(Collection entities) { + return entities.stream().map(UniqueEntity::toString).collect(Collectors.joining("\n - ")); + } } diff --git a/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java b/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java index 1ef47aeb5..27fd23d61 100644 --- a/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java +++ b/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java @@ -5,10 +5,8 @@ */ package edu.ie3.datamodel.utils.validation; -import edu.ie3.datamodel.exceptions.InvalidEntityException; -import edu.ie3.datamodel.exceptions.InvalidGridException; -import edu.ie3.datamodel.exceptions.UnsafeEntityException; -import edu.ie3.datamodel.exceptions.ValidationException; +import edu.ie3.datamodel.exceptions.*; +import edu.ie3.datamodel.models.UniqueEntity; import edu.ie3.datamodel.models.input.AssetInput; import edu.ie3.datamodel.models.input.MeasurementUnitInput; import edu.ie3.datamodel.models.input.NodeInput; @@ -24,6 +22,7 @@ public class GridContainerValidationUtils extends ValidationUtils { + @Deprecated private static String duplicateUuidsString(String simpleName, Optional exceptionString) { return "The provided entities in '" + simpleName @@ -52,18 +51,10 @@ private GridContainerValidationUtils() { return List.of(isNull); } - List> exceptions = new ArrayList<>(); - /* sanity check to ensure distinct UUIDs */ - Optional exceptionString = - checkForDuplicateUuids(new HashSet<>(gridContainer.allEntitiesAsList())); - exceptions.add( - Try.ofVoid( - exceptionString.isPresent(), - () -> - new InvalidGridException( - duplicateUuidsString( - gridContainer.getClass().getSimpleName(), exceptionString)))); + List> exceptions = + new ArrayList<>( + checkForDuplicate(gridContainer.allEntitiesAsList(), UniqueEntity::getUuid)); exceptions.addAll(checkRawGridElements(gridContainer.getRawGrid())); exceptions.addAll( @@ -98,18 +89,10 @@ private GridContainerValidationUtils() { return List.of(isNull); } - List> exceptions = new ArrayList<>(); - /* sanity check to ensure distinct UUIDs */ - Optional exceptionString = - checkForDuplicateUuids(new HashSet<>(rawGridElements.allEntitiesAsList())); - exceptions.add( - Try.ofVoid( - exceptionString.isPresent(), - () -> - new InvalidGridException( - duplicateUuidsString( - rawGridElements.getClass().getSimpleName(), exceptionString)))); + List> exceptions = + new ArrayList<>( + checkForDuplicate(rawGridElements.allEntitiesAsList(), UniqueEntity::getUuid)); /* Checking nodes */ Set nodes = rawGridElements.getNodes(); @@ -183,18 +166,18 @@ private GridContainerValidationUtils() { * Checks the validity of type ids of every entity. * * @param rawGridElements the raw grid elements - * @return a list of try objects either containing an {@link UnsafeEntityException} or an empty - * Success + * @return a list of try objects either containing an {@link DuplicateEntitiesException} or an + * empty Success */ - protected static List> checkRawGridTypeIds( + protected static List> checkRawGridTypeIds( RawGridElements rawGridElements) { - List> exceptions = new ArrayList<>(); - exceptions.addAll(ValidationUtils.checkIds(rawGridElements.getNodes())); - exceptions.addAll(ValidationUtils.checkIds(rawGridElements.getLines())); - exceptions.addAll(ValidationUtils.checkIds(rawGridElements.getTransformer2Ws())); - exceptions.addAll(ValidationUtils.checkIds(rawGridElements.getTransformer3Ws())); - exceptions.addAll(ValidationUtils.checkIds(rawGridElements.getSwitches())); - exceptions.addAll(ValidationUtils.checkIds(rawGridElements.getMeasurementUnits())); + List> exceptions = new ArrayList<>(); + exceptions.addAll(checkForDuplicate(rawGridElements.getNodes(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(rawGridElements.getLines(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(rawGridElements.getTransformer2Ws(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(rawGridElements.getTransformer3Ws(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(rawGridElements.getSwitches(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(rawGridElements.getMeasurementUnits(), AssetInput::getId)); return exceptions; } @@ -217,19 +200,10 @@ protected static List> checkRawGridTypeIds( return List.of(isNull); } - List> exceptions = new ArrayList<>(); - // sanity check for distinct uuids - Optional exceptionString = - ValidationUtils.checkForDuplicateUuids( - new HashSet<>(systemParticipants.allEntitiesAsList())); - exceptions.add( - Try.ofVoid( - exceptionString.isPresent(), - () -> - new InvalidGridException( - duplicateUuidsString( - systemParticipants.getClass().getSimpleName(), exceptionString)))); + List> exceptions = + new ArrayList<>( + checkForDuplicate(systemParticipants.allEntitiesAsList(), UniqueEntity::getUuid)); exceptions.addAll(checkSystemParticipants(systemParticipants.getBmPlants(), nodes)); exceptions.addAll(checkSystemParticipants(systemParticipants.getChpPlants(), nodes)); @@ -274,23 +248,23 @@ protected static List> checkRawGridTypeIds( * Checks the validity of type ids of every entity. * * @param systemParticipants the system participants - * @return a list of try objects either containing an {@link UnsafeEntityException} or an empty - * Success + * @return a list of try objects either containing an {@link DuplicateEntitiesException} or an + * empty Success */ - protected static List> checkSystemParticipantsTypeIds( + protected static List> checkSystemParticipantsTypeIds( SystemParticipants systemParticipants) { - List> exceptions = new ArrayList<>(); - exceptions.addAll(ValidationUtils.checkIds(systemParticipants.getBmPlants())); - exceptions.addAll(ValidationUtils.checkIds(systemParticipants.getChpPlants())); - exceptions.addAll(ValidationUtils.checkIds(systemParticipants.getEvCS())); - exceptions.addAll(ValidationUtils.checkIds(systemParticipants.getEvs())); - exceptions.addAll(ValidationUtils.checkIds(systemParticipants.getFixedFeedIns())); - exceptions.addAll(ValidationUtils.checkIds(systemParticipants.getHeatPumps())); - exceptions.addAll(ValidationUtils.checkIds(systemParticipants.getLoads())); - exceptions.addAll(ValidationUtils.checkIds(systemParticipants.getPvPlants())); - exceptions.addAll(ValidationUtils.checkIds(systemParticipants.getStorages())); - exceptions.addAll(ValidationUtils.checkIds(systemParticipants.getWecPlants())); - exceptions.addAll(ValidationUtils.checkIds(systemParticipants.getEmSystems())); + List> exceptions = new ArrayList<>(); + exceptions.addAll(checkForDuplicate(systemParticipants.getBmPlants(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(systemParticipants.getChpPlants(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(systemParticipants.getEvCS(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(systemParticipants.getEvs(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(systemParticipants.getFixedFeedIns(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(systemParticipants.getHeatPumps(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(systemParticipants.getLoads(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(systemParticipants.getPvPlants(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(systemParticipants.getStorages(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(systemParticipants.getWecPlants(), AssetInput::getId)); + exceptions.addAll(checkForDuplicate(systemParticipants.getEmSystems(), AssetInput::getId)); return exceptions; } @@ -312,18 +286,10 @@ protected static List> checkSystemParticipantsT return List.of(isNull); } - List> exceptions = new ArrayList<>(); - // sanity check for distinct uuids - Optional exceptionString = - checkForDuplicateUuids(new HashSet<>(graphicElements.allEntitiesAsList())); - exceptions.add( - Try.ofVoid( - exceptionString.isPresent(), - () -> - new InvalidGridException( - duplicateUuidsString( - graphicElements.getClass().getSimpleName(), exceptionString)))); + List> exceptions = + new ArrayList<>( + checkForDuplicate(graphicElements.allEntitiesAsList(), UniqueEntity::getUuid)); graphicElements .getNodeGraphics() diff --git a/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java b/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java index c09607a0d..585e1acd3 100644 --- a/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java +++ b/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java @@ -222,7 +222,10 @@ else if (SystemParticipantTypeInput.class.isAssignableFrom(assetTypeInput.getCla * @param inputs a set of asset inputs * @return a list of try objects either containing an {@link UnsafeEntityException} or an empty * Success + * @deprecated use {@link #checkForDuplicate(Collection, FieldSupplier)} with {@link + * AssetInput#getId()} as {@link FieldSupplier} instead */ + @Deprecated protected static List> checkIds( Set inputs) { List ids = new ArrayList<>(); @@ -359,7 +362,10 @@ public static Predicate distinctByKey(Function keyExtractor * * @param entities the entities that should be checkd for UUID uniqueness * @return either a string wrapped in an optional with duplicate UUIDs or an empty optional + * @deprecated use {@link #checkForDuplicate(Collection, FieldSupplier)} with {@link + * UniqueEntity#getUuid()} as {@link FieldSupplier} instead */ + @Deprecated protected static Optional checkForDuplicateUuids(Set entities) { if (distinctUuids(entities)) { return Optional.empty(); @@ -388,4 +394,46 @@ protected static Optional checkForDuplicateUuids(Set entit return Optional.of(duplicationsString); } + + /** + * Method to check for duplicate fields in a set of {@link UniqueEntity}. + * + * @param entities to be checked + * @param supplier for the field + * @return a list of {@link Try}. + * @param type of the field + * @param type of the {@link UniqueEntity} + */ + protected static + List> checkForDuplicate( + Collection entities, FieldSupplier supplier) { + Map> duplicates = + entities.stream().collect(Collectors.groupingBy(supplier::getField)); + + List> exceptions = new ArrayList<>(); + + duplicates.entrySet().stream() + .filter(e -> e.getValue().size() > 1) + .forEach( + duplicate -> { + A key = duplicate.getKey(); + exceptions.add( + Failure.ofVoid( + new DuplicateEntitiesException( + key.getClass().getSimpleName(), duplicates.get(key)))); + }); + + return exceptions; + } + + /** + * Supplier for unique entity fields. + * + * @param type of field + * @param type of unique entity + */ + @FunctionalInterface + protected interface FieldSupplier { + A getField(B entity); + } } diff --git a/src/test/groovy/edu/ie3/datamodel/utils/validation/ValidationUtilsTest.groovy b/src/test/groovy/edu/ie3/datamodel/utils/validation/ValidationUtilsTest.groovy index 26c43982d..aa2ee5295 100644 --- a/src/test/groovy/edu/ie3/datamodel/utils/validation/ValidationUtilsTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/utils/validation/ValidationUtilsTest.groovy @@ -5,17 +5,12 @@ */ package edu.ie3.datamodel.utils.validation -import static edu.ie3.datamodel.models.StandardUnits.* -import static edu.ie3.datamodel.utils.validation.DummyAssetInput.invalid -import static edu.ie3.datamodel.utils.validation.DummyAssetInput.valid -import static edu.ie3.util.quantities.PowerSystemUnits.OHM_PER_KILOMETRE -import static edu.ie3.util.quantities.PowerSystemUnits.PU - +import edu.ie3.datamodel.exceptions.DuplicateEntitiesException import edu.ie3.datamodel.exceptions.FailedValidationException import edu.ie3.datamodel.exceptions.InvalidEntityException -import edu.ie3.datamodel.exceptions.UnsafeEntityException import edu.ie3.datamodel.exceptions.ValidationException import edu.ie3.datamodel.models.OperationTime +import edu.ie3.datamodel.models.UniqueEntity import edu.ie3.datamodel.models.input.AssetInput import edu.ie3.datamodel.models.input.NodeInput import edu.ie3.datamodel.models.input.OperatorInput @@ -31,6 +26,12 @@ import tech.units.indriya.quantity.Quantities import javax.measure.Quantity +import static edu.ie3.datamodel.models.StandardUnits.* +import static edu.ie3.datamodel.utils.validation.DummyAssetInput.invalid +import static edu.ie3.datamodel.utils.validation.DummyAssetInput.valid +import static edu.ie3.util.quantities.PowerSystemUnits.OHM_PER_KILOMETRE +import static edu.ie3.util.quantities.PowerSystemUnits.PU + class ValidationUtilsTest extends Specification { def "Smoke Test: Correct asset throws no exception"() { @@ -71,9 +72,12 @@ class ValidationUtilsTest extends Specification { } def "The validation utils should check for duplicates as expected"() { - expect: - ValidationUtils.checkForDuplicateUuids(collection) == checkResult + ValidationUtils.checkForDuplicate(collection, UniqueEntity::getUuid).every { + it.exception.map { + it.message + } == checkResult + } where: collection || checkResult @@ -96,8 +100,7 @@ class ValidationUtilsTest extends Specification { null, GermanVoltageLevelUtils.LV, 6) - ] as Set || Optional.of("9e37ce48-9650-44ec-b888-c2fd182aff01: 2\n" + - " - NodeInput{uuid=9e37ce48-9650-44ec-b888-c2fd182aff01, id='node_f', operator=f15105c4-a2de-4ab8-a621-4bc98e372d92, operationTime=OperationTime{startDate=null, endDate=null, isLimited=false}, vTarget=1 p.u., slack=false, geoPosition=null, voltLvl=CommonVoltageLevel{id='Niederspannung', nominalVoltage=0.4 kV, synonymousIds=[Niederspannung, lv, ns], voltageRange=Interval [0.0 kV, 10 kV)}, subnet=6}\n" + + ] as Set || Optional.of("The following entities have duplicate 'UUID':NodeInput{uuid=9e37ce48-9650-44ec-b888-c2fd182aff01, id='node_f', operator=f15105c4-a2de-4ab8-a621-4bc98e372d92, operationTime=OperationTime{startDate=null, endDate=null, isLimited=false}, vTarget=1 p.u., slack=false, geoPosition=null, voltLvl=CommonVoltageLevel{id='Niederspannung', nominalVoltage=0.4 kV, synonymousIds=[Niederspannung, lv, ns], voltageRange=Interval [0.0 kV, 10 kV)}, subnet=6}\n" + " - NodeInput{uuid=9e37ce48-9650-44ec-b888-c2fd182aff01, id='node_g', operator=f15105c4-a2de-4ab8-a621-4bc98e372d92, operationTime=OperationTime{startDate=null, endDate=null, isLimited=false}, vTarget=1 p.u., slack=false, geoPosition=null, voltLvl=CommonVoltageLevel{id='Niederspannung', nominalVoltage=0.4 kV, synonymousIds=[Niederspannung, lv, ns], voltageRange=Interval [0.0 kV, 10 kV)}, subnet=6}") [ GridTestData.nodeD, @@ -133,32 +136,32 @@ class ValidationUtilsTest extends Specification { GridTestData.nodeA.copy().id(null).build() || new InvalidEntityException("No ID assigned", invalidAsset) GridTestData.nodeA.copy().operationTime(null).build() || new InvalidEntityException("Operation time of the asset is not defined", invalidAsset) GridTestData.nodeA.copy().operationTime(OperationTime.builder(). - withStart(TimeUtil.withDefaults.toZonedDateTime("2020-03-26 15:11:31")). - withEnd(TimeUtil.withDefaults.toZonedDateTime("2020-03-25 15:11:31")).build()).build() || new InvalidEntityException("Operation start time of the asset has to be before end time", invalidAsset) + withStart(TimeUtil.withDefaults.toZonedDateTime("2020-03-26 15:11:31")). + withEnd(TimeUtil.withDefaults.toZonedDateTime("2020-03-25 15:11:31")).build()).build() || new InvalidEntityException("Operation start time of the asset has to be before end time", invalidAsset) } def "The check for negative entities should work as expected"() { given: def asset = new LineTypeInput( - UUID.fromString("3bed3eb3-9790-4874-89b5-a5434d408088"), - "lineType_AtoB", - Quantities.getQuantity(0d, SUSCEPTANCE_PER_LENGTH), - Quantities.getQuantity(0d, CONDUCTANCE_PER_LENGTH), - Quantities.getQuantity(0.437d, OHM_PER_KILOMETRE), - Quantities.getQuantity(0.356d, OHM_PER_KILOMETRE), - Quantities.getQuantity(300d, ELECTRIC_CURRENT_MAGNITUDE), - Quantities.getQuantity(20d, RATED_VOLTAGE_MAGNITUDE) - ) + UUID.fromString("3bed3eb3-9790-4874-89b5-a5434d408088"), + "lineType_AtoB", + Quantities.getQuantity(0d, SUSCEPTANCE_PER_LENGTH), + Quantities.getQuantity(0d, CONDUCTANCE_PER_LENGTH), + Quantities.getQuantity(0.437d, OHM_PER_KILOMETRE), + Quantities.getQuantity(0.356d, OHM_PER_KILOMETRE), + Quantities.getQuantity(300d, ELECTRIC_CURRENT_MAGNITUDE), + Quantities.getQuantity(20d, RATED_VOLTAGE_MAGNITUDE) + ) def invalidAsset = new LineTypeInput( - UUID.fromString("3bed3eb3-9790-4874-89b5-a5434d408088"), - "lineType_AtoB", - Quantities.getQuantity(-1d, SUSCEPTANCE_PER_LENGTH), // invalid value - Quantities.getQuantity(0d, CONDUCTANCE_PER_LENGTH), - Quantities.getQuantity(0.437d, OHM_PER_KILOMETRE), - Quantities.getQuantity(0.356d, OHM_PER_KILOMETRE), - Quantities.getQuantity(300d, ELECTRIC_CURRENT_MAGNITUDE), - Quantities.getQuantity(20d, RATED_VOLTAGE_MAGNITUDE) - ) + UUID.fromString("3bed3eb3-9790-4874-89b5-a5434d408088"), + "lineType_AtoB", + Quantities.getQuantity(-1d, SUSCEPTANCE_PER_LENGTH), // invalid value + Quantities.getQuantity(0d, CONDUCTANCE_PER_LENGTH), + Quantities.getQuantity(0.437d, OHM_PER_KILOMETRE), + Quantities.getQuantity(0.356d, OHM_PER_KILOMETRE), + Quantities.getQuantity(300d, ELECTRIC_CURRENT_MAGNITUDE), + Quantities.getQuantity(20d, RATED_VOLTAGE_MAGNITUDE) + ) when: ValidationUtils.detectNegativeQuantities([asset.getB()] as Quantity[], asset) @@ -177,25 +180,25 @@ class ValidationUtilsTest extends Specification { def "The check for zero or negative entities should work as expected"() { given: def asset = new LineTypeInput( - UUID.fromString("3bed3eb3-9790-4874-89b5-a5434d408088"), - "lineType_AtoB", - Quantities.getQuantity(1d, SUSCEPTANCE_PER_LENGTH), - Quantities.getQuantity(0d, CONDUCTANCE_PER_LENGTH), - Quantities.getQuantity(0.437d, OHM_PER_KILOMETRE), - Quantities.getQuantity(0.356d, OHM_PER_KILOMETRE), - Quantities.getQuantity(300d, ELECTRIC_CURRENT_MAGNITUDE), - Quantities.getQuantity(20d, RATED_VOLTAGE_MAGNITUDE) - ) + UUID.fromString("3bed3eb3-9790-4874-89b5-a5434d408088"), + "lineType_AtoB", + Quantities.getQuantity(1d, SUSCEPTANCE_PER_LENGTH), + Quantities.getQuantity(0d, CONDUCTANCE_PER_LENGTH), + Quantities.getQuantity(0.437d, OHM_PER_KILOMETRE), + Quantities.getQuantity(0.356d, OHM_PER_KILOMETRE), + Quantities.getQuantity(300d, ELECTRIC_CURRENT_MAGNITUDE), + Quantities.getQuantity(20d, RATED_VOLTAGE_MAGNITUDE) + ) def invalidAsset = new LineTypeInput( - UUID.fromString("3bed3eb3-9790-4874-89b5-a5434d408088"), - "lineType_AtoB", - Quantities.getQuantity(0d, SUSCEPTANCE_PER_LENGTH), // invalid value - Quantities.getQuantity(0d, CONDUCTANCE_PER_LENGTH), - Quantities.getQuantity(0.437d, OHM_PER_KILOMETRE), - Quantities.getQuantity(0.356d, OHM_PER_KILOMETRE), - Quantities.getQuantity(300d, ELECTRIC_CURRENT_MAGNITUDE), - Quantities.getQuantity(20d, RATED_VOLTAGE_MAGNITUDE) - ) + UUID.fromString("3bed3eb3-9790-4874-89b5-a5434d408088"), + "lineType_AtoB", + Quantities.getQuantity(0d, SUSCEPTANCE_PER_LENGTH), // invalid value + Quantities.getQuantity(0d, CONDUCTANCE_PER_LENGTH), + Quantities.getQuantity(0.437d, OHM_PER_KILOMETRE), + Quantities.getQuantity(0.356d, OHM_PER_KILOMETRE), + Quantities.getQuantity(300d, ELECTRIC_CURRENT_MAGNITUDE), + Quantities.getQuantity(20d, RATED_VOLTAGE_MAGNITUDE) + ) when: ValidationUtils.detectZeroOrNegativeQuantities([asset.getB()] as Quantity[], asset) @@ -259,10 +262,12 @@ class ValidationUtilsTest extends Specification { ] when: - List> exceptions = ValidationUtils.checkIds(validAssetIds) + List> exceptions = ValidationUtils.checkForDuplicate(validAssetIds, AssetInput::getId) then: - exceptions.every { ex -> ex.success } + exceptions.every { + ex -> ex.success + } } def "Duplicate asset input ids leads to an exception"() { @@ -273,11 +278,11 @@ class ValidationUtilsTest extends Specification { ] when: - List> exceptions = ValidationUtils.checkIds(invalidAssetIds) + List> exceptions = ValidationUtils.checkForDuplicate(invalidAssetIds, AssetInput::getId) then: exceptions.size() == 1 exceptions.get(0).failure - exceptions.get(0).exception.get().message.contains("Entity may be unsafe because of: There is already an entity with the id invalid_asset") + exceptions.get(0).exception.get().message.contains("The following entities have duplicate 'String':AssetInput") } } From 0a8cc774ad5dfadfebaecf815c4e5d44911265c5 Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Mon, 20 Nov 2023 13:54:11 +0100 Subject: [PATCH 2/5] Adding information to `CHANGELOG`. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1a341398..635605ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed ### Changed +- Abstracting some methods in `ValidationUtils` [#852](https://github.com/ie3-institute/PowerSystemDataModel/issues/852) ## [4.1.0] - 2023-11-02 From 2104312d1bd15551f57321d15eb512b5e676cc27 Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Mon, 20 Nov 2023 15:15:27 +0100 Subject: [PATCH 3/5] Removing deprecated methods. --- .../GridContainerValidationUtils.java | 9 --- .../utils/validation/ValidationUtils.java | 70 ------------------- 2 files changed, 79 deletions(-) diff --git a/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java b/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java index 27fd23d61..8ff8bc9cd 100644 --- a/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java +++ b/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java @@ -22,15 +22,6 @@ public class GridContainerValidationUtils extends ValidationUtils { - @Deprecated - private static String duplicateUuidsString(String simpleName, Optional exceptionString) { - return "The provided entities in '" - + simpleName - + "' contains duplicate UUIDs. " - + "This is not allowed!\nDuplicated uuids:\n\n" - + exceptionString; - } - /** Private Constructor as this class is not meant to be instantiated */ private GridContainerValidationUtils() { throw new IllegalStateException("Don't try and instantiate a Utility class."); diff --git a/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java b/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java index 585e1acd3..affc0bb69 100644 --- a/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java +++ b/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java @@ -216,37 +216,6 @@ else if (SystemParticipantTypeInput.class.isAssignableFrom(assetTypeInput.getCla return exceptions; } - /** - * Checks the validity of the ids for a given set of {@link AssetInput}. - * - * @param inputs a set of asset inputs - * @return a list of try objects either containing an {@link UnsafeEntityException} or an empty - * Success - * @deprecated use {@link #checkForDuplicate(Collection, FieldSupplier)} with {@link - * AssetInput#getId()} as {@link FieldSupplier} instead - */ - @Deprecated - protected static List> checkIds( - Set inputs) { - List ids = new ArrayList<>(); - List> exceptions = new ArrayList<>(); - - inputs.forEach( - input -> { - String id = input.getId(); - if (!ids.contains(id)) { - ids.add(id); - } else { - exceptions.add( - new Failure<>( - new UnsafeEntityException( - "There is already an entity with the id " + id, input))); - } - }); - - return exceptions; - } - /** * Checks, if the given object is null. If so, an {@link InvalidEntityException} wrapped in a * {@link Failure} is returned. @@ -356,45 +325,6 @@ public static Predicate distinctByKey(Function keyExtractor return t -> seen.add(keyExtractor.apply(t)); } - /** - * Checks if the provided set of unique entities only contains elements with distinct UUIDs and - * either returns a string with duplicated UUIDs or an empty optional otherwise. - * - * @param entities the entities that should be checkd for UUID uniqueness - * @return either a string wrapped in an optional with duplicate UUIDs or an empty optional - * @deprecated use {@link #checkForDuplicate(Collection, FieldSupplier)} with {@link - * UniqueEntity#getUuid()} as {@link FieldSupplier} instead - */ - @Deprecated - protected static Optional checkForDuplicateUuids(Set entities) { - if (distinctUuids(entities)) { - return Optional.empty(); - } - String duplicationsString = - entities.stream() - .collect(Collectors.groupingBy(UniqueEntity::getUuid, Collectors.counting())) - .entrySet() - .stream() - .filter(entry -> entry.getValue() > 1) - .map( - entry -> { - String duplicateEntitiesString = - entities.stream() - .filter(entity -> entity.getUuid().equals(entry.getKey())) - .map(UniqueEntity::toString) - .collect(Collectors.joining("\n - ")); - - return entry.getKey() - + ": " - + entry.getValue() - + "\n - " - + duplicateEntitiesString; - }) - .collect(Collectors.joining("\n\n")); - - return Optional.of(duplicationsString); - } - /** * Method to check for duplicate fields in a set of {@link UniqueEntity}. * From 26194f10b0f1164f9edd04158a7a1c7ceb4dcaf4 Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Mon, 27 Nov 2023 13:11:28 +0100 Subject: [PATCH 4/5] Implementing requested changes. --- .../DuplicateEntitiesException.java | 2 +- .../ie3/datamodel/utils/ExceptionUtils.java | 4 +- .../GridContainerValidationUtils.java | 42 +++++++------- .../utils/validation/ValidationUtils.java | 57 +++++++------------ .../validation/ValidationUtilsTest.groovy | 43 ++++---------- 5 files changed, 54 insertions(+), 94 deletions(-) diff --git a/src/main/java/edu/ie3/datamodel/exceptions/DuplicateEntitiesException.java b/src/main/java/edu/ie3/datamodel/exceptions/DuplicateEntitiesException.java index abf5f1d92..c8c2ac7da 100644 --- a/src/main/java/edu/ie3/datamodel/exceptions/DuplicateEntitiesException.java +++ b/src/main/java/edu/ie3/datamodel/exceptions/DuplicateEntitiesException.java @@ -21,7 +21,7 @@ protected DuplicateEntitiesException(String s, String entities) { public DuplicateEntitiesException(String fieldName, Collection entities) { this( - "The following entities have duplicate '" + fieldName + "':", + "The following entities have duplicate '" + fieldName + "': ", ExceptionUtils.combine(entities)); } } diff --git a/src/main/java/edu/ie3/datamodel/utils/ExceptionUtils.java b/src/main/java/edu/ie3/datamodel/utils/ExceptionUtils.java index 1569adf0e..ebbaa61f8 100644 --- a/src/main/java/edu/ie3/datamodel/utils/ExceptionUtils.java +++ b/src/main/java/edu/ie3/datamodel/utils/ExceptionUtils.java @@ -35,6 +35,8 @@ public static String getMessages(List exceptions) { * @return a string */ public static String combine(Collection entities) { - return entities.stream().map(UniqueEntity::toString).collect(Collectors.joining("\n - ")); + return "{" + + entities.stream().map(UniqueEntity::toString).collect(Collectors.joining(", ")) + + "}"; } } diff --git a/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java b/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java index 8ff8bc9cd..77388c6d4 100644 --- a/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java +++ b/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java @@ -45,7 +45,7 @@ private GridContainerValidationUtils() { /* sanity check to ensure distinct UUIDs */ List> exceptions = new ArrayList<>( - checkForDuplicate(gridContainer.allEntitiesAsList(), UniqueEntity::getUuid)); + checkForDuplicates(gridContainer.allEntitiesAsList(), UniqueEntity::getUuid)); exceptions.addAll(checkRawGridElements(gridContainer.getRawGrid())); exceptions.addAll( @@ -83,7 +83,7 @@ private GridContainerValidationUtils() { /* sanity check to ensure distinct UUIDs */ List> exceptions = new ArrayList<>( - checkForDuplicate(rawGridElements.allEntitiesAsList(), UniqueEntity::getUuid)); + checkForDuplicates(rawGridElements.allEntitiesAsList(), UniqueEntity::getUuid)); /* Checking nodes */ Set nodes = rawGridElements.getNodes(); @@ -163,12 +163,12 @@ private GridContainerValidationUtils() { protected static List> checkRawGridTypeIds( RawGridElements rawGridElements) { List> exceptions = new ArrayList<>(); - exceptions.addAll(checkForDuplicate(rawGridElements.getNodes(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(rawGridElements.getLines(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(rawGridElements.getTransformer2Ws(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(rawGridElements.getTransformer3Ws(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(rawGridElements.getSwitches(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(rawGridElements.getMeasurementUnits(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(rawGridElements.getNodes(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(rawGridElements.getLines(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(rawGridElements.getTransformer2Ws(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(rawGridElements.getTransformer3Ws(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(rawGridElements.getSwitches(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(rawGridElements.getMeasurementUnits(), AssetInput::getId)); return exceptions; } @@ -194,7 +194,7 @@ protected static List> checkRawGridTypeIds // sanity check for distinct uuids List> exceptions = new ArrayList<>( - checkForDuplicate(systemParticipants.allEntitiesAsList(), UniqueEntity::getUuid)); + checkForDuplicates(systemParticipants.allEntitiesAsList(), UniqueEntity::getUuid)); exceptions.addAll(checkSystemParticipants(systemParticipants.getBmPlants(), nodes)); exceptions.addAll(checkSystemParticipants(systemParticipants.getChpPlants(), nodes)); @@ -245,17 +245,17 @@ protected static List> checkRawGridTypeIds protected static List> checkSystemParticipantsTypeIds( SystemParticipants systemParticipants) { List> exceptions = new ArrayList<>(); - exceptions.addAll(checkForDuplicate(systemParticipants.getBmPlants(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(systemParticipants.getChpPlants(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(systemParticipants.getEvCS(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(systemParticipants.getEvs(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(systemParticipants.getFixedFeedIns(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(systemParticipants.getHeatPumps(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(systemParticipants.getLoads(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(systemParticipants.getPvPlants(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(systemParticipants.getStorages(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(systemParticipants.getWecPlants(), AssetInput::getId)); - exceptions.addAll(checkForDuplicate(systemParticipants.getEmSystems(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(systemParticipants.getBmPlants(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(systemParticipants.getChpPlants(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(systemParticipants.getEvCS(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(systemParticipants.getEvs(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(systemParticipants.getFixedFeedIns(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(systemParticipants.getHeatPumps(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(systemParticipants.getLoads(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(systemParticipants.getPvPlants(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(systemParticipants.getStorages(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(systemParticipants.getWecPlants(), AssetInput::getId)); + exceptions.addAll(checkForDuplicates(systemParticipants.getEmSystems(), AssetInput::getId)); return exceptions; } @@ -280,7 +280,7 @@ protected static List> checkSystemParticip // sanity check for distinct uuids List> exceptions = new ArrayList<>( - checkForDuplicate(graphicElements.allEntitiesAsList(), UniqueEntity::getUuid)); + checkForDuplicates(graphicElements.allEntitiesAsList(), UniqueEntity::getUuid)); graphicElements .getNodeGraphics() diff --git a/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java b/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java index affc0bb69..035075adc 100644 --- a/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java +++ b/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java @@ -299,20 +299,6 @@ protected static void detectMalformedQuantities( } } - /** - * Determines if the provided set only contains elements with distinct UUIDs - * - * @param entities the set that should be checked - * @return true if all UUIDs of the provided entities are unique, false otherwise - */ - private static boolean distinctUuids(Set entities) { - return entities.stream() - .filter(distinctByKey(UniqueEntity::getUuid)) - .collect(Collectors.toSet()) - .size() - == entities.size(); - } - /** * Predicate that can be used to filter elements based on a given Function * @@ -331,39 +317,34 @@ public static Predicate distinctByKey(Function keyExtractor * @param entities to be checked * @param supplier for the field * @return a list of {@link Try}. - * @param type of the field - * @param type of the {@link UniqueEntity} + * @param type of the {@link UniqueEntity} + * @param type of the field */ - protected static - List> checkForDuplicate( - Collection entities, FieldSupplier supplier) { - Map> duplicates = + protected static + List> checkForDuplicates( + Collection entities, FieldSupplier supplier) { + Map> duplicates = entities.stream().collect(Collectors.groupingBy(supplier::getField)); - List> exceptions = new ArrayList<>(); - - duplicates.entrySet().stream() + return duplicates.entrySet().stream() .filter(e -> e.getValue().size() > 1) - .forEach( - duplicate -> { - A key = duplicate.getKey(); - exceptions.add( - Failure.ofVoid( - new DuplicateEntitiesException( - key.getClass().getSimpleName(), duplicates.get(key)))); - }); - - return exceptions; + .map( + duplicate -> + (Try) + Failure.ofVoid( + new DuplicateEntitiesException( + duplicate.getKey().getClass().getSimpleName(), duplicate.getValue()))) + .toList(); } /** - * Supplier for unique entity fields. + * Supplier for unique entity fields that returns a field of type F given an entity of type E. * - * @param type of field - * @param type of unique entity + * @param type of unique entity + * @param type of field */ @FunctionalInterface - protected interface FieldSupplier { - A getField(B entity); + protected interface FieldSupplier { + F getField(E entity); } } diff --git a/src/test/groovy/edu/ie3/datamodel/utils/validation/ValidationUtilsTest.groovy b/src/test/groovy/edu/ie3/datamodel/utils/validation/ValidationUtilsTest.groovy index aa2ee5295..d6971bcb0 100644 --- a/src/test/groovy/edu/ie3/datamodel/utils/validation/ValidationUtilsTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/utils/validation/ValidationUtilsTest.groovy @@ -45,36 +45,12 @@ class ValidationUtilsTest extends Specification { noExceptionThrown() } - def "The validation utils should determine if a collection with UniqueEntity's is distinct by their uuid"() { - - expect: - ValidationUtils.distinctUuids(collection) == distinct - - where: - collection || distinct - [ - GridTestData.nodeF, - new NodeInput( - UUID.fromString("9e37ce48-9650-44ec-b888-c2fd182aff01"), "node_g", OperatorInput.NO_OPERATOR_ASSIGNED, - OperationTime.notLimited() - , - Quantities.getQuantity(1d, PU), - false, - null, - GermanVoltageLevelUtils.LV, - 6) - ] as Set || false - [ - GridTestData.nodeD, - GridTestData.nodeE - ] as Set || true - [] as Set || true - } - def "The validation utils should check for duplicates as expected"() { expect: - ValidationUtils.checkForDuplicate(collection, UniqueEntity::getUuid).every { - it.exception.map { + def tries = ValidationUtils.checkForDuplicates(collection, UniqueEntity::getUuid) + + if (!tries.isEmpty()) { + tries.get(0).exception.map { it.message } == checkResult } @@ -100,8 +76,9 @@ class ValidationUtilsTest extends Specification { null, GermanVoltageLevelUtils.LV, 6) - ] as Set || Optional.of("The following entities have duplicate 'UUID':NodeInput{uuid=9e37ce48-9650-44ec-b888-c2fd182aff01, id='node_f', operator=f15105c4-a2de-4ab8-a621-4bc98e372d92, operationTime=OperationTime{startDate=null, endDate=null, isLimited=false}, vTarget=1 p.u., slack=false, geoPosition=null, voltLvl=CommonVoltageLevel{id='Niederspannung', nominalVoltage=0.4 kV, synonymousIds=[Niederspannung, lv, ns], voltageRange=Interval [0.0 kV, 10 kV)}, subnet=6}\n" + - " - NodeInput{uuid=9e37ce48-9650-44ec-b888-c2fd182aff01, id='node_g', operator=f15105c4-a2de-4ab8-a621-4bc98e372d92, operationTime=OperationTime{startDate=null, endDate=null, isLimited=false}, vTarget=1 p.u., slack=false, geoPosition=null, voltLvl=CommonVoltageLevel{id='Niederspannung', nominalVoltage=0.4 kV, synonymousIds=[Niederspannung, lv, ns], voltageRange=Interval [0.0 kV, 10 kV)}, subnet=6}") + ] as Set || Optional.of("The following entities have duplicate 'UUID': " + + "{NodeInput{uuid=9e37ce48-9650-44ec-b888-c2fd182aff01, id='node_f', operator=f15105c4-a2de-4ab8-a621-4bc98e372d92, operationTime=OperationTime{startDate=null, endDate=null, isLimited=false}, vTarget=1 p.u., slack=false, geoPosition=null, voltLvl=CommonVoltageLevel{id='Niederspannung', nominalVoltage=0.4 kV, synonymousIds=[Niederspannung, lv, ns], voltageRange=Interval [0.0 kV, 10 kV)}, subnet=6}, " + + "NodeInput{uuid=9e37ce48-9650-44ec-b888-c2fd182aff01, id='node_g', operator=f15105c4-a2de-4ab8-a621-4bc98e372d92, operationTime=OperationTime{startDate=null, endDate=null, isLimited=false}, vTarget=1 p.u., slack=false, geoPosition=null, voltLvl=CommonVoltageLevel{id='Niederspannung', nominalVoltage=0.4 kV, synonymousIds=[Niederspannung, lv, ns], voltageRange=Interval [0.0 kV, 10 kV)}, subnet=6}}") [ GridTestData.nodeD, GridTestData.nodeE @@ -262,7 +239,7 @@ class ValidationUtilsTest extends Specification { ] when: - List> exceptions = ValidationUtils.checkForDuplicate(validAssetIds, AssetInput::getId) + List> exceptions = ValidationUtils.checkForDuplicates(validAssetIds, AssetInput::getId) then: exceptions.every { @@ -278,11 +255,11 @@ class ValidationUtilsTest extends Specification { ] when: - List> exceptions = ValidationUtils.checkForDuplicate(invalidAssetIds, AssetInput::getId) + List> exceptions = ValidationUtils.checkForDuplicates(invalidAssetIds, AssetInput::getId) then: exceptions.size() == 1 exceptions.get(0).failure - exceptions.get(0).exception.get().message.contains("The following entities have duplicate 'String':AssetInput") + exceptions.get(0).exception.get().message.startsWith("The following entities have duplicate 'String': {AssetInput{uuid=") } } From 4bcf4c9df56ffa01f658b356448dd0287b25ea31 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 28 Nov 2023 16:23:47 +0100 Subject: [PATCH 5/5] Works without casting with collect(Collectors.toList()) --- .../ie3/datamodel/utils/validation/ValidationUtils.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java b/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java index 035075adc..9cdd9add2 100644 --- a/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java +++ b/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java @@ -330,11 +330,10 @@ List> checkForDuplicates( .filter(e -> e.getValue().size() > 1) .map( duplicate -> - (Try) - Failure.ofVoid( - new DuplicateEntitiesException( - duplicate.getKey().getClass().getSimpleName(), duplicate.getValue()))) - .toList(); + Failure.ofVoid( + new DuplicateEntitiesException( + duplicate.getKey().getClass().getSimpleName(), duplicate.getValue()))) + .collect(Collectors.toList()); } /**