From 742bbd294a0daf8b2441febea8b1c500ef91893b Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Thu, 22 Aug 2024 16:19:16 +0200 Subject: [PATCH 1/9] Allow alternative serde for extensions Signed-off-by: Olivier Perrin --- .../AbstractExtensionSerDeAlternative.java | 38 ++++++++++++ .../ExtensionProviderAlternative.java | 22 +++++++ .../extensions/ExtensionProviders.java | 62 +++++++++++++++---- .../extensions/ExtensionProvidersOptions.java | 17 +++++ .../extensions/ExtensionSerDeAlternative.java | 14 +++++ .../com/powsybl/iidm/serde/ExportOptions.java | 19 +++++- .../com/powsybl/iidm/serde/NetworkSerDe.java | 6 +- 7 files changed, 160 insertions(+), 18 deletions(-) create mode 100644 commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java create mode 100644 commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviderAlternative.java create mode 100644 commons/src/main/java/com/powsybl/commons/extensions/ExtensionProvidersOptions.java create mode 100644 commons/src/main/java/com/powsybl/commons/extensions/ExtensionSerDeAlternative.java diff --git a/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java b/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java new file mode 100644 index 00000000000..1662a09e630 --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.extensions; + +import javax.annotation.Nonnull; +import java.util.Objects; + +/** + * @author Olivier Perrin {@literal } + */ +public abstract class AbstractExtensionSerDeAlternative> implements ExtensionSerDeAlternative { + + private final String alternativeExtensionName; + private final String categoryName; + + protected AbstractExtensionSerDeAlternative(String alternativeExtensionName, String categoryName) { + this.alternativeExtensionName = Objects.requireNonNull(alternativeExtensionName); + this.categoryName = Objects.requireNonNull(categoryName); + } + + @Override + public String getCategoryName() { + return categoryName; + } + + @Override + public String getExtensionName() { + return alternativeExtensionName; + } + + @Override + public abstract @Nonnull ExtensionSerDe getProvider(); +} diff --git a/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviderAlternative.java b/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviderAlternative.java new file mode 100644 index 00000000000..103385dba2b --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviderAlternative.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.extensions; + +import javax.annotation.Nonnull; + +/** + * @author Olivier Perrin {@literal } + */ +public interface ExtensionProviderAlternative

