From 600085864cc27d44057e1056f603f5a80e2f6e20 Mon Sep 17 00:00:00 2001 From: Sebastian Krieter Date: Tue, 7 Oct 2025 17:45:31 +0200 Subject: [PATCH 1/8] Improves CLI output --- .../ovgu/featureide/fm/core/cli/ConfigurationGenerator.java | 4 ++-- .../src/de/ovgu/featureide/fm/core/cli/FeatureIDECLI.java | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/cli/ConfigurationGenerator.java b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/cli/ConfigurationGenerator.java index 712c15f8ba..26579b6447 100644 --- a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/cli/ConfigurationGenerator.java +++ b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/cli/ConfigurationGenerator.java @@ -225,11 +225,11 @@ private void parseArguments(List args) { break; } default: { - throw new IllegalArgumentException(arg); + throw new IllegalArgumentException("Unknown argument: " + arg); } } } else { - throw new IllegalArgumentException(arg); + throw new IllegalArgumentException("Unknown value: " + arg); } } } diff --git a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/cli/FeatureIDECLI.java b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/cli/FeatureIDECLI.java index 3189f7c920..4056baa62f 100644 --- a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/cli/FeatureIDECLI.java +++ b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/cli/FeatureIDECLI.java @@ -38,8 +38,6 @@ public static void main(String[] args) { System.err.println("No operation specified!"); return; } - System.err.println(Arrays.asList(args)); - final String functionName = args[0]; LibraryManager.registerLibrary(FMCoreLibrary.getInstance()); From 9e552d4ed3d786d1856910c3909c29567df75b86 Mon Sep 17 00:00:00 2001 From: Sebastian Krieter Date: Tue, 7 Oct 2025 17:47:15 +0200 Subject: [PATCH 2/8] Improves feature model clone and test --- .../fm/core/base/impl/AConstraint.java | 12 +++++++----- .../tests/TExtendedFeatureModel.java | 18 +++++++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/base/impl/AConstraint.java b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/base/impl/AConstraint.java index c22ad3d46a..41957e37ac 100644 --- a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/base/impl/AConstraint.java +++ b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/base/impl/AConstraint.java @@ -25,6 +25,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import org.prop4j.Node; @@ -47,7 +48,7 @@ public abstract class AConstraint extends AFeatureModelElement implements IConst protected final IPropertyContainer propertyContainer; - protected final List containedFeatureList = new ArrayList<>(); + protected final List containedFeatureList = new ArrayList<>(); protected Node propNode; boolean featureSelected; @@ -68,7 +69,7 @@ public abstract class AConstraint extends AFeatureModelElement implements IConst public AConstraint(IConstraint oldConstraint, IFeatureModel featureModel, boolean copyId) { super(oldConstraint, featureModel, copyId); setNode(oldConstraint.getNode().clone()); - description = oldConstraint.getDescription(); + description = new String(oldConstraint.getDescription()); tags = new HashSet<>(oldConstraint.getTags()); propertyContainer = new MapPropertyContainer(oldConstraint.getCustomProperties()); if (oldConstraint instanceof AConstraint) { @@ -106,7 +107,7 @@ public IPropertyContainer getCustomProperties() { @Override public Collection getContainedFeatures() { synchronized (containedFeatureList) { - return new ArrayList<>(containedFeatureList); + return new ArrayList<>(containedFeatureList.stream().map(featureModel::getFeature).collect(Collectors.toList())); } } @@ -136,8 +137,9 @@ public void setNode(Node node) { synchronized (containedFeatureList) { containedFeatureList.clear(); if (propNode != null) { - for (final String featureName : propNode.getContainedFeatures()) { - containedFeatureList.add(featureModel.getFeature(featureName)); + final List containedFeatureNames = propNode.getContainedFeatures(); + for (final String containedFeatureName : containedFeatureNames) { + containedFeatureList.add(new String(containedFeatureName)); } } } diff --git a/tests/de.ovgu.featureide.fm.attributes-test/src/de/ovgu/featureide/fm/attributes/tests/TExtendedFeatureModel.java b/tests/de.ovgu.featureide.fm.attributes-test/src/de/ovgu/featureide/fm/attributes/tests/TExtendedFeatureModel.java index 19680c84a5..9d4ae83a98 100644 --- a/tests/de.ovgu.featureide.fm.attributes-test/src/de/ovgu/featureide/fm/attributes/tests/TExtendedFeatureModel.java +++ b/tests/de.ovgu.featureide.fm.attributes-test/src/de/ovgu/featureide/fm/attributes/tests/TExtendedFeatureModel.java @@ -116,7 +116,7 @@ private void classesUseDifferentMemory(Object o1, Object o2, int depth, String f assertEquals(class1, class2); if (!unequalFields.contains(fieldName)) { - assertEquals(o1, o2); + assertEquals("values for " + fieldName + " are not equal", o1, o2); } if (class1.isPrimitive() || wrapperClasses.contains(class1)) { @@ -124,7 +124,7 @@ private void classesUseDifferentMemory(Object o1, Object o2, int depth, String f } if (!sameFields.contains(fieldName)) { - assertNotSame(o1, o2); + assertNotSame("values for " + fieldName + " are the same", o1, o2); } ArrayList allFields = new ArrayList<>(); @@ -172,11 +172,15 @@ private void classesUseDifferentMemory(Object o1, Object o2, int depth, String f } assertFalse(iterator2.hasNext()); } else if (field.getType().isArray()) { - Object[] array1 = (Object[]) i1; - Object[] array2 = (Object[]) i2; - assertEquals(array1.length, array2.length); - for (int i = 0; i < array1.length; i++) { - classesUseDifferentMemory(array1[i], array2[i], depth + 1, childName + i, alreadyChecked); + if (i1 instanceof Object[]) { + Object[] array1 = (Object[]) i1; + Object[] array2 = (Object[]) i2; + assertEquals(array1.length, array2.length); + for (int i = 0; i < array1.length; i++) { + classesUseDifferentMemory(array1[i], array2[i], depth + 1, childName + i, alreadyChecked); + } + } else { + assertEquals(i1, i2); } } else { classesUseDifferentMemory(i1, i2, depth + 1, childName, alreadyChecked); From 8c27b23e6c7f6795e3543e79e791d8e783a574bd Mon Sep 17 00:00:00 2001 From: Sebastian Krieter Date: Tue, 7 Oct 2025 17:51:36 +0200 Subject: [PATCH 3/8] Fixes potential problems in slicing wrapper --- .../fm/core/job/SliceFeatureModel.java | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/SliceFeatureModel.java b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/SliceFeatureModel.java index d6a9b9b2a9..0c3bb7282b 100644 --- a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/SliceFeatureModel.java +++ b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/SliceFeatureModel.java @@ -37,7 +37,6 @@ import de.ovgu.featureide.fm.core.analysis.cnf.solver.SimpleSatSolver; import de.ovgu.featureide.fm.core.base.FeatureUtils; import de.ovgu.featureide.fm.core.base.IConstraint; -import de.ovgu.featureide.fm.core.base.IFeature; import de.ovgu.featureide.fm.core.base.IFeatureModel; import de.ovgu.featureide.fm.core.base.IFeatureModelFactory; import de.ovgu.featureide.fm.core.base.IFeatureStructure; @@ -58,11 +57,11 @@ public class SliceFeatureModel implements LongRunningMethod { private static final int GROUP_OR = 1, GROUP_AND = 2, GROUP_ALT = 3, GROUP_NO = 0; private final FeatureModelFormula formula; - private final Collection featureNames; - private final IFeatureModel featureModel; + private final Collection featuresToKeep, featuresToRemove; + private final IFeatureModel slicedFeatureModel; + private final IFeatureModelFactory factory; private final boolean useSlicing; - private IFeatureModel slicedFeatureModel; private boolean slicingNecesary; public SliceFeatureModel(IFeatureModel featureModel, Collection featureNames, boolean useSlicing) { @@ -70,15 +69,18 @@ public SliceFeatureModel(IFeatureModel featureModel, Collection featureN } public SliceFeatureModel(IFeatureModel featureModel, Collection featureNames, boolean useSlicing, boolean usePersistentFormula) { - if (usePersistentFormula) { - formula = FeatureModelManager.getInstance(featureModel).getPersistentFormula(); - this.featureModel = formula.getFeatureModel(); - } else { - formula = FeatureModelManager.getInstance(featureModel).getVariableFormula(); - this.featureModel = featureModel; - } + formula = usePersistentFormula // + ? FeatureModelManager.getInstance(featureModel).getPersistentFormula() // + : FeatureModelManager.getInstance(featureModel).getVariableFormula(); + + final IFeatureModel featureModelObject = formula.getFeatureModel(); + factory = FMFactoryManager.getInstance().getFactory(featureModelObject); + slicedFeatureModel = featureModelObject.clone(); + featuresToKeep = featureNames; + featuresToRemove = new HashSet<>(FeatureUtils.getFeatureNames(featureModelObject)); + featuresToRemove.removeAll(featuresToKeep); + this.useSlicing = useSlicing; - this.featureNames = featureNames; } @Override @@ -90,7 +92,7 @@ public IFeatureModel execute(IMonitor monitor) throws Exception { monitor.checkCancel(); final CNF slicedFeatureModelCNF = sliceFormula(monitor.subTask(80)); monitor.checkCancel(); - merge(FMFactoryManager.getInstance().getFactory(featureModel), slicedFeatureModelCNF, featureTree, monitor.subTask(18)); + merge(factory, slicedFeatureModelCNF, featureTree, monitor.subTask(18)); } return featureTree; @@ -98,19 +100,18 @@ public IFeatureModel execute(IMonitor monitor) throws Exception { private CNF sliceFormula(IMonitor monitor) { monitor.setTaskName("Slicing Feature Model Formula"); - final HashSet removeFeatures = new HashSet<>(FeatureUtils.getFeatureNames(featureModel)); - removeFeatures.removeAll(featureNames); - return LongRunningWrapper.runMethod(new CNFSlicer(formula.getCNF(), removeFeatures), monitor.subTask(1)); + return LongRunningWrapper.runMethod(new CNFSlicer(formula.getCNF(), featuresToRemove), monitor.subTask(1)); } private IFeatureModel sliceTree(IMonitor monitor) { monitor.setTaskName("Slicing Feature Tree"); monitor.setRemainingWork(2); slicingNecesary = false; - slicedFeatureModel = featureModel.clone(); IFeatureStructure root = slicedFeatureModel.getStructure().getRoot(); + final List constraints = new ArrayList<>(slicedFeatureModel.getConstraints()); slicedFeatureModel.reset(); + postOrderProcessing(root); if (isToBeRemoved(root)) { if ((root.getChildrenCount() == 1) && root.getFirstChild().isMandatory()) { @@ -126,17 +127,9 @@ private IFeatureModel sliceTree(IMonitor monitor) { slicedFeatureModel.getStructure().setRoot(root); monitor.step(); - for (final IConstraint constaint : featureModel.getConstraints()) { - final Collection containedFeatures = constaint.getContainedFeatures(); - boolean containsOnlyRemainingFeatures = !containedFeatures.isEmpty(); - for (final IFeature feature : containedFeatures) { - if (!featureNames.contains(feature.getName())) { - containsOnlyRemainingFeatures = false; - break; - } - } - if (containsOnlyRemainingFeatures) { - slicedFeatureModel.addConstraint(constaint); + for (final IConstraint constraint : constraints) { + if (featuresToKeep.containsAll(constraint.getNode().getContainedFeatures())) { + slicedFeatureModel.addConstraint(constraint); } else { slicingNecesary = true; } @@ -311,7 +304,7 @@ private void toAnd(final IFeatureStructure parent) { } private boolean isToBeRemoved(final IFeatureStructure feat) { - return !featureNames.contains(feat.getFeature().getName()); + return !featuresToKeep.contains(feat.getFeature().getName()); } } From ea5f7c34024ba8ec83204043181d9f592fbfae1e Mon Sep 17 00:00:00 2001 From: Sebastian Krieter Date: Wed, 8 Oct 2025 13:52:57 +0200 Subject: [PATCH 4/8] Revises constructor of SliceFeatureModel wrapper --- .../de/ovgu/featureide/core/mpl/MPLPlugin.java | 2 +- .../fm/core/job/SliceFeatureModel.java | 16 ++++++---------- .../operations/DeleteSlicingOperation.java | 2 +- .../ui/handlers/FeatureModelSlicingHandler.java | 2 +- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/plugins/de.ovgu.featureide.core.mpl/src/de/ovgu/featureide/core/mpl/MPLPlugin.java b/plugins/de.ovgu.featureide.core.mpl/src/de/ovgu/featureide/core/mpl/MPLPlugin.java index 223c58a7c4..b1ba5c32f4 100644 --- a/plugins/de.ovgu.featureide.core.mpl/src/de/ovgu/featureide/core/mpl/MPLPlugin.java +++ b/plugins/de.ovgu.featureide.core.mpl/src/de/ovgu/featureide/core/mpl/MPLPlugin.java @@ -353,7 +353,7 @@ public void printStatistics(LinkedList projects, String folder) { public void createInterface(IProject mplProject, IFeatureProject featureProject, Collection featureNames) { final ArrayList> arguments = new ArrayList<>(1); - arguments.add(new SliceFeatureModel(featureProject.getFeatureModel(), featureNames, true)); + arguments.add(new SliceFeatureModel(featureProject.getFeatureModelManager().getPersistentFormula(), featureNames, true)); FMCorePlugin.startJobs(arguments, StringTable.CREATE_INTERFACE, true); } diff --git a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/SliceFeatureModel.java b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/SliceFeatureModel.java index 0c3bb7282b..b4fe9d2fa3 100644 --- a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/SliceFeatureModel.java +++ b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/SliceFeatureModel.java @@ -42,12 +42,11 @@ import de.ovgu.featureide.fm.core.base.IFeatureStructure; import de.ovgu.featureide.fm.core.base.impl.FMFactoryManager; import de.ovgu.featureide.fm.core.base.impl.FeatureModel; -import de.ovgu.featureide.fm.core.io.manager.FeatureModelManager; import de.ovgu.featureide.fm.core.job.monitor.IMonitor; import de.ovgu.featureide.fm.core.localization.StringTable; /** - * Create mpl interfaces. + * Slices a feature model while preserving as much of its hierarchy and cross-tree constrains as possible. * * @author Sebastian Krieter * @author Marcus Pinnecke (Feature Interface) @@ -56,8 +55,8 @@ public class SliceFeatureModel implements LongRunningMethod { private static final int GROUP_OR = 1, GROUP_AND = 2, GROUP_ALT = 3, GROUP_NO = 0; - private final FeatureModelFormula formula; private final Collection featuresToKeep, featuresToRemove; + private final CNF cnfFormula; private final IFeatureModel slicedFeatureModel; private final IFeatureModelFactory factory; private final boolean useSlicing; @@ -65,17 +64,14 @@ public class SliceFeatureModel implements LongRunningMethod { private boolean slicingNecesary; public SliceFeatureModel(IFeatureModel featureModel, Collection featureNames, boolean useSlicing) { - this(featureModel, featureNames, useSlicing, true); + this(new FeatureModelFormula(featureModel), featureNames, useSlicing); } - public SliceFeatureModel(IFeatureModel featureModel, Collection featureNames, boolean useSlicing, boolean usePersistentFormula) { - formula = usePersistentFormula // - ? FeatureModelManager.getInstance(featureModel).getPersistentFormula() // - : FeatureModelManager.getInstance(featureModel).getVariableFormula(); - + public SliceFeatureModel(FeatureModelFormula formula, Collection featureNames, boolean useSlicing) { final IFeatureModel featureModelObject = formula.getFeatureModel(); factory = FMFactoryManager.getInstance().getFactory(featureModelObject); slicedFeatureModel = featureModelObject.clone(); + cnfFormula = formula.getCNF(); featuresToKeep = featureNames; featuresToRemove = new HashSet<>(FeatureUtils.getFeatureNames(featureModelObject)); featuresToRemove.removeAll(featuresToKeep); @@ -100,7 +96,7 @@ public IFeatureModel execute(IMonitor monitor) throws Exception { private CNF sliceFormula(IMonitor monitor) { monitor.setTaskName("Slicing Feature Model Formula"); - return LongRunningWrapper.runMethod(new CNFSlicer(formula.getCNF(), featuresToRemove), monitor.subTask(1)); + return LongRunningWrapper.runMethod(new CNFSlicer(cnfFormula, featuresToRemove), monitor.subTask(1)); } private IFeatureModel sliceTree(IMonitor monitor) { diff --git a/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/editors/featuremodel/operations/DeleteSlicingOperation.java b/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/editors/featuremodel/operations/DeleteSlicingOperation.java index f332f00236..cfd6b83685 100644 --- a/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/editors/featuremodel/operations/DeleteSlicingOperation.java +++ b/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/editors/featuremodel/operations/DeleteSlicingOperation.java @@ -71,7 +71,7 @@ protected FeatureIDEEvent operation(IFeatureModel featureModel) { oldModel = featureModel.clone(); - final LongRunningMethod method = new SliceFeatureModel(featureModel, notSelectedFeatureNames, useSlicing, false); + final LongRunningMethod method = new SliceFeatureModel(featureModel, notSelectedFeatureNames, useSlicing); final IFeatureModel slicingModel = LongRunningWrapper.runMethod(method); replaceFeatureModel(featureModel, slicingModel); diff --git a/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/handlers/FeatureModelSlicingHandler.java b/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/handlers/FeatureModelSlicingHandler.java index dcac70bab5..375ea372e1 100644 --- a/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/handlers/FeatureModelSlicingHandler.java +++ b/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/handlers/FeatureModelSlicingHandler.java @@ -57,7 +57,7 @@ protected void singleAction(final IFile file) { wizard.putData(WizardConstants.KEY_IN_FEATUREMODEL, featureModel); if (Window.OK == new WizardDialog(Display.getCurrent().getActiveShell(), wizard).open()) { final Collection selectedFeatures = (Collection) wizard.getData(WizardConstants.KEY_OUT_FEATURES); - final LongRunningMethod method = new SliceFeatureModel(featureModel, selectedFeatures, true); + final LongRunningMethod method = new SliceFeatureModel(manager.getPersistentFormula(), selectedFeatures, true); final IRunner runner = LongRunningWrapper.getRunner(method, "Slicing Feature Model"); runner.addJobFinishedListener(finishedJob -> save(finishedJob, file, format)); From 4e28a0bed5f28c296d9394cf81c7d1e0520e0c13 Mon Sep 17 00:00:00 2001 From: Sebastian Krieter Date: Mon, 27 Oct 2025 14:31:11 +0100 Subject: [PATCH 5/8] Adapts test launch files Now uses Java 17 as execution environment --- .../de.ovgu.featureide.core.ahead-test.launch | 32 +++++++++++-------- ...e.ovgu.featureide.core.antenna-test.launch | 32 +++++++++++-------- ...e.ovgu.featureide.core.aspectj-test.launch | 32 +++++++++++-------- ...u.featureide.core.featurehouse-test.launch | 32 +++++++++++-------- .../de.ovgu.featureide.core.munge-test.launch | 32 +++++++++++-------- ....ovgu.featureide.fm.attributes-test.launch | 32 +++++++++++-------- .../de.ovgu.featureide.fm.core-test.launch | 32 +++++++++++-------- .../de.ovgu.featureide.fm.ui-test.launch | 32 +++++++++++-------- .../de.ovgu.featureide.ui-test.launch | 32 +++++++++++-------- 9 files changed, 162 insertions(+), 126 deletions(-) diff --git a/tests/de.ovgu.featureide.core.ahead-test/de.ovgu.featureide.core.ahead-test.launch b/tests/de.ovgu.featureide.core.ahead-test/de.ovgu.featureide.core.ahead-test.launch index 8719af7b31..91880f050f 100644 --- a/tests/de.ovgu.featureide.core.ahead-test/de.ovgu.featureide.core.ahead-test.launch +++ b/tests/de.ovgu.featureide.core.ahead-test/de.ovgu.featureide.core.ahead-test.launch @@ -1,17 +1,21 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/tests/de.ovgu.featureide.core.antenna-test/de.ovgu.featureide.core.antenna-test.launch b/tests/de.ovgu.featureide.core.antenna-test/de.ovgu.featureide.core.antenna-test.launch index 272311c807..cb01664e1e 100644 --- a/tests/de.ovgu.featureide.core.antenna-test/de.ovgu.featureide.core.antenna-test.launch +++ b/tests/de.ovgu.featureide.core.antenna-test/de.ovgu.featureide.core.antenna-test.launch @@ -1,17 +1,21 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/tests/de.ovgu.featureide.core.aspectj-test/de.ovgu.featureide.core.aspectj-test.launch b/tests/de.ovgu.featureide.core.aspectj-test/de.ovgu.featureide.core.aspectj-test.launch index 6b0d5da3f8..ba38f0fe5c 100644 --- a/tests/de.ovgu.featureide.core.aspectj-test/de.ovgu.featureide.core.aspectj-test.launch +++ b/tests/de.ovgu.featureide.core.aspectj-test/de.ovgu.featureide.core.aspectj-test.launch @@ -1,17 +1,21 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/tests/de.ovgu.featureide.core.featurehouse-test/de.ovgu.featureide.core.featurehouse-test.launch b/tests/de.ovgu.featureide.core.featurehouse-test/de.ovgu.featureide.core.featurehouse-test.launch index 7d78b2cf76..0d0bc2156c 100644 --- a/tests/de.ovgu.featureide.core.featurehouse-test/de.ovgu.featureide.core.featurehouse-test.launch +++ b/tests/de.ovgu.featureide.core.featurehouse-test/de.ovgu.featureide.core.featurehouse-test.launch @@ -1,17 +1,21 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/tests/de.ovgu.featureide.core.munge-test/de.ovgu.featureide.core.munge-test.launch b/tests/de.ovgu.featureide.core.munge-test/de.ovgu.featureide.core.munge-test.launch index 992677dd2c..f29e9c2f8b 100644 --- a/tests/de.ovgu.featureide.core.munge-test/de.ovgu.featureide.core.munge-test.launch +++ b/tests/de.ovgu.featureide.core.munge-test/de.ovgu.featureide.core.munge-test.launch @@ -1,17 +1,21 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/tests/de.ovgu.featureide.fm.attributes-test/de.ovgu.featureide.fm.attributes-test.launch b/tests/de.ovgu.featureide.fm.attributes-test/de.ovgu.featureide.fm.attributes-test.launch index cf89234faf..c885f3ff46 100644 --- a/tests/de.ovgu.featureide.fm.attributes-test/de.ovgu.featureide.fm.attributes-test.launch +++ b/tests/de.ovgu.featureide.fm.attributes-test/de.ovgu.featureide.fm.attributes-test.launch @@ -1,17 +1,21 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/tests/de.ovgu.featureide.fm.core-test/de.ovgu.featureide.fm.core-test.launch b/tests/de.ovgu.featureide.fm.core-test/de.ovgu.featureide.fm.core-test.launch index a4ae18bf3f..343b9a0e48 100644 --- a/tests/de.ovgu.featureide.fm.core-test/de.ovgu.featureide.fm.core-test.launch +++ b/tests/de.ovgu.featureide.fm.core-test/de.ovgu.featureide.fm.core-test.launch @@ -1,17 +1,21 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/tests/de.ovgu.featureide.fm.ui-test/de.ovgu.featureide.fm.ui-test.launch b/tests/de.ovgu.featureide.fm.ui-test/de.ovgu.featureide.fm.ui-test.launch index f9a8eadcf4..b00ac000ac 100644 --- a/tests/de.ovgu.featureide.fm.ui-test/de.ovgu.featureide.fm.ui-test.launch +++ b/tests/de.ovgu.featureide.fm.ui-test/de.ovgu.featureide.fm.ui-test.launch @@ -1,17 +1,21 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/tests/de.ovgu.featureide.ui-test/de.ovgu.featureide.ui-test.launch b/tests/de.ovgu.featureide.ui-test/de.ovgu.featureide.ui-test.launch index f5354c1408..059d3a6791 100644 --- a/tests/de.ovgu.featureide.ui-test/de.ovgu.featureide.ui-test.launch +++ b/tests/de.ovgu.featureide.ui-test/de.ovgu.featureide.ui-test.launch @@ -1,17 +1,21 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + From 5dc6c7bcca83235a2335cdc2555952a8b950535a Mon Sep 17 00:00:00 2001 From: Sebastian Krieter Date: Mon, 27 Oct 2025 14:56:37 +0100 Subject: [PATCH 6/8] Fixes #1509 --- .../featureide/fm/core/job/AbstractJob.java | 2 +- .../de/ovgu/featureide/fm/core/job/IJob.java | 2 +- .../fm/core/job/LongRunningJob.java | 5 +++++ .../fm/core/job/LongRunningThread.java | 21 +++++++++++++++++++ .../fm/ui/editors/FeatureDiagramEditor.java | 2 +- .../statistics/core/composite/LazyParent.java | 2 +- .../lazyimplementations/ConfigNode.java | 2 +- .../collaboration/CollaborationView.java | 4 ++-- 8 files changed, 33 insertions(+), 7 deletions(-) diff --git a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/AbstractJob.java b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/AbstractJob.java index 6e3b183a00..b4bf6d4933 100644 --- a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/AbstractJob.java +++ b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/AbstractJob.java @@ -86,7 +86,7 @@ public int hashCode() { protected AbstractJob(String name, int priority) { super(name); - setPriority(priority); + setJobPriority(priority); } @SuppressWarnings("rawtypes") diff --git a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/IJob.java b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/IJob.java index 23cf0e9612..d0b7896d03 100644 --- a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/IJob.java +++ b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/IJob.java @@ -99,7 +99,7 @@ public int getValue() { void schedule(); - void setPriority(int priority); + void setJobPriority(int priority); void setIntermediateFunction(Consumer intermediateFunction); diff --git a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/LongRunningJob.java b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/LongRunningJob.java index 3f8c21658b..e94eeca916 100644 --- a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/LongRunningJob.java +++ b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/LongRunningJob.java @@ -95,4 +95,9 @@ public void setTimeout(int timeout) { this.timeout = timeout; } + @Override + public void setJobPriority(int priority) { + setPriority(priority); + } + } diff --git a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/LongRunningThread.java b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/LongRunningThread.java index 95ff812db1..f3456c8152 100644 --- a/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/LongRunningThread.java +++ b/plugins/de.ovgu.featureide.fm.core/src/de/ovgu/featureide/fm/core/job/LongRunningThread.java @@ -24,6 +24,8 @@ import java.util.List; import java.util.function.Consumer; +import org.eclipse.core.runtime.jobs.Job; + import de.ovgu.featureide.fm.core.Logger; import de.ovgu.featureide.fm.core.job.monitor.IMonitor; import de.ovgu.featureide.fm.core.job.monitor.NullMonitor; @@ -165,4 +167,23 @@ public void setStoppable(boolean stoppable) { this.stoppable = stoppable; } + @Override + public void setJobPriority(int priority) { + switch (priority) { + case Job.INTERACTIVE: + setPriority(Thread.MAX_PRIORITY); + break; + case Job.SHORT: + setPriority(Thread.NORM_PRIORITY); + break; + case Job.LONG: + case Job.BUILD: + case Job.DECORATE: + setPriority(Thread.MIN_PRIORITY); + break; + default: + throw new IllegalArgumentException(String.valueOf(priority)); + } + } + } diff --git a/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/editors/FeatureDiagramEditor.java b/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/editors/FeatureDiagramEditor.java index 5dd24eb2cc..5b4405a153 100644 --- a/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/editors/FeatureDiagramEditor.java +++ b/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/editors/FeatureDiagramEditor.java @@ -580,7 +580,7 @@ public Boolean execute(IMonitor monitor) throws Exception { return true; } }, ANALYZE_FEATURE_MODEL); - analyzeJob.setPriority(Job.LONG); + analyzeJob.setJobPriority(Job.LONG); LongRunningWrapper.startJob(analysisToken, analyzeJob); } diff --git a/plugins/de.ovgu.featureide.ui/src/de/ovgu/featureide/ui/statistics/core/composite/LazyParent.java b/plugins/de.ovgu.featureide.ui/src/de/ovgu/featureide/ui/statistics/core/composite/LazyParent.java index 7f864f298c..7b3fab92ab 100644 --- a/plugins/de.ovgu.featureide.ui/src/de/ovgu/featureide/ui/statistics/core/composite/LazyParent.java +++ b/plugins/de.ovgu.featureide.ui/src/de/ovgu/featureide/ui/statistics/core/composite/LazyParent.java @@ -87,7 +87,7 @@ protected Parent[] calculateChidren(boolean expand) { if (lazy) { final TreeJob job = new StatisticTreeJob(this, expand); final IRunner runner = LongRunningWrapper.getRunner(job, CALCULATE + this.getClass().getName()); - runner.setPriority(Job.SHORT); + runner.setJobPriority(Job.SHORT); if (runner instanceof LongRunningJob) { ((LongRunningJob) runner).addJobChangeListener(JobDoneListener.getInstance()); } diff --git a/plugins/de.ovgu.featureide.ui/src/de/ovgu/featureide/ui/statistics/core/composite/lazyimplementations/ConfigNode.java b/plugins/de.ovgu.featureide.ui/src/de/ovgu/featureide/ui/statistics/core/composite/lazyimplementations/ConfigNode.java index ec83d50165..ff4d860421 100644 --- a/plugins/de.ovgu.featureide.ui/src/de/ovgu/featureide/ui/statistics/core/composite/lazyimplementations/ConfigNode.java +++ b/plugins/de.ovgu.featureide.ui/src/de/ovgu/featureide/ui/statistics/core/composite/lazyimplementations/ConfigNode.java @@ -81,7 +81,7 @@ public boolean cancel() { } }; final IRunner runner = LongRunningWrapper.getRunner(job, CALCULATING + description); - runner.setPriority(priority); + runner.setJobPriority(priority); if (runner instanceof LongRunningJob) { ((LongRunningJob) runner).addJobChangeListener(JobDoneListener.getInstance()); } diff --git a/plugins/de.ovgu.featureide.ui/src/de/ovgu/featureide/ui/views/collaboration/CollaborationView.java b/plugins/de.ovgu.featureide.ui/src/de/ovgu/featureide/ui/views/collaboration/CollaborationView.java index 7230ea6848..20c3d454cb 100644 --- a/plugins/de.ovgu.featureide.ui/src/de/ovgu/featureide/ui/views/collaboration/CollaborationView.java +++ b/plugins/de.ovgu.featureide.ui/src/de/ovgu/featureide/ui/views/collaboration/CollaborationView.java @@ -847,7 +847,7 @@ public Boolean execute(IMonitor workMonitor) throws Exception { } }; final IRunner runner = LongRunningWrapper.getRunner(job, REFRESH_COLLABORATION_VIEW); - runner.setPriority(Job.SHORT); + runner.setJobPriority(Job.SHORT); runner.schedule(); } }; @@ -867,7 +867,7 @@ public void updateGuiAfterBuild(final IFeatureProject project, final IFile confi configurations.add(configurationFile); } final IRunner updateGUIRunner = LongRunningWrapper.getRunner(updateGUIMethod, UPDATE_COLLABORATION_VIEW); - updateGUIRunner.setPriority(Job.LONG); + updateGUIRunner.setJobPriority(Job.LONG); LongRunningWrapper.startJob(updateGuiToken, updateGUIRunner); } } From 4e24d3b4edf5e004fc4ba1be42fdfa0a9ac87204 Mon Sep 17 00:00:00 2001 From: Malte Grave Date: Tue, 7 Oct 2025 14:54:34 +0200 Subject: [PATCH 7/8] fix: Resolve the Images in the icon folder correctly Fixes #1508 --- .../de.ovgu.featureide.fm.ui/META-INF/MANIFEST.MF | 1 + .../src/de/ovgu/featureide/fm/ui/FMUIPlugin.java | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/plugins/de.ovgu.featureide.fm.ui/META-INF/MANIFEST.MF b/plugins/de.ovgu.featureide.fm.ui/META-INF/MANIFEST.MF index 5b9dca0883..cc1e0e9cc4 100644 --- a/plugins/de.ovgu.featureide.fm.ui/META-INF/MANIFEST.MF +++ b/plugins/de.ovgu.featureide.fm.ui/META-INF/MANIFEST.MF @@ -78,3 +78,4 @@ Export-Package: de.ovgu.featureide.fm.ui, Bundle-ClassPath: ., lib/org.abego.treelayout.core-1.0.3.jar Automatic-Module-Name: de.ovgu.featureide.fm.ui +Import-Package: org.eclipse.core.runtime diff --git a/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/FMUIPlugin.java b/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/FMUIPlugin.java index 78b411d35a..bef63dac95 100644 --- a/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/FMUIPlugin.java +++ b/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/FMUIPlugin.java @@ -22,6 +22,7 @@ import static de.ovgu.featureide.fm.core.localization.StringTable.SELECT_THE_FEATURE_MODEL_FOR_THE_CURRENT_PROJECT; +import java.net.URL; import java.util.Arrays; import java.util.Optional; @@ -30,6 +31,12 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Shell; @@ -83,7 +90,13 @@ public static FMUIPlugin getDefault() { } public static Image getImage(String name) { - return getDefault().getImageDescriptor("icons/" + name).createImage(); + final URL url = FileLocator.find(Platform.getBundle(PLUGIN_ID), new Path("icons/" + name), null); + if (url != null) { + return ImageDescriptor.createFromURL(url).createImage(); + } else { + getDefault().getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, "Image not found: " + name)); + return null; + } } /** From 367dfcda85c5b0e25dcc14cc5d2be18e72a7d413 Mon Sep 17 00:00:00 2001 From: Malte Grave Date: Wed, 24 Sep 2025 10:22:33 +0200 Subject: [PATCH 8/8] fix: Reworked resource handling for the ToolBarMenuManager Fixes #1507 --- .../fm/ui/editors/ToolBarMenuManager.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/editors/ToolBarMenuManager.java b/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/editors/ToolBarMenuManager.java index cf07892de3..62ffa62085 100644 --- a/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/editors/ToolBarMenuManager.java +++ b/plugins/de.ovgu.featureide.fm.ui/src/de/ovgu/featureide/fm/ui/editors/ToolBarMenuManager.java @@ -1,5 +1,6 @@ /* FeatureIDE - A Framework for Feature-Oriented Software Development * Copyright (C) 2005-2019 FeatureIDE team, University of Magdeburg, Germany + * 2025 Malte Grave, VaSiCS, LIT CPS Lab, Johannes Kepler University, Linz * * This file is part of FeatureIDE. * @@ -38,26 +39,38 @@ * @author Jonas Weigt * @author Christian Harnisch * @author Marcus Pinnecke + * @author Malte Grave */ public class ToolBarMenuManager extends MenuManager { - private Image image; + private ImageDescriptor imageDescriptor; public ToolBarMenuManager(String text) { super(text); } - public ToolBarMenuManager(String text, ImageDescriptor image, String id) { - super(text, image, id); - this.image = image.createImage(); + public ToolBarMenuManager(String text, ImageDescriptor imageDescriptor, String id) { + super(text, imageDescriptor, id); + this.imageDescriptor = imageDescriptor; } @Override public void fill(final ToolBar toolbar, int index) { final ToolItem toolItem = (index >= 0) ? new ToolItem(toolbar, SWT.DROP_DOWN, index) : new ToolItem(toolbar, SWT.DROP_DOWN); toolItem.setText(getMenuText()); - toolItem.setImage(image); + + if (imageDescriptor != null) { + final Image img = imageDescriptor.createImage(); + toolItem.setImage(img); + + toolItem.addDisposeListener(e -> { + if ((img != null) && !img.isDisposed()) { + img.dispose(); + } + }); + } + toolItem.addSelectionListener(new SelectionListener() { @Override @@ -73,5 +86,4 @@ public void widgetSelected(SelectionEvent e) { public void widgetDefaultSelected(SelectionEvent e) {} }); } - }