Skip to content

Commit

Permalink
GH-787 - Allow references between sibling sub-modules.
Browse files Browse the repository at this point in the history
Previously, we rejected references between sub-modules both contained in the same parent package.

Related ticket: GH-578.
  • Loading branch information
odrotbohm committed Aug 30, 2024
1 parent 803c698 commit b370d5a
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1201,18 +1201,17 @@ Violations isValidDependencyWithin(ApplicationModules modules) {

// Parent child relationships

return targetModule.getParentModule(modules)
.filter(it -> !it.equals(originModule))
.map(__ -> {

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);
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));

return violations.and(new Violation(violationText));
}

return violations;
}

ApplicationModule getExistingModuleOf(JavaClass javaClass, ApplicationModules modules) {
Expand Down Expand Up @@ -1347,6 +1346,27 @@ private static String createDescription(JavaMember codeUnit, JavaClass declaring
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
2 changes: 1 addition & 1 deletion src/docs/antora/modules/ROOT/pages/fundamentals.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit b370d5a

Please sign in to comment.