Skip to content

Commit

Permalink
Clarify composite component creation and fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
FloBoJa committed Nov 22, 2023
1 parent 3d5e390 commit cbaf730
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public Optional<CompilationUnit> compilationUnit() {
public String name() {
return compUnitOrName.name();
}

public CompUnitOrName identifier() {
return compUnitOrName;
}

@Override
public int hashCode() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Component> components, Requirements compositeRequirements,
public Composite construct(Collection<Component> freeComponents, Requirements compositeRequirements,
Provisions compositeProvisions, Collection<OperationInterface> visibleProvisions) {
Logger.getLogger(getClass())
.warn("Constructing composite component " + name);
Expand All @@ -47,7 +51,7 @@ public Composite construct(Collection<Component> components, Requirements compos
.map(x -> x.create(allDependencies, visibleProvisions))
.collect(Collectors.toSet());

Set<Component> remainingComponents = new HashSet<>(components);
Set<Component> remainingComponents = new HashSet<>(freeComponents);
remainingComponents.removeAll(parts);
Set<OperationInterface> internalInterfaces = new HashSet<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <T> only for ease of calling
*/
public static <T extends OperationInterface> Map<OperationInterface, List<OperationInterface>> groupDependencies(
Collection<T> dependencies, Collection<OperationInterface> allDependencies) {
Map<OperationInterface, List<OperationInterface>> groupedDependencies = new HashMap<>();
Queue<OperationInterface> 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<String> commonInterfaceName = requirement.getName()
Optional<String> commonInterfaceName = grouplessDependency.getName()
.getCommonInterface(rootInterface.getName());
boolean containsOtherDependency = false;

Expand All @@ -59,16 +65,18 @@ public static <T extends OperationInterface> Map<OperationInterface, List<Operat
if (!containsOtherDependency) {
// De-duplicate interfaces.
Set<OperationInterface> 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;
}
}
}
if (isRoot) {
groupedDependencies.put(requirement, new LinkedList<>());
groupedDependencies.put(grouplessDependency, new LinkedList<>());
groupedDependencies.get(grouplessDependency).add(grouplessDependency);
}
}
return groupedDependencies;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -17,6 +18,8 @@ public class PCMDetectionResult {
public PCMDetectionResult(Map<CompUnitOrName, ComponentBuilder> components,
Map<String, CompositeBuilder> composites, ProvisionsBuilder compositeProvisions,
RequirementsBuilder compositeRequirements) {

Map<CompUnitOrName, ComponentBuilder> freeComponents = PCMDetectionResult.filterFreeComponents(components, composites.values());

// Collect globally visible provisions
Set<Component> temporaryComponents = PCMDetectionResult.createComponents(components, compositeProvisions,
Expand All @@ -25,23 +28,40 @@ public PCMDetectionResult(Map<CompUnitOrName, ComponentBuilder> components,
composites, compositeProvisions, compositeRequirements, Set.of());
Set<OperationInterface> 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<CompUnitOrName, ComponentBuilder> filterFreeComponents(Map<CompUnitOrName, ComponentBuilder> components, Collection<CompositeBuilder> composites) {
Set<CompUnitOrName> 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<Component> createComponents(Map<CompUnitOrName, ComponentBuilder> components,
ProvisionsBuilder compositeProvisions, RequirementsBuilder compositeRequirements,
Set<OperationInterface> visibleProvisions) {
List<OperationInterface> 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());

Expand All @@ -51,15 +71,15 @@ private static Set<Component> createComponents(Map<CompUnitOrName, ComponentBuil
.collect(Collectors.toSet());
}

private static Set<Composite> createCompositeComponents(Set<Component> components,
private static Set<Composite> createCompositeComponents(Set<Component> freeComponents,
Map<String, CompositeBuilder> composites, ProvisionsBuilder compositeProvisions,
RequirementsBuilder compositeRequirements, Set<OperationInterface> visibleProvisions) {

// Construct composites.
Set<Composite> constructedComposites = new HashSet<>();
List<Composite> 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());

Expand Down Expand Up @@ -117,6 +137,19 @@ private static Set<OperationInterface> collectVisibleProvisions(Set<Component> c

return provisions;
}

private static void removeBoundComponents(Map<CompUnitOrName, ComponentBuilder> freeComponents, Set<Composite> composites) {
Set<CompUnitOrName> boundComponents = composites
.stream()
.flatMap(composite -> composite
.parts()
.stream())
.map(part -> part.identifier())
.collect(Collectors.toSet());
for (CompUnitOrName identifier : boundComponents) {
freeComponents.remove(identifier);
}
}

private Map<OperationInterface, List<Operation>> createOperationInterfaces() {
// TODO: This has to include composite interfaces as well
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<OperationInterface> allDependencies = List.of(provision, requirement);
List<OperationInterface> 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");
Expand Down Expand Up @@ -83,9 +87,13 @@ void exposingSingletonComposite() {

CompositeBuilder compositeBuilder = new CompositeBuilder("CompositeComponent");
compositeBuilder.addPart(componentBuilder);

List<OperationInterface> allDependencies = List.of(provision, requirement);
List<OperationInterface> 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");
Expand Down Expand Up @@ -119,9 +127,11 @@ void twoComponentComposite() {
compositeBuilder.addPart(componentBuilder2);

List<OperationInterface> allDependencies = List.of(provision1, provision2, requirement1, requirement2);
List<OperationInterface> 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");
Expand Down Expand Up @@ -160,9 +170,11 @@ void overlappingTwoComponentComposite() {

List<OperationInterface> allDependencies = List.of(provision, requirement, additionalRequirement1,
additionalRequirement2);
List<OperationInterface> 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");
Expand All @@ -174,18 +186,23 @@ 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()
.add(provision);

CompositeBuilder compositeBuilder = new CompositeBuilder("CompositeComponent");
compositeBuilder.addPart(componentBuilder);
OperationInterface provisionInterface = new EntireInterface(new JavaInterfaceName("Interface"));

List<OperationInterface> allDependencies = List.of(provision);
List<OperationInterface> 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");
Expand Down

0 comments on commit cbaf730

Please sign in to comment.