diff --git a/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/Component.java b/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/Component.java index 902cbb0f..5a68a9ff 100644 --- a/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/Component.java +++ b/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/Component.java @@ -37,6 +37,10 @@ public Optional compilationUnit() { public String name() { return compUnitOrName.name(); } + + public CompUnitOrName identifier() { + return compUnitOrName; + } @Override public int hashCode() { diff --git a/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/ComponentBuilder.java b/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/ComponentBuilder.java index 75ba7bab..69295ea3 100644 --- a/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/ComponentBuilder.java +++ b/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/ComponentBuilder.java @@ -19,6 +19,10 @@ public ComponentBuilder(CompUnitOrName compUnitOrName) { this.requirements = new RequirementsBuilder(); this.provisions = new ProvisionsBuilder(); } + + public CompUnitOrName identifier() { + return compUnitOrName; + } public RequirementsBuilder requirements() { return requirements; diff --git a/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/CompositeBuilder.java b/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/CompositeBuilder.java index 1281f227..15e486d3 100644 --- a/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/CompositeBuilder.java +++ b/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/CompositeBuilder.java @@ -28,8 +28,12 @@ public CompositeBuilder(String name) { public void addPart(ComponentBuilder componentBuilder) { explicitParts.add(componentBuilder); } + + public boolean hasPart(CompUnitOrName identifier) { + return explicitParts.stream().anyMatch(part -> part.identifier().equals(identifier)); + } - public Composite construct(Collection components, Requirements compositeRequirements, + public Composite construct(Collection freeComponents, Requirements compositeRequirements, Provisions compositeProvisions, Collection visibleProvisions) { Logger.getLogger(getClass()) .warn("Constructing composite component " + name); @@ -47,7 +51,7 @@ public Composite construct(Collection components, Requirements compos .map(x -> x.create(allDependencies, visibleProvisions)) .collect(Collectors.toSet()); - Set remainingComponents = new HashSet<>(components); + Set remainingComponents = new HashSet<>(freeComponents); remainingComponents.removeAll(parts); Set internalInterfaces = new HashSet<>(); diff --git a/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/DependencyUtils.java b/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/DependencyUtils.java index f5382980..a23b71a3 100644 --- a/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/DependencyUtils.java +++ b/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/DependencyUtils.java @@ -18,25 +18,31 @@ private DependencyUtils() throws IllegalAccessException { throw new IllegalAccessException(); } + /** + * Group all dependencies in {@code dependencies} by finding their common ancestors. + * Ensure that no other dependency (not in {@code dependencies}, but in {@code allDependencies}) is included into a group by accident. + * + * @param only for ease of calling + */ public static Map> groupDependencies( Collection dependencies, Collection allDependencies) { Map> groupedDependencies = new HashMap<>(); Queue sortedDependencies = new PriorityQueue<>(dependencies); while (!sortedDependencies.isEmpty()) { - OperationInterface requirement = sortedDependencies.poll(); + OperationInterface grouplessDependency = sortedDependencies.poll(); boolean isRoot = true; for (OperationInterface rootInterface : groupedDependencies.keySet()) { - if (requirement.isPartOf(rootInterface)) { + if (grouplessDependency.isPartOf(rootInterface)) { groupedDependencies.get(rootInterface) - .add(requirement); + .add(grouplessDependency); isRoot = false; break; } } if (isRoot) { for (OperationInterface rootInterface : groupedDependencies.keySet()) { - Optional commonInterfaceName = requirement.getName() + Optional commonInterfaceName = grouplessDependency.getName() .getCommonInterface(rootInterface.getName()); boolean containsOtherDependency = false; @@ -59,8 +65,9 @@ public static Map interfaces = new HashSet<>(groupedDependencies.remove(rootInterface)); + interfaces.add(commonInterface); interfaces.add(rootInterface); - interfaces.add(requirement); + interfaces.add(grouplessDependency); groupedDependencies.put(commonInterface, new ArrayList<>(interfaces)); isRoot = false; break; @@ -68,7 +75,8 @@ public static Map()); + groupedDependencies.put(grouplessDependency, new LinkedList<>()); + groupedDependencies.get(grouplessDependency).add(grouplessDependency); } } return groupedDependencies; diff --git a/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/PCMDetectionResult.java b/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/PCMDetectionResult.java index c3bccde7..413f36c7 100644 --- a/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/PCMDetectionResult.java +++ b/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/model/PCMDetectionResult.java @@ -1,5 +1,6 @@ package org.palladiosimulator.somox.analyzer.rules.model; +import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -17,6 +18,8 @@ public class PCMDetectionResult { public PCMDetectionResult(Map components, Map composites, ProvisionsBuilder compositeProvisions, RequirementsBuilder compositeRequirements) { + + Map freeComponents = PCMDetectionResult.filterFreeComponents(components, composites.values()); // Collect globally visible provisions Set temporaryComponents = PCMDetectionResult.createComponents(components, compositeProvisions, @@ -25,23 +28,40 @@ public PCMDetectionResult(Map components, composites, compositeProvisions, compositeRequirements, Set.of()); Set visibleProvisions = PCMDetectionResult.collectVisibleProvisions(temporaryComponents, temporaryComposites); + + PCMDetectionResult.removeBoundComponents(components, temporaryComposites); // TODO: Do not rebuild everything, that is theoretically not necessary since provisions do // not change. // Construct final result - this.components = PCMDetectionResult.createComponents(components, compositeProvisions, compositeRequirements, + this.components = PCMDetectionResult.createComponents(freeComponents, compositeProvisions, compositeRequirements, visibleProvisions); this.composites = PCMDetectionResult.createCompositeComponents(this.components, composites, compositeProvisions, compositeRequirements, visibleProvisions); this.operationInterfaces = createOperationInterfaces(); } + + private static Map filterFreeComponents(Map components, Collection composites) { + Set boundComponents = new HashSet<>(); + for (CompUnitOrName identifier : components.keySet()) { + if (composites + .stream() + .anyMatch(composite -> composite.hasPart(identifier))) { + boundComponents.add(identifier); + } + } + return components.entrySet() + .stream() + .filter(entry -> !boundComponents.contains(entry.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } private static Set createComponents(Map components, ProvisionsBuilder compositeProvisions, RequirementsBuilder compositeRequirements, Set visibleProvisions) { List allDependencies = new LinkedList<>(); - // TODO: Aren't the dependencies of bare components missing here? Is that alright? + // TODO: Aren't the dependencies of free components missing here? Is that alright? allDependencies.addAll(compositeRequirements.toList()); allDependencies.addAll(compositeProvisions.toList()); @@ -51,7 +71,7 @@ private static Set createComponents(Map createCompositeComponents(Set components, + private static Set createCompositeComponents(Set freeComponents, Map composites, ProvisionsBuilder compositeProvisions, RequirementsBuilder compositeRequirements, Set visibleProvisions) { @@ -59,7 +79,7 @@ private static Set createCompositeComponents(Set component Set constructedComposites = new HashSet<>(); List allComposites = composites.values() .stream() - .map(x -> x.construct(components, compositeRequirements.create(List.of(), visibleProvisions), + .map(x -> x.construct(freeComponents, compositeRequirements.create(List.of(), visibleProvisions), compositeProvisions.create(List.of()), visibleProvisions)) .collect(Collectors.toList()); @@ -117,6 +137,19 @@ private static Set collectVisibleProvisions(Set c return provisions; } + + private static void removeBoundComponents(Map freeComponents, Set composites) { + Set boundComponents = composites + .stream() + .flatMap(composite -> composite + .parts() + .stream()) + .map(part -> part.identifier()) + .collect(Collectors.toSet()); + for (CompUnitOrName identifier : boundComponents) { + freeComponents.remove(identifier); + } + } private Map> createOperationInterfaces() { // TODO: This has to include composite interfaces as well diff --git a/tests/org.palladiosimulator.somox.analyzer.rules.test/src/org/palladiosimulator/somox/analyzer/rules/test/model/CompositeTest.java b/tests/org.palladiosimulator.somox.analyzer.rules.test/src/org/palladiosimulator/somox/analyzer/rules/test/model/CompositeTest.java index 1222a7cc..89c9ae70 100644 --- a/tests/org.palladiosimulator.somox.analyzer.rules.test/src/org/palladiosimulator/somox/analyzer/rules/test/model/CompositeTest.java +++ b/tests/org.palladiosimulator.somox.analyzer.rules.test/src/org/palladiosimulator/somox/analyzer/rules/test/model/CompositeTest.java @@ -48,8 +48,12 @@ void singletonComposite() { CompositeBuilder compositeBuilder = new CompositeBuilder("CompositeComponent"); compositeBuilder.addPart(componentBuilder); - Composite result = compositeBuilder.construct(List.of(), new Requirements(List.of(), List.of(), List.of()), - new Provisions(List.of(), List.of(provision, requirement)), List.of(provision)); + + List allDependencies = List.of(provision, requirement); + List visibleProvisions = List.of(provision); + + Composite result = compositeBuilder.construct(List.of(), new Requirements(List.of(), allDependencies, visibleProvisions), + new Provisions(List.of(), allDependencies), visibleProvisions); assertEquals(1, result.parts() .size(), "this composite should have exactly one part"); @@ -83,9 +87,13 @@ void exposingSingletonComposite() { CompositeBuilder compositeBuilder = new CompositeBuilder("CompositeComponent"); compositeBuilder.addPart(componentBuilder); + + List allDependencies = List.of(provision, requirement); + List visibleProvisions = List.of(provision); + Composite result = compositeBuilder.construct(List.of(), - new Requirements(List.of(requirement), List.of(), List.of(provision)), - new Provisions(List.of(provision), List.of(provision, requirement)), List.of(provision)); + new Requirements(List.of(requirement), allDependencies, visibleProvisions), + new Provisions(List.of(provision), allDependencies), visibleProvisions); assertEquals(1, result.parts() .size(), "this composite should have exactly one part"); @@ -119,9 +127,11 @@ void twoComponentComposite() { compositeBuilder.addPart(componentBuilder2); List allDependencies = List.of(provision1, provision2, requirement1, requirement2); + List visibleProvisions = List.of(provision1, provision2); + Composite result = compositeBuilder.construct(List.of(), - new Requirements(List.of(requirement1, requirement2), allDependencies, List.of(provision1, provision2)), - new Provisions(List.of(provision1, provision2), allDependencies), List.of(provision1, provision2)); + new Requirements(List.of(requirement1, requirement2), allDependencies, visibleProvisions), + new Provisions(List.of(provision1, provision2), allDependencies), visibleProvisions); assertEquals(2, result.parts() .size(), "this composite should have exactly two parts"); @@ -160,9 +170,11 @@ void overlappingTwoComponentComposite() { List allDependencies = List.of(provision, requirement, additionalRequirement1, additionalRequirement2); + List visibleProvisions = List.of(provision); + Composite result = compositeBuilder.construct(List.of(), - new Requirements(List.of(requirement), allDependencies, List.of(provision)), - new Provisions(List.of(provision), allDependencies), List.of(provision)); + new Requirements(List.of(requirement), allDependencies, visibleProvisions), + new Provisions(List.of(provision), allDependencies), visibleProvisions); assertEquals(2, result.parts() .size(), "this composite should have exactly two parts"); @@ -174,7 +186,9 @@ void overlappingTwoComponentComposite() { @Test void impreciseExposure() { + // TODO: Re-think this test. OperationInterface provision = new Operation(null, new JavaOperationName("Interface", "providedMethod")); + OperationInterface impreciseProvision = new EntireInterface(new JavaInterfaceName("Interface")); ComponentBuilder componentBuilder = new ComponentBuilder(new CompUnitOrName("Component")); componentBuilder.provisions() @@ -182,10 +196,13 @@ void impreciseExposure() { CompositeBuilder compositeBuilder = new CompositeBuilder("CompositeComponent"); compositeBuilder.addPart(componentBuilder); - OperationInterface provisionInterface = new EntireInterface(new JavaInterfaceName("Interface")); + + List allDependencies = List.of(provision); + List visibleProvisions = List.of(provision); + Composite result = compositeBuilder.construct(List.of(), - new Requirements(List.of(), List.of(provision), List.of(provision)), - new Provisions(List.of(provisionInterface), List.of(provision)), List.of(provision)); + new Requirements(List.of(), allDependencies, visibleProvisions), + new Provisions(List.of(impreciseProvision), allDependencies), visibleProvisions); assertEquals(1, result.parts() .size(), "this composite should have exactly one part");