{ + String getCategoryName(); + + String getExtensionName(); + + @Nonnull + P getProvider(); +} diff --git a/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviders.java b/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviders.java index faeda78c612..88f4ca241a5 100644 --- a/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviders.java +++ b/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviders.java @@ -12,6 +12,7 @@ import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Mathieu Bague {@literal } @@ -19,6 +20,7 @@ public final class ExtensionProviders { private final Map providers; + private final Map> providerAlternatives; public static ExtensionProviders createProvider(Class clazz) { return new ExtensionProviders<>(clazz); @@ -33,9 +35,7 @@ public static ExtensionProviders createProvider } private ExtensionProviders(Class clazz) { - Objects.requireNonNull(clazz); - providers = new ServiceLoaderCache<>(clazz).getServices().stream() - .collect(Collectors.toMap(T::getExtensionName, e -> e)); + this(clazz, null, null); } private ExtensionProviders(Class clazz, String categoryName) { @@ -44,29 +44,65 @@ private ExtensionProviders(Class clazz, String categoryName) { private ExtensionProviders(Class clazz, String categoryName, Set extensionNames) { Objects.requireNonNull(clazz); - Objects.requireNonNull(categoryName); - List services = new ServiceLoaderCache<>(clazz).getServices(); - providers = services.stream() - .filter(s -> s.getCategoryName().equals(categoryName) && (extensionNames == null || extensionNames.contains(s.getExtensionName()))) - .collect(Collectors.toMap(T::getExtensionName, e -> e)); + Stream providersStream = new ServiceLoaderCache<>(clazz).getServices().stream(); + if (categoryName != null) { + providersStream = providersStream.filter(s -> s.getCategoryName().equals(categoryName) && + (extensionNames == null || extensionNames.contains(s.getExtensionName()))); + } + providers = providersStream.collect(Collectors.toMap(T::getExtensionName, e -> e)); + + Class alternativeClass = getExtensionProviderAlternativeClass(clazz); + if (alternativeClass != null) { + Stream providerAlternativeStream = new ServiceLoaderCache<>(alternativeClass).getServices().stream(); + if (categoryName != null) { + providerAlternativeStream = providerAlternativeStream.filter(s -> s.getCategoryName().equals(categoryName) && + (extensionNames == null || extensionNames.contains(s.getExtensionName()))); + } + providerAlternatives = providerAlternativeStream + .collect(Collectors.toMap(ExtensionProviderAlternative::getExtensionName, e -> e)); + } else { + providerAlternatives = Collections.emptyMap(); + } + } + + private Class getExtensionProviderAlternativeClass(Class clazz) { + if (clazz.equals(ExtensionSerDe.class)) { + return ExtensionSerDeAlternative.class; + } + return null; } public T findProvider(String name) { + return findProvider(name, null); + } + + public T findProvider(String name, ExtensionProvidersOptions options) { + if (options != null && options.useAlternativeVersion(name)) { + ExtensionProviderAlternative alternative = providerAlternatives.get(name); + if (alternative != null) { + return alternative.getProvider(); + } + } return providers.get(name); } public T findProviderOrThrowException(String name) { - T serializer = findProvider(name); - if (serializer == null) { + return findProviderOrThrowException(name, null); + } + + public T findProviderOrThrowException(String name, ExtensionProvidersOptions options) { + T provider = findProvider(name, options); + if (provider == null) { throw new PowsyblException("Provider not found for extension " + name); } - - return serializer; + return provider; } public Collection getProviders() { - return providers.values(); + return providers.keySet().stream() + .map(this::findProvider) + .toList(); } public void addExtensions(Extendable extendable, Collection> extensions) { diff --git a/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProvidersOptions.java b/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProvidersOptions.java new file mode 100644 index 00000000000..7e9bb72aeee --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProvidersOptions.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.extensions; + +/** + * @author Olivier Perrin {@literal } + */ +public interface ExtensionProvidersOptions { + + boolean useAlternativeVersion(String extensionName); + +} diff --git a/commons/src/main/java/com/powsybl/commons/extensions/ExtensionSerDeAlternative.java b/commons/src/main/java/com/powsybl/commons/extensions/ExtensionSerDeAlternative.java new file mode 100644 index 00000000000..644585048b3 --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/extensions/ExtensionSerDeAlternative.java @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.extensions; + +/** + * @author Olivier Perrin {@literal } + */ +public interface ExtensionSerDeAlternative> extends ExtensionProviderAlternative> { +} diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java index e9b0caaa6ff..9ee80284604 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java @@ -9,6 +9,7 @@ import com.google.common.collect.Sets; import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.extensions.ExtensionProvidersOptions; import com.powsybl.iidm.network.TopologyLevel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,7 +24,7 @@ * @author Geoffroy Jamgotchian {@literal } * @author Miora Ralambotiana {@literal } */ -public class ExportOptions extends AbstractOptions { +public class ExportOptions extends AbstractOptions implements ExtensionProvidersOptions { public enum IidmVersionIncompatibilityBehavior { THROW_EXCEPTION, @@ -32,6 +33,8 @@ public enum IidmVersionIncompatibilityBehavior { private static final Logger LOGGER = LoggerFactory.getLogger(ExportOptions.class); + public static final String ALTERNATIVE_SUFFIX = "alternative-"; + private boolean withBranchSV = true; private boolean indent = true; @@ -225,7 +228,13 @@ public ExportOptions addExtensionVersion(String extensionName, String extensionV * If it has never been added, return an empty optional. */ public Optional getExtensionVersion(String extensionName) { - return Optional.ofNullable(extensionsVersions.get(extensionName)); + return Optional.ofNullable(extensionsVersions.get(extensionName)) + .map(v -> { + if (v.startsWith(ALTERNATIVE_SUFFIX)) { + return v.length() == ALTERNATIVE_SUFFIX.length() ? null : v.substring(ALTERNATIVE_SUFFIX.length()); + } + return v; + }); } public boolean isSorted() { @@ -245,4 +254,10 @@ public ExportOptions setWithAutomationSystems(boolean withAutomationSystems) { this.withAutomationSystems = withAutomationSystems; return this; } + + @Override + public boolean useAlternativeVersion(String extensionName) { + String v = extensionsVersions.get(extensionName); + return v != null && v.startsWith(ALTERNATIVE_SUFFIX); + } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java index c9e243c829e..3b768c8dd9b 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java @@ -158,7 +158,7 @@ private static void writeExtension(Extension> extensio throw new IllegalStateException("Extension Serializer of " + extension.getName() + " should not be null"); } String namespaceUri = getNamespaceUri(extensionSerDe, context.getOptions()); - writer.writeStartNode(namespaceUri, extension.getName()); + writer.writeStartNode(namespaceUri, extensionSerDe.getExtensionName()); context.getExtensionVersion(extension.getName()).ifPresent(extensionSerDe::checkExtensionVersionSupported); extensionSerDe.write(extension, context); writer.writeEndNode(); @@ -167,8 +167,8 @@ private static void writeExtension(Extension> extensio private static ExtensionSerDe getExtensionSerializer(ExportOptions options, Extension> extension) { if (options.withExtension(extension.getName())) { ExtensionSerDe extensionSerDe = options.isThrowExceptionIfExtensionNotFound() - ? EXTENSIONS_SUPPLIER.get().findProviderOrThrowException(extension.getName()) - : EXTENSIONS_SUPPLIER.get().findProvider(extension.getName()); + ? EXTENSIONS_SUPPLIER.get().findProviderOrThrowException(extension.getName(), options) + : EXTENSIONS_SUPPLIER.get().findProvider(extension.getName(), options); if (extensionSerDe == null) { String message = "XmlSerializer for " + extension.getName() + " not found"; throwExceptionIfOption(options, message); From dc48b0ed8dc77091aafc17bcc3314a1517cc8376 Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Wed, 28 Aug 2024 07:38:08 +0200 Subject: [PATCH 2/9] Fix Sonar issue Signed-off-by: Olivier Perrin --- .../commons/extensions/AbstractExtensionSerDeAlternative.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java b/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java index 1662a09e630..bcee5ceadbe 100644 --- a/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java +++ b/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java @@ -7,7 +7,6 @@ */ package com.powsybl.commons.extensions; -import javax.annotation.Nonnull; import java.util.Objects; /** @@ -32,7 +31,4 @@ public String getCategoryName() { public String getExtensionName() { return alternativeExtensionName; } - - @Override - public abstract @Nonnull ExtensionSerDe getProvider(); } From 7795947e7f952552e9be05b29805c97b18fa659b Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Wed, 28 Aug 2024 08:33:57 +0200 Subject: [PATCH 3/9] Small refactoring + unit test Signed-off-by: Olivier Perrin --- .../AbstractExtensionSerDeAlternative.java | 15 ++++-- .../AlternativeExtensionXmlTest.java | 52 +++++++++++++++++++ .../extensions/OperatingStatusXmlTest.java | 6 +-- .../LegacyOperatingStatusSerDe.java | 43 +++++++++++++++ .../OperatingStatusSerDeAlternative.java | 25 +++++++++ .../alternativeOperatingStatusRef.xml | 17 ++++++ .../resources/xsd/legacyOperatingStatus.xsd | 24 +++++++++ 7 files changed, 175 insertions(+), 7 deletions(-) create mode 100644 iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java create mode 100644 iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyOperatingStatusSerDe.java create mode 100644 iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/OperatingStatusSerDeAlternative.java create mode 100644 iidm/iidm-serde/src/test/resources/alternativeOperatingStatusRef.xml create mode 100644 iidm/iidm-serde/src/test/resources/xsd/legacyOperatingStatus.xsd diff --git a/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java b/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java index bcee5ceadbe..a9e4caced5f 100644 --- a/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java +++ b/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java @@ -7,6 +7,7 @@ */ package com.powsybl.commons.extensions; +import javax.annotation.Nonnull; import java.util.Objects; /** @@ -15,20 +16,26 @@ public abstract class AbstractExtensionSerDeAlternative> implements ExtensionSerDeAlternative { private final String alternativeExtensionName; - private final String categoryName; + private final ExtensionSerDe provider; - protected AbstractExtensionSerDeAlternative(String alternativeExtensionName, String categoryName) { + protected AbstractExtensionSerDeAlternative(String alternativeExtensionName, ExtensionSerDe provider) { this.alternativeExtensionName = Objects.requireNonNull(alternativeExtensionName); - this.categoryName = Objects.requireNonNull(categoryName); + this.provider = Objects.requireNonNull(provider); } @Override public String getCategoryName() { - return categoryName; + return provider.getCategoryName(); } @Override public String getExtensionName() { return alternativeExtensionName; } + + @Nonnull + @Override + public ExtensionSerDe getProvider() { + return provider; + } } diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java new file mode 100644 index 00000000000..53d68e816aa --- /dev/null +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde.extensions; + +import com.powsybl.iidm.network.Line; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.extensions.OperatingStatus; +import com.powsybl.iidm.network.impl.extensions.OperatingStatusImpl; +import com.powsybl.iidm.serde.AbstractIidmSerDeTest; +import com.powsybl.iidm.serde.ExportOptions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * @author Olivier Perrin {@literal } + */ +class AlternativeExtensionXmlTest extends AbstractIidmSerDeTest { + + @Test + void test() throws IOException { + Network network = OperatingStatusXmlTest.createTestNetwork(); + + // extend line + Line line = network.getLine("L"); + assertNotNull(line); + OperatingStatus lineOperatingStatus = new OperatingStatusImpl<>(line, + OperatingStatus.Status.PLANNED_OUTAGE); + line.addExtension(OperatingStatus.class, lineOperatingStatus); + + var exportOptions = new ExportOptions().addExtensionVersion(OperatingStatus.NAME, "alternative-"); + Network network2 = allFormatsRoundTripTest(network, "/alternativeOperatingStatusRef.xml", exportOptions); + + Line line2 = network2.getLine("L"); + assertNotNull(line2); + OperatingStatus lineOperatingStatus2 = line2.getExtension(OperatingStatus.class); + assertNotNull(lineOperatingStatus2); + assertEquals(lineOperatingStatus.getStatus(), lineOperatingStatus2.getStatus()); + + lineOperatingStatus2.setStatus(OperatingStatus.Status.IN_OPERATION); + assertEquals(OperatingStatus.Status.IN_OPERATION, lineOperatingStatus2.getStatus()); + } + +} diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/OperatingStatusXmlTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/OperatingStatusXmlTest.java index 9ac57d3b426..195fdc82bb7 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/OperatingStatusXmlTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/OperatingStatusXmlTest.java @@ -24,14 +24,14 @@ */ class OperatingStatusXmlTest extends AbstractIidmSerDeTest { - private static Network createTestNetwork() { + protected static Network createTestNetwork() { Network network = Network.create("test", "test"); network.setCaseDate(ZonedDateTime.parse("2016-06-27T12:27:58.535+02:00")); Substation s = network.newSubstation() .setId("S") .setCountry(Country.FR) .add(); - VoltageLevel vl = s.newVoltageLevel() + s.newVoltageLevel() .setId("VL") .setNominalV(400) .setTopologyKind(TopologyKind.NODE_BREAKER) @@ -40,7 +40,7 @@ private static Network createTestNetwork() { .setId("S2") .setCountry(Country.FR) .add(); - VoltageLevel vl2 = s2.newVoltageLevel() + s2.newVoltageLevel() .setId("VL2") .setNominalV(400) .setTopologyKind(TopologyKind.NODE_BREAKER) diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyOperatingStatusSerDe.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyOperatingStatusSerDe.java new file mode 100644 index 00000000000..8052327c89f --- /dev/null +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyOperatingStatusSerDe.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde.extensions.alternatives; + +import com.google.auto.service.AutoService; +import com.powsybl.commons.extensions.AbstractExtensionSerDe; +import com.powsybl.commons.extensions.ExtensionSerDe; +import com.powsybl.commons.io.DeserializerContext; +import com.powsybl.commons.io.SerializerContext; +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.extensions.OperatingStatus; +import com.powsybl.iidm.network.extensions.OperatingStatusAdder; + +/** + * @author Olivier Perrin {@literal } + */ +@AutoService(ExtensionSerDe.class) +public class LegacyOperatingStatusSerDe> extends AbstractExtensionSerDe> { + + public LegacyOperatingStatusSerDe() { + super("legacyOperatingStatus", "network", OperatingStatus.class, + "legacyOperatingStatus.xsd", "http://www.powsybl.org/schema/iidm/ext/legacy_operating_status/1_0", + "los"); + } + + @Override + public void write(OperatingStatus status, SerializerContext context) { + context.getWriter().writeNodeContent(status.getStatus().name()); + } + + @Override + public OperatingStatus read(I identifiable, DeserializerContext context) { + OperatingStatus.Status status = OperatingStatus.Status.valueOf(context.getReader().readContent()); + OperatingStatusAdder adder = identifiable.newExtension(OperatingStatusAdder.class); + return adder.withStatus(status) + .add(); + } +} diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/OperatingStatusSerDeAlternative.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/OperatingStatusSerDeAlternative.java new file mode 100644 index 00000000000..5724c4d7ab9 --- /dev/null +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/OperatingStatusSerDeAlternative.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde.extensions.alternatives; + +import com.google.auto.service.AutoService; +import com.powsybl.commons.extensions.AbstractExtensionSerDeAlternative; +import com.powsybl.commons.extensions.ExtensionSerDeAlternative; +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.extensions.OperatingStatus; + +/** + * @author Olivier Perrin {@literal } + */ +@AutoService(ExtensionSerDeAlternative.class) +public class OperatingStatusSerDeAlternative> extends AbstractExtensionSerDeAlternative> { + + public OperatingStatusSerDeAlternative() { + super(OperatingStatus.NAME, new LegacyOperatingStatusSerDe<>()); + } +} diff --git a/iidm/iidm-serde/src/test/resources/alternativeOperatingStatusRef.xml b/iidm/iidm-serde/src/test/resources/alternativeOperatingStatusRef.xml new file mode 100644 index 00000000000..fd55f68bf0e --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/alternativeOperatingStatusRef.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + PLANNED_OUTAGE + + \ No newline at end of file diff --git a/iidm/iidm-serde/src/test/resources/xsd/legacyOperatingStatus.xsd b/iidm/iidm-serde/src/test/resources/xsd/legacyOperatingStatus.xsd new file mode 100644 index 00000000000..5f2f0b1b2ec --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/xsd/legacyOperatingStatus.xsd @@ -0,0 +1,24 @@ + + + + + + + + + + + + From 7b925ee265725d4464915388ea414ed44cee1487 Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Wed, 28 Aug 2024 09:17:10 +0200 Subject: [PATCH 4/9] Rename AbstractExtensionSerDeAlternative.alternativeExtensionName to originalExtensionName Signed-off-by: Olivier Perrin --- .../extensions/AbstractExtensionSerDeAlternative.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java b/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java index a9e4caced5f..5b8103c804e 100644 --- a/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java +++ b/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java @@ -15,11 +15,11 @@ */ public abstract class AbstractExtensionSerDeAlternative> implements ExtensionSerDeAlternative { - private final String alternativeExtensionName; + private final String originalExtensionName; private final ExtensionSerDe provider; - protected AbstractExtensionSerDeAlternative(String alternativeExtensionName, ExtensionSerDe provider) { - this.alternativeExtensionName = Objects.requireNonNull(alternativeExtensionName); + protected AbstractExtensionSerDeAlternative(String originalExtensionName, ExtensionSerDe provider) { + this.originalExtensionName = Objects.requireNonNull(originalExtensionName); this.provider = Objects.requireNonNull(provider); } @@ -30,7 +30,7 @@ public String getCategoryName() { @Override public String getExtensionName() { - return alternativeExtensionName; + return originalExtensionName; } @Nonnull From a62f941b329240f10e591886f5d92c75fb889a9c Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Wed, 28 Aug 2024 09:33:50 +0200 Subject: [PATCH 5/9] Simplify 'alternative' version name for non-versioned extensions Signed-off-by: Olivier Perrin --- .../main/java/com/powsybl/iidm/serde/ExportOptions.java | 8 ++++++-- .../serde/extensions/AlternativeExtensionXmlTest.java | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java index 9ee80284604..41ac6720226 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java @@ -33,7 +33,7 @@ public enum IidmVersionIncompatibilityBehavior { private static final Logger LOGGER = LoggerFactory.getLogger(ExportOptions.class); - public static final String ALTERNATIVE_SUFFIX = "alternative-"; + public static final String ALTERNATIVE_SUFFIX = "alternative"; private boolean withBranchSV = true; @@ -231,7 +231,11 @@ public Optional getExtensionVersion(String extensionName) { return Optional.ofNullable(extensionsVersions.get(extensionName)) .map(v -> { if (v.startsWith(ALTERNATIVE_SUFFIX)) { - return v.length() == ALTERNATIVE_SUFFIX.length() ? null : v.substring(ALTERNATIVE_SUFFIX.length()); + // If the version starts with "alternative", 2 cases: + // - the version is exactly "alternative" => no version was specified for the alternative + // - the version has extra-characters, it is striped from its prefix. Note that an additional character + // is also skipped. The version should be specified as "alternative_1.0" or "alternative-1.2" for instance. + return v.length() == ALTERNATIVE_SUFFIX.length() ? null : v.substring(ALTERNATIVE_SUFFIX.length() + 1); } return v; }); diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java index 53d68e816aa..27e84760627 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java @@ -25,6 +25,8 @@ */ class AlternativeExtensionXmlTest extends AbstractIidmSerDeTest { + // This test is the same as OperatingStatusXmlTest::test, but the extension is exported/imported + // using the defined alternative (which is not versioned). @Test void test() throws IOException { Network network = OperatingStatusXmlTest.createTestNetwork(); @@ -36,7 +38,7 @@ void test() throws IOException { OperatingStatus.Status.PLANNED_OUTAGE); line.addExtension(OperatingStatus.class, lineOperatingStatus); - var exportOptions = new ExportOptions().addExtensionVersion(OperatingStatus.NAME, "alternative-"); + var exportOptions = new ExportOptions().addExtensionVersion(OperatingStatus.NAME, "alternative"); Network network2 = allFormatsRoundTripTest(network, "/alternativeOperatingStatusRef.xml", exportOptions); Line line2 = network2.getLine("L"); From 293cf2cc4ef26a85cf36b6681ebca33e6ec0ed4f Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Wed, 28 Aug 2024 09:54:48 +0200 Subject: [PATCH 6/9] Rename unit test Signed-off-by: Olivier Perrin --- .../iidm/serde/extensions/AlternativeExtensionXmlTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java index 27e84760627..2b4feae02d8 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java @@ -28,7 +28,7 @@ class AlternativeExtensionXmlTest extends AbstractIidmSerDeTest { // This test is the same as OperatingStatusXmlTest::test, but the extension is exported/imported // using the defined alternative (which is not versioned). @Test - void test() throws IOException { + void nonVersionedAlternativeTest() throws IOException { Network network = OperatingStatusXmlTest.createTestNetwork(); // extend line From f15dd31a8b83e84e5d04bbed915808306d72a5a3 Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Wed, 28 Aug 2024 13:18:31 +0200 Subject: [PATCH 7/9] Add TU on versioned alternative extension Signed-off-by: Olivier Perrin --- .../AlternativeExtensionXmlTest.java | 50 ++++++++++- .../ActivePowerControlSerDeAlternative.java | 25 ++++++ .../LegacyActivePowerControlSerDe.java | 84 +++++++++++++++++++ .../alternativeActivePowerControlV1_0.xml | 38 +++++++++ .../alternativeActivePowerControlV1_1.xml | 38 +++++++++ .../xsd/legacyActivePowerControlV1_0.xsd | 21 +++++ .../xsd/legacyActivePowerControlV1_1.xsd | 22 +++++ 7 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/ActivePowerControlSerDeAlternative.java create mode 100644 iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyActivePowerControlSerDe.java create mode 100644 iidm/iidm-serde/src/test/resources/V1_0/alternativeActivePowerControlV1_0.xml create mode 100644 iidm/iidm-serde/src/test/resources/V1_0/alternativeActivePowerControlV1_1.xml create mode 100644 iidm/iidm-serde/src/test/resources/xsd/legacyActivePowerControlV1_0.xsd create mode 100644 iidm/iidm-serde/src/test/resources/xsd/legacyActivePowerControlV1_1.xsd diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java index 2b4feae02d8..116b2b35376 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java @@ -7,18 +7,23 @@ */ package com.powsybl.iidm.serde.extensions; +import com.powsybl.iidm.network.Battery; +import com.powsybl.iidm.network.Generator; import com.powsybl.iidm.network.Line; import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.extensions.ActivePowerControl; import com.powsybl.iidm.network.extensions.OperatingStatus; +import com.powsybl.iidm.network.impl.extensions.ActivePowerControlImpl; import com.powsybl.iidm.network.impl.extensions.OperatingStatusImpl; +import com.powsybl.iidm.network.test.BatteryNetworkFactory; import com.powsybl.iidm.serde.AbstractIidmSerDeTest; import com.powsybl.iidm.serde.ExportOptions; +import com.powsybl.iidm.serde.IidmVersion; import org.junit.jupiter.api.Test; import java.io.IOException; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.*; /** * @author Olivier Perrin {@literal } @@ -51,4 +56,45 @@ void nonVersionedAlternativeTest() throws IOException { assertEquals(OperatingStatus.Status.IN_OPERATION, lineOperatingStatus2.getStatus()); } + @Test + void versionedAlternativeWithDefaultVersionTest() throws IOException { + Network network = BatteryNetworkFactory.create(); + + Battery bat = network.getBattery("BAT"); + bat.addExtension(ActivePowerControl.class, new ActivePowerControlImpl<>(bat, true, 4.0, 1.2)); + + Generator generator = network.getGenerator("GEN"); + generator.addExtension(ActivePowerControl.class, new ActivePowerControlImpl<>(generator, false, 3.0, 1.0)); + + // No version is specified for the alternative: it should use the default version (v1.1) + var exportOptions = new ExportOptions().addExtensionVersion(ActivePowerControl.NAME, "alternative"); + Network network2 = allFormatsRoundTripTest(network, "/alternativeActivePowerControlV1_1.xml", IidmVersion.V_1_0, exportOptions); + + Battery bat2 = network2.getBattery("BAT"); + assertNotNull(bat2); + ActivePowerControl activePowerControl2 = bat2.getExtension(ActivePowerControl.class); + assertNotNull(activePowerControl2); + assertEquals(1.2, activePowerControl2.getParticipationFactor(), 0.001d); // default version supports participationFactor + } + + @Test + void versionedAlternativeWithSpecificVersionTest() throws IOException { + Network network = BatteryNetworkFactory.create(); + + Battery bat = network.getBattery("BAT"); + bat.addExtension(ActivePowerControl.class, new ActivePowerControlImpl<>(bat, true, 4.0, 1.2)); + + Generator generator = network.getGenerator("GEN"); + generator.addExtension(ActivePowerControl.class, new ActivePowerControlImpl<>(generator, false, 3.0, 1.0)); + + // Explicitly ask for version 1.0 of the alternative + var exportOptions = new ExportOptions().addExtensionVersion(ActivePowerControl.NAME, "alternative-1.0"); + Network network2 = allFormatsRoundTripTest(network, "/alternativeActivePowerControlV1_0.xml", IidmVersion.V_1_0, exportOptions); + + Battery bat2 = network2.getBattery("BAT"); + assertNotNull(bat2); + ActivePowerControl activePowerControl2 = bat2.getExtension(ActivePowerControl.class); + assertNotNull(activePowerControl2); + assertTrue(Double.isNaN(activePowerControl2.getParticipationFactor())); // version 1.0 does NOT support participationFactor + } } diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/ActivePowerControlSerDeAlternative.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/ActivePowerControlSerDeAlternative.java new file mode 100644 index 00000000000..60621e8ab8e --- /dev/null +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/ActivePowerControlSerDeAlternative.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde.extensions.alternatives; + +import com.google.auto.service.AutoService; +import com.powsybl.commons.extensions.AbstractExtensionSerDeAlternative; +import com.powsybl.commons.extensions.ExtensionSerDeAlternative; +import com.powsybl.iidm.network.Injection; +import com.powsybl.iidm.network.extensions.ActivePowerControl; + +/** + * @author Olivier Perrin {@literal } + */ +@AutoService(ExtensionSerDeAlternative.class) +public class ActivePowerControlSerDeAlternative> extends AbstractExtensionSerDeAlternative> { + + public ActivePowerControlSerDeAlternative() { + super(ActivePowerControl.NAME, new LegacyActivePowerControlSerDe<>()); + } +} diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyActivePowerControlSerDe.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyActivePowerControlSerDe.java new file mode 100644 index 00000000000..267a9ffe8d5 --- /dev/null +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyActivePowerControlSerDe.java @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde.extensions.alternatives; + +import com.google.auto.service.AutoService; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSortedSet; +import com.powsybl.commons.extensions.ExtensionSerDe; +import com.powsybl.commons.io.DeserializerContext; +import com.powsybl.commons.io.SerializerContext; +import com.powsybl.iidm.network.Injection; +import com.powsybl.iidm.network.extensions.ActivePowerControl; +import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; +import com.powsybl.iidm.serde.IidmVersion; +import com.powsybl.iidm.serde.NetworkDeserializerContext; +import com.powsybl.iidm.serde.NetworkSerializerContext; +import com.powsybl.iidm.serde.extensions.AbstractVersionableNetworkExtensionSerDe; + +import java.io.InputStream; +import java.util.List; + +/** + * @author Olivier Perrin {@literal } + */ +@AutoService(ExtensionSerDe.class) +public class LegacyActivePowerControlSerDe> extends AbstractVersionableNetworkExtensionSerDe> { + + public LegacyActivePowerControlSerDe() { + super("legacyActivePowerControl", ActivePowerControl.class, "lapc", + new ImmutableMap.Builder>() + .put(IidmVersion.V_1_0, ImmutableSortedSet.of("1.0", "1.1")) + .build(), + new ImmutableMap.Builder() + .put("1.0", "http://www.itesla_project.eu/schema/iidm/ext/legacy_active_power_control/1_0") + .put("1.1", "http://www.powsybl.org/schema/iidm/ext/legacy_active_power_control/1_1") + .build()); + } + + @Override + public void write(ActivePowerControl activePowerControl, SerializerContext context) { + context.getWriter().writeBooleanAttribute("legacyParticipate", activePowerControl.isParticipate()); + context.getWriter().writeDoubleAttribute("legacyDroop", activePowerControl.getDroop()); + NetworkSerializerContext networkContext = (NetworkSerializerContext) context; + String extVersionStr = networkContext.getExtensionVersion(ActivePowerControl.NAME) + .orElseGet(() -> getVersion(networkContext.getVersion())); + if ("1.1".compareTo(extVersionStr) <= 0) { + context.getWriter().writeDoubleAttribute("legacyParticipationFactor", activePowerControl.getParticipationFactor()); + } + } + + @Override + public InputStream getXsdAsStream() { + return getClass().getResourceAsStream("/xsd/legacyActivePowerControlV1_1.xsd"); + } + + @Override + public List getXsdAsStreamList() { + return List.of(getClass().getResourceAsStream("/xsd/legacyActivePowerControlV1_1.xsd"), + getClass().getResourceAsStream("/xsd/legacyActivePowerControlV1_0.xsd")); + } + + @Override + public ActivePowerControl read(T identifiable, DeserializerContext context) { + boolean participate = context.getReader().readBooleanAttribute("legacyParticipate"); + double droop = context.getReader().readDoubleAttribute("legacyDroop"); + double participationFactor = Double.NaN; + NetworkDeserializerContext networkContext = (NetworkDeserializerContext) context; + String extVersionStr = networkContext.getExtensionVersion(this).orElseThrow(IllegalStateException::new); + if ("1.1".compareTo(extVersionStr) <= 0) { + participationFactor = context.getReader().readDoubleAttribute("legacyParticipationFactor"); + } + context.getReader().readEndNode(); + ActivePowerControlAdder activePowerControlAdder = identifiable.newExtension(ActivePowerControlAdder.class); + return activePowerControlAdder.withParticipate(participate) + .withDroop(droop) + .withParticipationFactor(participationFactor) + .add(); + } +} diff --git a/iidm/iidm-serde/src/test/resources/V1_0/alternativeActivePowerControlV1_0.xml b/iidm/iidm-serde/src/test/resources/V1_0/alternativeActivePowerControlV1_0.xml new file mode 100644 index 00000000000..5dfd71183b4 --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_0/alternativeActivePowerControlV1_0.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/iidm/iidm-serde/src/test/resources/V1_0/alternativeActivePowerControlV1_1.xml b/iidm/iidm-serde/src/test/resources/V1_0/alternativeActivePowerControlV1_1.xml new file mode 100644 index 00000000000..c0b5e06f86d --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_0/alternativeActivePowerControlV1_1.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/iidm/iidm-serde/src/test/resources/xsd/legacyActivePowerControlV1_0.xsd b/iidm/iidm-serde/src/test/resources/xsd/legacyActivePowerControlV1_0.xsd new file mode 100644 index 00000000000..1ba9515a77f --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/xsd/legacyActivePowerControlV1_0.xsd @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/xsd/legacyActivePowerControlV1_1.xsd b/iidm/iidm-serde/src/test/resources/xsd/legacyActivePowerControlV1_1.xsd new file mode 100644 index 00000000000..62416143470 --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/xsd/legacyActivePowerControlV1_1.xsd @@ -0,0 +1,22 @@ + + + + + + + + + + + From 2959fb8987ba6109c538e57f03311a6cb8aae0c7 Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Wed, 28 Aug 2024 14:02:22 +0200 Subject: [PATCH 8/9] Refactoring Signed-off-by: Olivier Perrin --- .../AbstractAlternativeExtensionSerDe.java | 21 ++++++++++ .../AbstractExtensionSerDeAlternative.java | 41 ------------------- ...ve.java => AlternativeExtensionSerDe.java} | 2 +- .../ExtensionProviderAlternative.java | 11 +---- .../extensions/ExtensionProviders.java | 16 ++++---- ...ativeVersionableNetworkExtensionSerDe.java | 29 +++++++++++++ .../ActivePowerControlSerDeAlternative.java | 25 ----------- .../LegacyActivePowerControlSerDe.java | 14 +++++-- .../LegacyOperatingStatusSerDe.java | 13 +++++- .../OperatingStatusSerDeAlternative.java | 25 ----------- 10 files changed, 83 insertions(+), 114 deletions(-) create mode 100644 commons/src/main/java/com/powsybl/commons/extensions/AbstractAlternativeExtensionSerDe.java delete mode 100644 commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java rename commons/src/main/java/com/powsybl/commons/extensions/{ExtensionSerDeAlternative.java => AlternativeExtensionSerDe.java} (73%) create mode 100644 iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/AbstractAlternativeVersionableNetworkExtensionSerDe.java delete mode 100644 iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/ActivePowerControlSerDeAlternative.java delete mode 100644 iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/OperatingStatusSerDeAlternative.java diff --git a/commons/src/main/java/com/powsybl/commons/extensions/AbstractAlternativeExtensionSerDe.java b/commons/src/main/java/com/powsybl/commons/extensions/AbstractAlternativeExtensionSerDe.java new file mode 100644 index 00000000000..e45e2739fd7 --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/extensions/AbstractAlternativeExtensionSerDe.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.extensions; + +/** + * @author Olivier Perrin {@literal } + */ +public abstract class AbstractAlternativeExtensionSerDe> + extends AbstractExtensionSerDe + implements AlternativeExtensionSerDe { + + protected AbstractAlternativeExtensionSerDe(String extensionName, String categoryName, Class extensionClass, + String xsdFileName, String namespaceUri, String namespacePrefix) { + super(extensionName, categoryName, extensionClass, xsdFileName, namespaceUri, namespacePrefix); + } +} diff --git a/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java b/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java deleted file mode 100644 index 5b8103c804e..00000000000 --- a/commons/src/main/java/com/powsybl/commons/extensions/AbstractExtensionSerDeAlternative.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) 2024, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.commons.extensions; - -import javax.annotation.Nonnull; -import java.util.Objects; - -/** - * @author Olivier Perrin {@literal } - */ -public abstract class AbstractExtensionSerDeAlternative> implements ExtensionSerDeAlternative { - - private final String originalExtensionName; - private final ExtensionSerDe provider; - - protected AbstractExtensionSerDeAlternative(String originalExtensionName, ExtensionSerDe provider) { - this.originalExtensionName = Objects.requireNonNull(originalExtensionName); - this.provider = Objects.requireNonNull(provider); - } - - @Override - public String getCategoryName() { - return provider.getCategoryName(); - } - - @Override - public String getExtensionName() { - return originalExtensionName; - } - - @Nonnull - @Override - public ExtensionSerDe getProvider() { - return provider; - } -} diff --git a/commons/src/main/java/com/powsybl/commons/extensions/ExtensionSerDeAlternative.java b/commons/src/main/java/com/powsybl/commons/extensions/AlternativeExtensionSerDe.java similarity index 73% rename from commons/src/main/java/com/powsybl/commons/extensions/ExtensionSerDeAlternative.java rename to commons/src/main/java/com/powsybl/commons/extensions/AlternativeExtensionSerDe.java index 644585048b3..e1dcad3c8ba 100644 --- a/commons/src/main/java/com/powsybl/commons/extensions/ExtensionSerDeAlternative.java +++ b/commons/src/main/java/com/powsybl/commons/extensions/AlternativeExtensionSerDe.java @@ -10,5 +10,5 @@ /** * @author Olivier Perrin {@literal } */ -public interface ExtensionSerDeAlternative> extends ExtensionProviderAlternative> { +public interface AlternativeExtensionSerDe> extends ExtensionProviderAlternative, ExtensionSerDe { } diff --git a/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviderAlternative.java b/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviderAlternative.java index 103385dba2b..5d3278884a8 100644 --- a/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviderAlternative.java +++ b/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviderAlternative.java @@ -7,16 +7,9 @@ */ package com.powsybl.commons.extensions; -import javax.annotation.Nonnull; - /** * @author Olivier Perrin {@literal } */ -public interface ExtensionProviderAlternative

{ - String getCategoryName(); - - String getExtensionName(); - - @Nonnull - P getProvider(); +public interface ExtensionProviderAlternative> extends ExtensionSerDe { + String getOriginalExtensionName(); } diff --git a/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviders.java b/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviders.java index 88f4ca241a5..ab48b020536 100644 --- a/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviders.java +++ b/commons/src/main/java/com/powsybl/commons/extensions/ExtensionProviders.java @@ -20,7 +20,7 @@ public final class ExtensionProviders { private final Map providers; - private final Map> providerAlternatives; + private final Map alternativeProviders; public static ExtensionProviders createProvider(Class clazz) { return new ExtensionProviders<>(clazz); @@ -57,18 +57,18 @@ private ExtensionProviders(Class clazz, String categoryName, Set exte Stream providerAlternativeStream = new ServiceLoaderCache<>(alternativeClass).getServices().stream(); if (categoryName != null) { providerAlternativeStream = providerAlternativeStream.filter(s -> s.getCategoryName().equals(categoryName) && - (extensionNames == null || extensionNames.contains(s.getExtensionName()))); + (extensionNames == null || extensionNames.contains(s.getOriginalExtensionName()))); } - providerAlternatives = providerAlternativeStream - .collect(Collectors.toMap(ExtensionProviderAlternative::getExtensionName, e -> e)); + alternativeProviders = providerAlternativeStream + .collect(Collectors.toMap(ExtensionProviderAlternative::getOriginalExtensionName, e -> (T) e)); } else { - providerAlternatives = Collections.emptyMap(); + alternativeProviders = Collections.emptyMap(); } } private Class getExtensionProviderAlternativeClass(Class clazz) { if (clazz.equals(ExtensionSerDe.class)) { - return ExtensionSerDeAlternative.class; + return AlternativeExtensionSerDe.class; } return null; } @@ -79,9 +79,9 @@ public T findProvider(String name) { public T findProvider(String name, ExtensionProvidersOptions options) { if (options != null && options.useAlternativeVersion(name)) { - ExtensionProviderAlternative alternative = providerAlternatives.get(name); + T alternative = alternativeProviders.get(name); if (alternative != null) { - return alternative.getProvider(); + return alternative; } } return providers.get(name); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/AbstractAlternativeVersionableNetworkExtensionSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/AbstractAlternativeVersionableNetworkExtensionSerDe.java new file mode 100644 index 00000000000..ee06dd244bb --- /dev/null +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/AbstractAlternativeVersionableNetworkExtensionSerDe.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde.extensions; + +import com.google.common.collect.ImmutableSortedSet; +import com.powsybl.commons.extensions.AlternativeExtensionSerDe; +import com.powsybl.commons.extensions.Extendable; +import com.powsybl.commons.extensions.Extension; +import com.powsybl.iidm.serde.IidmVersion; + +import java.util.Map; + +/** + * @author Olivier Perrin {@literal } + */ +public abstract class AbstractAlternativeVersionableNetworkExtensionSerDe> + extends AbstractVersionableNetworkExtensionSerDe + implements AlternativeExtensionSerDe { + + protected AbstractAlternativeVersionableNetworkExtensionSerDe(String extensionName, Class extensionClass, String namespacePrefix, + Map> extensionVersions, Map namespaceUris) { + super(extensionName, extensionClass, namespacePrefix, extensionVersions, namespaceUris); + } +} diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/ActivePowerControlSerDeAlternative.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/ActivePowerControlSerDeAlternative.java deleted file mode 100644 index 60621e8ab8e..00000000000 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/ActivePowerControlSerDeAlternative.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2024, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.iidm.serde.extensions.alternatives; - -import com.google.auto.service.AutoService; -import com.powsybl.commons.extensions.AbstractExtensionSerDeAlternative; -import com.powsybl.commons.extensions.ExtensionSerDeAlternative; -import com.powsybl.iidm.network.Injection; -import com.powsybl.iidm.network.extensions.ActivePowerControl; - -/** - * @author Olivier Perrin {@literal } - */ -@AutoService(ExtensionSerDeAlternative.class) -public class ActivePowerControlSerDeAlternative> extends AbstractExtensionSerDeAlternative> { - - public ActivePowerControlSerDeAlternative() { - super(ActivePowerControl.NAME, new LegacyActivePowerControlSerDe<>()); - } -} diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyActivePowerControlSerDe.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyActivePowerControlSerDe.java index 267a9ffe8d5..6fa10180a2d 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyActivePowerControlSerDe.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyActivePowerControlSerDe.java @@ -10,6 +10,7 @@ import com.google.auto.service.AutoService; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedSet; +import com.powsybl.commons.extensions.AlternativeExtensionSerDe; import com.powsybl.commons.extensions.ExtensionSerDe; import com.powsybl.commons.io.DeserializerContext; import com.powsybl.commons.io.SerializerContext; @@ -19,7 +20,7 @@ import com.powsybl.iidm.serde.IidmVersion; import com.powsybl.iidm.serde.NetworkDeserializerContext; import com.powsybl.iidm.serde.NetworkSerializerContext; -import com.powsybl.iidm.serde.extensions.AbstractVersionableNetworkExtensionSerDe; +import com.powsybl.iidm.serde.extensions.AbstractAlternativeVersionableNetworkExtensionSerDe; import java.io.InputStream; import java.util.List; @@ -27,8 +28,9 @@ /** * @author Olivier Perrin {@literal } */ -@AutoService(ExtensionSerDe.class) -public class LegacyActivePowerControlSerDe> extends AbstractVersionableNetworkExtensionSerDe> { +@AutoService({AlternativeExtensionSerDe.class, ExtensionSerDe.class}) +public class LegacyActivePowerControlSerDe> + extends AbstractAlternativeVersionableNetworkExtensionSerDe> { public LegacyActivePowerControlSerDe() { super("legacyActivePowerControl", ActivePowerControl.class, "lapc", @@ -41,6 +43,11 @@ public LegacyActivePowerControlSerDe() { .build()); } + @Override + public String getOriginalExtensionName() { + return ActivePowerControl.NAME; + } + @Override public void write(ActivePowerControl activePowerControl, SerializerContext context) { context.getWriter().writeBooleanAttribute("legacyParticipate", activePowerControl.isParticipate()); @@ -81,4 +88,5 @@ public ActivePowerControl read(T identifiable, DeserializerContext context) { .withParticipationFactor(participationFactor) .add(); } + } diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyOperatingStatusSerDe.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyOperatingStatusSerDe.java index 8052327c89f..5704f5b373e 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyOperatingStatusSerDe.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyOperatingStatusSerDe.java @@ -8,7 +8,9 @@ package com.powsybl.iidm.serde.extensions.alternatives; import com.google.auto.service.AutoService; +import com.powsybl.commons.extensions.AbstractAlternativeExtensionSerDe; import com.powsybl.commons.extensions.AbstractExtensionSerDe; +import com.powsybl.commons.extensions.AlternativeExtensionSerDe; import com.powsybl.commons.extensions.ExtensionSerDe; import com.powsybl.commons.io.DeserializerContext; import com.powsybl.commons.io.SerializerContext; @@ -19,8 +21,9 @@ /** * @author Olivier Perrin {@literal } */ -@AutoService(ExtensionSerDe.class) -public class LegacyOperatingStatusSerDe> extends AbstractExtensionSerDe> { +@AutoService({AlternativeExtensionSerDe.class, ExtensionSerDe.class}) +public class LegacyOperatingStatusSerDe> + extends AbstractAlternativeExtensionSerDe> { public LegacyOperatingStatusSerDe() { super("legacyOperatingStatus", "network", OperatingStatus.class, @@ -28,6 +31,11 @@ public LegacyOperatingStatusSerDe() { "los"); } + @Override + public String getOriginalExtensionName() { + return OperatingStatus.NAME; + } + @Override public void write(OperatingStatus status, SerializerContext context) { context.getWriter().writeNodeContent(status.getStatus().name()); @@ -40,4 +48,5 @@ public OperatingStatus read(I identifiable, DeserializerContext context) { return adder.withStatus(status) .add(); } + } diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/OperatingStatusSerDeAlternative.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/OperatingStatusSerDeAlternative.java deleted file mode 100644 index 5724c4d7ab9..00000000000 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/OperatingStatusSerDeAlternative.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2024, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.iidm.serde.extensions.alternatives; - -import com.google.auto.service.AutoService; -import com.powsybl.commons.extensions.AbstractExtensionSerDeAlternative; -import com.powsybl.commons.extensions.ExtensionSerDeAlternative; -import com.powsybl.iidm.network.Identifiable; -import com.powsybl.iidm.network.extensions.OperatingStatus; - -/** - * @author Olivier Perrin {@literal } - */ -@AutoService(ExtensionSerDeAlternative.class) -public class OperatingStatusSerDeAlternative> extends AbstractExtensionSerDeAlternative> { - - public OperatingStatusSerDeAlternative() { - super(OperatingStatus.NAME, new LegacyOperatingStatusSerDe<>()); - } -} From 1536f62f787c5972216bffacfddfb717f9b3e38d Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Wed, 28 Aug 2024 15:37:29 +0200 Subject: [PATCH 9/9] Fix case where a specific version is asked for alternative Signed-off-by: Olivier Perrin --- .../com/powsybl/iidm/serde/ExportOptions.java | 15 +++------------ .../extensions/AlternativeExtensionXmlTest.java | 6 ++++-- .../LegacyActivePowerControlSerDe.java | 2 +- .../alternatives/LegacyOperatingStatusSerDe.java | 1 - 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java index 41ac6720226..043a97114f7 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java @@ -33,7 +33,7 @@ public enum IidmVersionIncompatibilityBehavior { private static final Logger LOGGER = LoggerFactory.getLogger(ExportOptions.class); - public static final String ALTERNATIVE_SUFFIX = "alternative"; + public static final String ALTERNATIVE_VERSION = "alternative"; private boolean withBranchSV = true; @@ -229,16 +229,7 @@ public ExportOptions addExtensionVersion(String extensionName, String extensionV */ public Optional getExtensionVersion(String extensionName) { return Optional.ofNullable(extensionsVersions.get(extensionName)) - .map(v -> { - if (v.startsWith(ALTERNATIVE_SUFFIX)) { - // If the version starts with "alternative", 2 cases: - // - the version is exactly "alternative" => no version was specified for the alternative - // - the version has extra-characters, it is striped from its prefix. Note that an additional character - // is also skipped. The version should be specified as "alternative_1.0" or "alternative-1.2" for instance. - return v.length() == ALTERNATIVE_SUFFIX.length() ? null : v.substring(ALTERNATIVE_SUFFIX.length() + 1); - } - return v; - }); + .map(v -> ALTERNATIVE_VERSION.equals(v) ? null : v); } public boolean isSorted() { @@ -262,6 +253,6 @@ public ExportOptions setWithAutomationSystems(boolean withAutomationSystems) { @Override public boolean useAlternativeVersion(String extensionName) { String v = extensionsVersions.get(extensionName); - return v != null && v.startsWith(ALTERNATIVE_SUFFIX); + return ALTERNATIVE_VERSION.equals(v); } } diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java index 116b2b35376..3a2f4ea2179 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/AlternativeExtensionXmlTest.java @@ -67,7 +67,7 @@ void versionedAlternativeWithDefaultVersionTest() throws IOException { generator.addExtension(ActivePowerControl.class, new ActivePowerControlImpl<>(generator, false, 3.0, 1.0)); // No version is specified for the alternative: it should use the default version (v1.1) - var exportOptions = new ExportOptions().addExtensionVersion(ActivePowerControl.NAME, "alternative"); + var exportOptions = new ExportOptions().addExtensionVersion(ActivePowerControl.NAME, ExportOptions.ALTERNATIVE_VERSION); Network network2 = allFormatsRoundTripTest(network, "/alternativeActivePowerControlV1_1.xml", IidmVersion.V_1_0, exportOptions); Battery bat2 = network2.getBattery("BAT"); @@ -88,7 +88,9 @@ void versionedAlternativeWithSpecificVersionTest() throws IOException { generator.addExtension(ActivePowerControl.class, new ActivePowerControlImpl<>(generator, false, 3.0, 1.0)); // Explicitly ask for version 1.0 of the alternative - var exportOptions = new ExportOptions().addExtensionVersion(ActivePowerControl.NAME, "alternative-1.0"); + var exportOptions = new ExportOptions() + .addExtensionVersion("legacyActivePowerControl", "1.0") + .addExtensionVersion(ActivePowerControl.NAME, ExportOptions.ALTERNATIVE_VERSION); Network network2 = allFormatsRoundTripTest(network, "/alternativeActivePowerControlV1_0.xml", IidmVersion.V_1_0, exportOptions); Battery bat2 = network2.getBattery("BAT"); diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyActivePowerControlSerDe.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyActivePowerControlSerDe.java index 6fa10180a2d..5a7ddf594ac 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyActivePowerControlSerDe.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyActivePowerControlSerDe.java @@ -53,7 +53,7 @@ public void write(ActivePowerControl activePowerControl, SerializerContext co context.getWriter().writeBooleanAttribute("legacyParticipate", activePowerControl.isParticipate()); context.getWriter().writeDoubleAttribute("legacyDroop", activePowerControl.getDroop()); NetworkSerializerContext networkContext = (NetworkSerializerContext) context; - String extVersionStr = networkContext.getExtensionVersion(ActivePowerControl.NAME) + String extVersionStr = networkContext.getExtensionVersion(getExtensionName()) .orElseGet(() -> getVersion(networkContext.getVersion())); if ("1.1".compareTo(extVersionStr) <= 0) { context.getWriter().writeDoubleAttribute("legacyParticipationFactor", activePowerControl.getParticipationFactor()); diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyOperatingStatusSerDe.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyOperatingStatusSerDe.java index 5704f5b373e..2811a177f9b 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyOperatingStatusSerDe.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/alternatives/LegacyOperatingStatusSerDe.java @@ -9,7 +9,6 @@ import com.google.auto.service.AutoService; import com.powsybl.commons.extensions.AbstractAlternativeExtensionSerDe; -import com.powsybl.commons.extensions.AbstractExtensionSerDe; import com.powsybl.commons.extensions.AlternativeExtensionSerDe; import com.powsybl.commons.extensions.ExtensionSerDe; import com.powsybl.commons.io.DeserializerContext;