From 3d5d3b2325f8c6a9f752e092d417994e58d7e092 Mon Sep 17 00:00:00 2001 From: Daniel Schwering Date: Wed, 2 Oct 2024 13:30:05 +0200 Subject: [PATCH] FIPS-11962/FIPS-11971 :: Add base class for renamed attribute migrations Change-Id: Ia154aa69874d72b59f7f5419a6a480938f8db704 --- .../MigrationForChangedAttribute.java | 97 ++++++++ .../META-INF/MANIFEST.MF | 6 +- .../META-INF/MANIFEST.MF | 2 + .../MigrationForChangedAttributeTest.java | 226 ++++++++++++++++++ 4 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 devtools/eclipse/plugins/org.faktorips.devtools.core/src/org/faktorips/devtools/core/internal/migrationextensions/MigrationForChangedAttribute.java create mode 100644 devtools/eclipse/plugins/tests/org.faktorips.devtools.core.test/src/org/faktorips/devtools/core/internal/migrationextensions/MigrationForChangedAttributeTest.java diff --git a/devtools/eclipse/plugins/org.faktorips.devtools.core/src/org/faktorips/devtools/core/internal/migrationextensions/MigrationForChangedAttribute.java b/devtools/eclipse/plugins/org.faktorips.devtools.core/src/org/faktorips/devtools/core/internal/migrationextensions/MigrationForChangedAttribute.java new file mode 100644 index 0000000000..d568a53f0c --- /dev/null +++ b/devtools/eclipse/plugins/org.faktorips.devtools.core/src/org/faktorips/devtools/core/internal/migrationextensions/MigrationForChangedAttribute.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) Faktor Zehn GmbH - faktorzehn.org + * + * This source code is available under the terms of the AGPL Affero General Public License version + * 3. + * + * Please see LICENSE.txt for full license terms, including the additional permissions and + * restrictions as well as the possibility of alternative license terms. + *******************************************************************************/ + +package org.faktorips.devtools.core.internal.migrationextensions; + +import java.util.Arrays; +import java.util.List; + +import org.faktorips.devtools.abstraction.exception.IpsException; +import org.faktorips.devtools.core.internal.migration.DefaultMigration; +import org.faktorips.devtools.model.ipsobject.IIpsSrcFile; +import org.faktorips.devtools.model.ipsobject.IpsObjectType; +import org.faktorips.devtools.model.ipsproject.IIpsProject; +import org.faktorips.devtools.model.pctype.IPolicyCmptType; +import org.faktorips.devtools.model.productcmpt.IAttributeValue; +import org.faktorips.devtools.model.productcmpt.IConfigElement; +import org.faktorips.devtools.model.productcmpt.IProductCmpt; +import org.faktorips.devtools.model.productcmpt.IPropertyValue; +import org.faktorips.devtools.model.productcmpttype.IProductCmptType; + +/** + * Base class for custom migrations that migrate {@link IProductCmpt product components} after an + * attribute was renamed in a model type. + */ +public class MigrationForChangedAttribute extends DefaultMigration { + + private String targetVersion; + private String description; + private ChangedAttribute[] changedAttributes; + + public MigrationForChangedAttribute(IIpsProject projectToMigrate, String featureId, String targetVersion, + String description, ChangedAttribute... changedAttributes) { + super(projectToMigrate, featureId); + this.targetVersion = targetVersion; + this.description = description; + this.changedAttributes = changedAttributes; + } + + @Override + protected void migrate(IIpsSrcFile srcFile) throws IpsException { + if (IpsObjectType.PRODUCT_CMPT.equals(srcFile.getIpsObjectType())) { + IProductCmpt productCmpt = (IProductCmpt)srcFile.getIpsObject(); + IProductCmptType productCmptType = productCmpt.findProductCmptType(getIpsProject()); + List productCmptTypes = productCmptType.getSupertypeHierarchy() + .getAllSupertypesInclSelf(productCmptType).stream().map(IProductCmptType.class::cast).toList(); + List policyCmptTypes = productCmptTypes.stream() + .filter(IProductCmptType::isConfigurationForPolicyCmptType) + .map(t -> t.findPolicyCmptType(getIpsProject())).toList(); + Arrays.stream(changedAttributes).forEach(changedAttribute -> { + if (productCmptTypes.stream() + .anyMatch(t -> t.getQualifiedName().equals(changedAttribute.qualifiedTypeName))) { + IAttributeValue attributeValue = productCmpt.getAttributeValue(changedAttribute.oldName); + if (attributeValue != null) { + attributeValue.setAttribute(changedAttribute.newName); + srcFile.save(null); + } + } + if (policyCmptTypes.stream() + .anyMatch(t -> t.getQualifiedName().equals(changedAttribute.qualifiedTypeName))) { + List propertyValues = productCmpt.getPropertyValues(changedAttribute.oldName); + propertyValues.forEach(v -> { + if (v instanceof IConfigElement configElement) { + configElement.setPolicyCmptTypeAttribute(changedAttribute.newName); + } + }); + srcFile.save(null); + } + }); + } + } + + @Override + public String getTargetVersion() { + return targetVersion; + } + + @Override + public String getDescription() { + return description; + } + + /** + * A changed attribute is defined by the qualified name of the policy component type or product + * component type it is defined in and the old and new name of the attribute. + */ + public record ChangedAttribute(String qualifiedTypeName, String oldName, String newName) { + + } + +} diff --git a/devtools/eclipse/plugins/org.faktorips.devtools.model/META-INF/MANIFEST.MF b/devtools/eclipse/plugins/org.faktorips.devtools.model/META-INF/MANIFEST.MF index bbd0635395..de4ad0eb76 100755 --- a/devtools/eclipse/plugins/org.faktorips.devtools.model/META-INF/MANIFEST.MF +++ b/devtools/eclipse/plugins/org.faktorips.devtools.model/META-INF/MANIFEST.MF @@ -221,7 +221,11 @@ Export-Package: org.faktorips.devtools.model, org.faktorips.devtools.core.ui, org.faktorips.devtools.core.ui.test, org.faktorips.devtools.model.decorators", - org.faktorips.devtools.model.internal.type;x-friends:="org.faktorips.abstracttest,org.faktorips.devtools.core.ui,org.faktorips.devtools.core.ui.test", + org.faktorips.devtools.model.internal.type; + x-friends:="org.faktorips.abstracttest, + org.faktorips.devtools.core.ui, + org.faktorips.devtools.core.ui.test, + org.faktorips.devtools.core.test", org.faktorips.devtools.model.internal.util;x-friends:="org.faktorips.devtools.core.ui,org.faktorips.devtools.core.ui.test", org.faktorips.devtools.model.internal.value;x-friends:="org.faktorips.devtools.multiediting,org.faktorips.devtools.multiediting.test", org.faktorips.devtools.model.internal.valueset; diff --git a/devtools/eclipse/plugins/tests/org.faktorips.devtools.core.test/META-INF/MANIFEST.MF b/devtools/eclipse/plugins/tests/org.faktorips.devtools.core.test/META-INF/MANIFEST.MF index cf968b651c..4cbc2449d5 100755 --- a/devtools/eclipse/plugins/tests/org.faktorips.devtools.core.test/META-INF/MANIFEST.MF +++ b/devtools/eclipse/plugins/tests/org.faktorips.devtools.core.test/META-INF/MANIFEST.MF @@ -29,6 +29,8 @@ Import-Package: org.faktorips.abstracttest, org.faktorips.devtools.model.internal.productcmpt, org.faktorips.devtools.model.internal.tablecontents, org.faktorips.devtools.model.internal.testcasetype, + org.faktorips.devtools.model.internal.type, + org.faktorips.devtools.model.internal.valueset, org.faktorips.devtools.model.ipsobject, org.faktorips.devtools.model.ipsproject, org.faktorips.devtools.model.method, diff --git a/devtools/eclipse/plugins/tests/org.faktorips.devtools.core.test/src/org/faktorips/devtools/core/internal/migrationextensions/MigrationForChangedAttributeTest.java b/devtools/eclipse/plugins/tests/org.faktorips.devtools.core.test/src/org/faktorips/devtools/core/internal/migrationextensions/MigrationForChangedAttributeTest.java new file mode 100644 index 0000000000..445e01cb05 --- /dev/null +++ b/devtools/eclipse/plugins/tests/org.faktorips.devtools.core.test/src/org/faktorips/devtools/core/internal/migrationextensions/MigrationForChangedAttributeTest.java @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (c) Faktor Zehn GmbH - faktorzehn.org + * + * This source code is available under the terms of the AGPL Affero General Public License version + * 3. + * + * Please see LICENSE.txt for full license terms, including the additional permissions and + * restrictions as well as the possibility of alternative license terms. + *******************************************************************************/ + +package org.faktorips.devtools.core.internal.migrationextensions; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.faktorips.abstracttest.AbstractIpsPluginTest; +import org.faktorips.abstracttest.TestIpsModelExtensions; +import org.faktorips.datatype.Datatype; +import org.faktorips.devtools.core.internal.migrationextensions.MigrationForChangedAttribute.ChangedAttribute; +import org.faktorips.devtools.model.internal.pctype.PolicyCmptType; +import org.faktorips.devtools.model.internal.productcmpt.ProductCmpt; +import org.faktorips.devtools.model.internal.productcmpt.SingleValueHolder; +import org.faktorips.devtools.model.internal.productcmpttype.ProductCmptType; +import org.faktorips.devtools.model.ipsproject.IIpsProject; +import org.faktorips.devtools.model.ipsproject.IIpsProjectProperties; +import org.faktorips.devtools.model.pctype.IPolicyCmptTypeAttribute; +import org.faktorips.devtools.model.productcmpt.IAttributeValue; +import org.faktorips.devtools.model.productcmpt.IConfiguredDefault; +import org.faktorips.devtools.model.productcmpt.IConfiguredValueSet; +import org.faktorips.devtools.model.productcmpttype.IProductCmptType; +import org.faktorips.devtools.model.productcmpttype.IProductCmptTypeAttribute; +import org.faktorips.devtools.model.valueset.IStringLengthValueSet; +import org.faktorips.devtools.model.valueset.ValueSetType; +import org.faktorips.devtools.model.versionmanager.IIpsFeatureVersionManager; +import org.junit.Test; + +public class MigrationForChangedAttributeTest extends AbstractIpsPluginTest { + + private static final String FEATURE_ID = "my.feature"; + + private IIpsFeatureVersionManager versionManager; + + @Override + public void setUp() throws Exception { + super.setUp(); + versionManager = mock(IIpsFeatureVersionManager.class); + when(versionManager.getFeatureId()).thenReturn(FEATURE_ID); + when(versionManager.isRequiredForAllProjects()).thenReturn(false); + } + + @Test + public void testMigrationOfProductAttribute() { + try (TestIpsModelExtensions testIpsModelExtensions = TestIpsModelExtensions.get()) { + testIpsModelExtensions.setFeatureVersionManagers(versionManager); + IIpsProject ipsProject = newIpsProjectWithFeature(); + ProductCmptType changedProductCmptType = newProductCmptType(ipsProject, + "model.products.ChangedProductCmptType"); + String oldAttributeName = "oldAttributeName"; + IProductCmptTypeAttribute changedAttribute = newAttribute(changedProductCmptType, oldAttributeName); + ProductCmpt productCmpt = newProductCmpt(changedProductCmptType, "productToChange"); + setAttributeValue(productCmpt, changedAttribute, "aValue"); + changedAttribute.setName("newAttributeName"); + changedProductCmptType.getIpsSrcFile().save(null); + MigrationForChangedAttribute migration = new MigrationForChangedAttribute(ipsProject, FEATURE_ID, "2", + "a description", new ChangedAttribute( + "model.products.ChangedProductCmptType", oldAttributeName, "newAttributeName")); + + migration.migrate(productCmpt.getIpsSrcFile()); + + assertThat(productCmpt.getAttributeValue("oldAttributeName"), is(nullValue())); + assertThat(productCmpt.getAttributeValue("newAttributeName").getValueHolder().getStringValue(), + is("aValue")); + } + } + + @Test + public void testMigrationOfPolicyAttribute() { + try (TestIpsModelExtensions testIpsModelExtensions = TestIpsModelExtensions.get()) { + testIpsModelExtensions.setFeatureVersionManagers(versionManager); + IIpsProject ipsProject = newIpsProjectWithFeature(); + PolicyCmptType changedPolicyCmptType = newPolicyAndProductCmptType(ipsProject, + "model.ChangedPolicyCmptType", "model.ChangedProductCmptType"); + IProductCmptType changedProductCmptType = changedPolicyCmptType.findProductCmptType(ipsProject); + String oldAttributeName = "oldAttributeName"; + IPolicyCmptTypeAttribute changedAttribute = newAttribute(changedPolicyCmptType, oldAttributeName); + ProductCmpt productCmpt = newProductCmpt(changedProductCmptType, "productToChange"); + setDefaultValue(productCmpt, changedAttribute, "aValue"); + setValueSetStringLength(productCmpt, changedAttribute, "10"); + changedAttribute.setName("newAttributeName"); + changedPolicyCmptType.getIpsSrcFile().save(null); + MigrationForChangedAttribute migration = new MigrationForChangedAttribute(ipsProject, FEATURE_ID, "2", + "a description", new ChangedAttribute( + "model.ChangedPolicyCmptType", oldAttributeName, "newAttributeName")); + + migration.migrate(productCmpt.getIpsSrcFile()); + + assertThat(productCmpt.getPropertyValue("oldAttributeName", IConfiguredDefault.class), is(nullValue())); + assertThat(productCmpt.getPropertyValue("newAttributeName", IConfiguredDefault.class).getValue(), + is("aValue")); + assertThat(productCmpt.getPropertyValue("oldAttributeName", IConfiguredValueSet.class), is(nullValue())); + assertThat(productCmpt.getPropertyValue("newAttributeName", IConfiguredValueSet.class).getValueSet(), + is(instanceOf(IStringLengthValueSet.class))); + assertThat( + ((IStringLengthValueSet)productCmpt.getPropertyValue("newAttributeName", IConfiguredValueSet.class) + .getValueSet()).getMaximumLength(), + is("10")); + } + } + + @Test + public void testMigrationOfSubtype() { + try (TestIpsModelExtensions testIpsModelExtensions = TestIpsModelExtensions.get()) { + testIpsModelExtensions.setFeatureVersionManagers(versionManager); + IIpsProject ipsProject = newIpsProjectWithFeature(); + ProductCmptType changedProductCmptType = newProductCmptType(ipsProject, + "model.products.ChangedProductCmptType"); + ProductCmptType subProductCmptType = newProductCmptType(ipsProject, + "model.products.SubProductCmptType"); + subProductCmptType.setSupertype("model.products.ChangedProductCmptType"); + String oldAttributeName = "oldAttributeName"; + IProductCmptTypeAttribute changedAttribute = newAttribute(changedProductCmptType, oldAttributeName); + ProductCmpt productCmpt = newProductCmpt(subProductCmptType, "productToChange"); + setAttributeValue(productCmpt, changedAttribute, "aValue"); + changedAttribute.setName("newAttributeName"); + changedProductCmptType.getIpsSrcFile().save(null); + MigrationForChangedAttribute migration = new MigrationForChangedAttribute(ipsProject, FEATURE_ID, "2", + "a description", new ChangedAttribute( + "model.products.ChangedProductCmptType", oldAttributeName, "newAttributeName")); + + migration.migrate(productCmpt.getIpsSrcFile()); + + assertThat(productCmpt.getAttributeValue("oldAttributeName"), is(nullValue())); + assertThat(productCmpt.getAttributeValue("newAttributeName").getValueHolder().getStringValue(), + is("aValue")); + } + } + + @Test + public void testMigrationTwice() { + try (TestIpsModelExtensions testIpsModelExtensions = TestIpsModelExtensions.get()) { + testIpsModelExtensions.setFeatureVersionManagers(versionManager); + IIpsProject ipsProject = newIpsProjectWithFeature(); + ProductCmptType changedProductCmptType = newProductCmptType(ipsProject, + "model.products.ChangedProductCmptType"); + String oldAttributeName = "oldAttributeName"; + IProductCmptTypeAttribute changedAttribute = newAttribute(changedProductCmptType, oldAttributeName); + ProductCmpt productCmpt = newProductCmpt(changedProductCmptType, "productToChange"); + setAttributeValue(productCmpt, changedAttribute, "aValue"); + changedAttribute.setName("newAttributeName"); + changedProductCmptType.getIpsSrcFile().save(null); + MigrationForChangedAttribute migration = new MigrationForChangedAttribute(ipsProject, FEATURE_ID, "2", + "a description", new ChangedAttribute( + "model.products.ChangedProductCmptType", oldAttributeName, "newAttributeName")); + + migration.migrate(productCmpt.getIpsSrcFile()); + + assertThat(productCmpt.getAttributeValue("oldAttributeName"), is(nullValue())); + assertThat(productCmpt.getAttributeValue("newAttributeName").getValueHolder().getStringValue(), + is("aValue")); + + migration.migrate(productCmpt.getIpsSrcFile()); + + assertThat(productCmpt.getAttributeValue("oldAttributeName"), is(nullValue())); + assertThat(productCmpt.getAttributeValue("newAttributeName").getValueHolder().getStringValue(), + is("aValue")); + } + } + + private void setAttributeValue(ProductCmpt productCmpt, + IProductCmptTypeAttribute changedAttribute, + String value) { + IAttributeValue attributeValue = productCmpt.newPropertyValue(changedAttribute, IAttributeValue.class); + attributeValue.setAttribute(changedAttribute.getName()); + attributeValue.setValueHolder(new SingleValueHolder(attributeValue, value)); + productCmpt.getIpsSrcFile().save(null); + } + + private IProductCmptTypeAttribute newAttribute(ProductCmptType changedProductCmptType, String name) { + IProductCmptTypeAttribute changedAttribute = changedProductCmptType.newProductCmptTypeAttribute(name); + changedAttribute.setDatatype(Datatype.STRING.getQualifiedName()); + changedProductCmptType.getIpsSrcFile().save(null); + return changedAttribute; + } + + private void setDefaultValue(ProductCmpt productCmpt, + IPolicyCmptTypeAttribute changedAttribute, + String value) { + IConfiguredDefault defaultValue = productCmpt.newPropertyValue(changedAttribute, IConfiguredDefault.class); + defaultValue.setPolicyCmptTypeAttribute(changedAttribute.getName()); + defaultValue.setValue(value); + productCmpt.getIpsSrcFile().save(null); + } + + private void setValueSetStringLength(ProductCmpt productCmpt, + IPolicyCmptTypeAttribute changedAttribute, + String value) { + IConfiguredValueSet configuredValueSet = productCmpt.newPropertyValue(changedAttribute, + IConfiguredValueSet.class); + configuredValueSet.setPolicyCmptTypeAttribute(changedAttribute.getName()); + IStringLengthValueSet valueSet = (IStringLengthValueSet)configuredValueSet + .changeValueSetType(ValueSetType.STRINGLENGTH); + valueSet.setMaximumLength(value); + productCmpt.getIpsSrcFile().save(null); + } + + private IPolicyCmptTypeAttribute newAttribute(PolicyCmptType changedPolicyCmptType, String name) { + IPolicyCmptTypeAttribute changedAttribute = changedPolicyCmptType.newPolicyCmptTypeAttribute(name); + changedAttribute.setDatatype(Datatype.STRING.getQualifiedName()); + changedAttribute.setValueSetConfiguredByProduct(true); + changedPolicyCmptType.getIpsSrcFile().save(null); + return changedAttribute; + } + + private IIpsProject newIpsProjectWithFeature() { + IIpsProject ipsProject = newIpsProject(); + IIpsProjectProperties props = ipsProject.getProperties(); + props.setMinRequiredVersionNumber(FEATURE_ID, "1"); + ipsProject.setProperties(props); + return ipsProject; + } + +}