GH-787 - Allow references between sibling sub-modules.

Previously, we rejected references between sub-modules both contained in the same parent package.

Related ticket: GH-578.
This commit is contained in:
Oliver Drotbohm
2024-08-30 18:13:45 +02:00
parent 803c69835d
commit b370d5a3f2
3 changed files with 33 additions and 11 deletions

View File

@@ -1201,18 +1201,17 @@ public class ApplicationModule implements Comparable<ApplicationModule> {
// Parent child relationships
return targetModule.getParentModule(modules)
.filter(it -> !it.equals(originModule))
.map(__ -> {
if (!haveSameParentOrDirectParentRelationship(originModule, targetModule, modules)) {
var violationText = INVALID_SUB_MODULE_REFERENCE
.formatted(originModule.getName(), targetModule.getName(),
FormatableType.of(source).getAbbreviatedFullName(originModule),
FormatableType.of(target).getAbbreviatedFullName(targetModule));
var violationText = INVALID_SUB_MODULE_REFERENCE
.formatted(originModule.getName(), targetModule.getName(),
FormatableType.of(source).getAbbreviatedFullName(originModule),
FormatableType.of(target).getAbbreviatedFullName(targetModule));
return violations.and(new Violation(violationText));
})
.orElse(violations);
return violations.and(new Violation(violationText));
}
return violations;
}
ApplicationModule getExistingModuleOf(JavaClass javaClass, ApplicationModules modules) {
@@ -1347,6 +1346,27 @@ public class ApplicationModule implements Comparable<ApplicationModule> {
private static boolean isInjectionPoint(JavaMember unit) {
return INJECTION_TYPES.stream().anyMatch(type -> unit.isAnnotatedWith(type));
}
private static boolean haveSameParentOrDirectParentRelationship(ApplicationModule source, ApplicationModule target,
ApplicationModules modules) {
var sourceParent = modules.getParentOf(source);
var targetParent = modules.getParentOf(target);
// Top-level modules
return targetParent.isEmpty()
// One is parent of the other
|| hasValue(sourceParent, target)
|| hasValue(targetParent, source)
// Same immediate parent
|| sourceParent.flatMap(it -> targetParent.filter(it::equals)).isPresent();
}
private static <T> boolean hasValue(Optional<T> optional, T expected) {
return optional.filter(expected::equals).isPresent();
}
}
private static class InjectionDependency extends QualifiedDependency {

View File

@@ -16,10 +16,12 @@
package example.ni.nested.b.second;
import example.ni.nested.InNested;
import example.ni.nested.b.first.InNestedBFirst;
/**
* @author Oliver Drotbohm
*/
public class InNestedBSecond {
InNested inNested;
InNestedBFirst siblingReference;
}

View File

@@ -211,7 +211,7 @@ In this example `inventory` is an application module as described xref:fundament
The `@ApplicationModule` annotation on the `nested` package caused that to become a nested application module in turn.
In that arrangement, the following access rules apply:
* The code in _Nested_ is only available from _Inventory_, i.e. only any of the `SomethingInventoryInternal` types can access `NestedApi`. `NestedInternal` is only accessible from `NestedApi`.
* The code in _Nested_ is only available from _Inventory_ or any types exposed by sibling application modules nested inside _Inventory_.
* Any code in the _Nested_ module can access code in parent modules, even internal.
I.e., both `NestedApi` and `NestedInternal` can access `inventory.internal.SomethingInventoryInternal`.
* Code from nested modules can also access exposed types by top-level application modules.