Skip to content

Commit

Permalink
Add component type of arrays to JavaClass dependencies from self
Browse files Browse the repository at this point in the history
This will apply recursively for multi-dimensional arrays, e.g. for a class `Bar` depending on `Foo[][][]` we would consider `Bar` to have dependencies to `Foo[][][]`, `Foo[][]`, `Foo[]` and `Foo`.

Issue: #255

Signed-off-by: Johannes Wengert <johannes.wengert@googlemail.com>
Signed-off-by: Peter Gafert <peter.gafert@tngtech.com>
  • Loading branch information
jjank authored and codecholeric committed Dec 6, 2020
1 parent 077db87 commit 956475b
Show file tree
Hide file tree
Showing 7 changed files with 346 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Field <com.tngtech.archunit.example.layers.controller.SomeController.otherServic
Field <com.tngtech.archunit.example.layers.controller.SomeController.service> has type <com.tngtech.archunit.example.layers.service.ServiceViolatingDaoRules> in (SomeController.java:0)
Field <com.tngtech.archunit.example.layers.persistence.layerviolation.DaoCallingService.service> has type <com.tngtech.archunit.example.layers.service.ServiceViolatingLayerRules> in (DaoCallingService.java:0)
Field <com.tngtech.archunit.example.layers.service.ServiceType.$VALUES> has type <[Lcom.tngtech.archunit.example.layers.service.ServiceType;> in (ServiceType.java:0)
Field <com.tngtech.archunit.example.layers.service.ServiceType.$VALUES> depends on component type <com.tngtech.archunit.example.layers.service.ServiceType> in (ServiceType.java:0)
Field <com.tngtech.archunit.example.layers.service.ServiceViolatingDaoRules.myEntityManager> has type <com.tngtech.archunit.example.layers.service.ServiceViolatingDaoRules$MyEntityManager> in (ServiceViolatingDaoRules.java:0)
Method <com.tngtech.archunit.example.layers.SomeMediator.violateLayerRulesIndirectly()> calls method <com.tngtech.archunit.example.layers.service.ServiceViolatingLayerRules.doSomething()> in (SomeMediator.java:15)
Method <com.tngtech.archunit.example.layers.controller.SomeGuiController.callServiceLayer()> calls constructor <com.tngtech.archunit.example.layers.service.ServiceHelper.<init>()> in (SomeGuiController.java:7)
Expand All @@ -20,4 +21,6 @@ Method <com.tngtech.archunit.example.layers.service.ComplexServiceAnnotation.ser
Method <com.tngtech.archunit.example.layers.service.ComplexServiceAnnotation.simpleServiceAnnotation()> has return type <com.tngtech.archunit.example.layers.service.SimpleServiceAnnotation> in (ComplexServiceAnnotation.java:0)
Method <com.tngtech.archunit.example.layers.service.ServiceType.values()> calls method <[Lcom.tngtech.archunit.example.layers.service.ServiceType;.clone()> in (ServiceType.java:3)
Method <com.tngtech.archunit.example.layers.service.ServiceType.values()> has return type <[Lcom.tngtech.archunit.example.layers.service.ServiceType;> in (ServiceType.java:0)
Method <com.tngtech.archunit.example.layers.service.ServiceType.values()> depends on component type <com.tngtech.archunit.example.layers.service.ServiceType> in (ServiceType.java:0)
Method <com.tngtech.archunit.example.layers.service.ServiceType.values()> depends on component type <com.tngtech.archunit.example.layers.service.ServiceType> in (ServiceType.java:3)
Method <com.tngtech.archunit.example.layers.service.ServiceViolatingDaoRules.illegallyUseEntityManager()> calls method <com.tngtech.archunit.example.layers.service.ServiceViolatingDaoRules$MyEntityManager.persist(java.lang.Object)> in (ServiceViolatingDaoRules.java:27)
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@
*/
package com.tngtech.archunit.core.domain;

import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

import com.google.common.base.MoreObjects;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableSet;
import com.tngtech.archunit.PublicAPI;
import com.tngtech.archunit.base.ChainableFunction;
import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.base.HasDescription;
import com.tngtech.archunit.base.Optional;
import com.tngtech.archunit.core.domain.properties.HasName;
import com.tngtech.archunit.core.domain.properties.HasSourceCodeLocation;

Expand Down Expand Up @@ -62,11 +63,17 @@ private Dependency(JavaClass originClass, JavaClass targetClass, int lineNumber,
this.sourceCodeLocation = SourceCodeLocation.of(originClass, lineNumber);
}

static Optional<Dependency> tryCreateFromAccess(JavaAccess<?> access) {
if (access.getOriginOwner().equals(access.getTargetOwner()) || access.getTargetOwner().isPrimitive()) {
return Optional.absent();
static Set<Dependency> tryCreateFromAccess(JavaAccess<?> access) {
JavaClass originOwner = access.getOriginOwner();
JavaClass targetOwner = access.getTargetOwner();
if (originOwner.equals(targetOwner) || targetOwner.isPrimitive()) {
return Collections.emptySet();
}
return Optional.of(new Dependency(access.getOriginOwner(), access.getTargetOwner(), access.getLineNumber(), access.getDescription()));

ImmutableSet.Builder<Dependency> dependencies = ImmutableSet.<Dependency>builder()
.addAll(createComponentTypeDependencies(originOwner, access.getOrigin().getDescription(), targetOwner, access.getSourceCodeLocation()));
dependencies.add(new Dependency(originOwner, targetOwner, access.getLineNumber(), access.getDescription()));
return dependencies.build();
}

static Dependency fromInheritance(JavaClass origin, JavaClass targetSuperType) {
Expand All @@ -87,32 +94,32 @@ static Dependency fromInheritance(JavaClass origin, JavaClass targetSuperType) {
return new Dependency(origin, targetSuperType, 0, description);
}

static Optional<Dependency> tryCreateFromField(JavaField field) {
static Set<Dependency> tryCreateFromField(JavaField field) {
return tryCreateDependencyFromJavaMember(field, "has type", field.getRawType());
}

static Optional<Dependency> tryCreateFromReturnType(JavaMethod method) {
static Set<Dependency> tryCreateFromReturnType(JavaMethod method) {
return tryCreateDependencyFromJavaMember(method, "has return type", method.getRawReturnType());
}

static Optional<Dependency> tryCreateFromParameter(JavaCodeUnit codeUnit, JavaClass parameter) {
static Set<Dependency> tryCreateFromParameter(JavaCodeUnit codeUnit, JavaClass parameter) {
return tryCreateDependencyFromJavaMember(codeUnit, "has parameter of type", parameter);
}

static Optional<Dependency> tryCreateFromThrowsDeclaration(ThrowsDeclaration<? extends JavaCodeUnit> declaration) {
static Set<Dependency> tryCreateFromThrowsDeclaration(ThrowsDeclaration<? extends JavaCodeUnit> declaration) {
return tryCreateDependencyFromJavaMember(declaration.getLocation(), "throws type", declaration.getRawType());
}

static Optional<Dependency> tryCreateFromInstanceofCheck(InstanceofCheck instanceofCheck) {
static Set<Dependency> tryCreateFromInstanceofCheck(InstanceofCheck instanceofCheck) {
return tryCreateDependencyFromJavaMemberWithLocation(instanceofCheck.getOwner(), "checks instanceof", instanceofCheck.getRawType(), instanceofCheck.getLineNumber());
}

static Optional<Dependency> tryCreateFromAnnotation(JavaAnnotation<?> target) {
static Set<Dependency> tryCreateFromAnnotation(JavaAnnotation<?> target) {
Origin origin = findSuitableOrigin(target);
return tryCreateDependency(origin.originClass, origin.originDescription, "is annotated with", target.getRawType());
}

static Optional<Dependency> tryCreateFromAnnotationMember(JavaAnnotation<?> annotation, JavaClass memberType) {
static Set<Dependency> tryCreateFromAnnotationMember(JavaAnnotation<?> annotation, JavaClass memberType) {
Origin origin = findSuitableOrigin(annotation);
return tryCreateDependency(origin.originClass, origin.originDescription, "has annotation member of type", memberType);
}
Expand All @@ -130,32 +137,47 @@ private static Origin findSuitableOrigin(JavaAnnotation<?> annotation) {
throw new IllegalStateException("Could not find suitable dependency origin for " + annotation);
}

private static Optional<Dependency> tryCreateDependencyFromJavaMember(JavaMember origin, String dependencyType, JavaClass target) {
private static Set<Dependency> tryCreateDependencyFromJavaMember(JavaMember origin, String dependencyType, JavaClass target) {
return tryCreateDependency(origin.getOwner(), origin.getDescription(), dependencyType, target);
}

private static Optional<Dependency> tryCreateDependencyFromJavaMemberWithLocation(JavaMember origin, String dependencyType, JavaClass target, int lineNumber) {
private static Set<Dependency> tryCreateDependencyFromJavaMemberWithLocation(JavaMember origin, String dependencyType, JavaClass target, int lineNumber) {
return tryCreateDependency(origin.getOwner(), origin.getDescription(), dependencyType, target, SourceCodeLocation.of(origin.getOwner(), lineNumber));
}

private static Optional<Dependency> tryCreateDependency(
private static Set<Dependency> tryCreateDependency(
JavaClass originClass, String originDescription, String dependencyType, JavaClass targetClass) {

return tryCreateDependency(originClass, originDescription, dependencyType, targetClass, originClass.getSourceCodeLocation());
}

private static Optional<Dependency> tryCreateDependency(
private static Set<Dependency> tryCreateDependency(
JavaClass originClass, String originDescription, String dependencyType, JavaClass targetClass, SourceCodeLocation sourceCodeLocation) {

if (originClass.equals(targetClass) || targetClass.isPrimitive()) {
return Optional.absent();
return Collections.emptySet();
}

ImmutableSet.Builder<Dependency> dependencies = ImmutableSet.<Dependency>builder()
.addAll(createComponentTypeDependencies(originClass, originDescription, targetClass, sourceCodeLocation));
String targetDescription = bracketFormat(targetClass.getName());
String dependencyDescription = originDescription + " " + dependencyType + " " + targetDescription;
String description = dependencyDescription + " in " + sourceCodeLocation;
int lineNumber = sourceCodeLocation.getLineNumber();
return Optional.of(new Dependency(originClass, targetClass, lineNumber, description));
dependencies.add(new Dependency(originClass, targetClass, sourceCodeLocation.getLineNumber(), description));
return dependencies.build();
}

private static Set<Dependency> createComponentTypeDependencies(JavaClass originClass, String originDescription, JavaClass targetClass, SourceCodeLocation sourceCodeLocation) {
ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
JavaClass componentType = targetClass;
while (componentType.isArray()) {
componentType = componentType.getComponentType();
String componentTypeTargetDescription = bracketFormat(componentType.getName());
String componentTypeDependencyDescription = originDescription + " depends on component type " + componentTypeTargetDescription;
String componentTypeDescription = componentTypeDependencyDescription + " in " + sourceCodeLocation;
result.add(new Dependency(originClass, componentType, sourceCodeLocation.getLineNumber(), componentTypeDescription));
}
return result.build();
}

private static String bracketFormat(String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ Set<InstanceofCheck> getInstanceofChecksWithTypeOfClass() {
private Set<Dependency> dependenciesFromAccesses(Set<JavaAccess<?>> accesses) {
ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
for (JavaAccess<?> access : accesses) {
result.addAll(Dependency.tryCreateFromAccess(access).asSet());
result.addAll(Dependency.tryCreateFromAccess(access));
}
return result.build();
}
Expand All @@ -158,15 +158,15 @@ private Set<Dependency> inheritanceDependenciesFromSelf() {
private Set<Dependency> fieldDependenciesFromSelf() {
ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
for (JavaField field : javaClass.getFields()) {
result.addAll(Dependency.tryCreateFromField(field).asSet());
result.addAll(Dependency.tryCreateFromField(field));
}
return result.build();
}

private Set<Dependency> returnTypeDependenciesFromSelf() {
ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
for (JavaMethod method : javaClass.getMethods()) {
result.addAll(Dependency.tryCreateFromReturnType(method).asSet());
result.addAll(Dependency.tryCreateFromReturnType(method));
}
return result.build();
}
Expand All @@ -175,7 +175,7 @@ private Set<Dependency> methodParameterDependenciesFromSelf() {
ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
for (JavaMethod method : javaClass.getMethods()) {
for (JavaClass parameter : method.getRawParameterTypes()) {
result.addAll(Dependency.tryCreateFromParameter(method, parameter).asSet());
result.addAll(Dependency.tryCreateFromParameter(method, parameter));
}
}
return result.build();
Expand All @@ -185,7 +185,7 @@ private Set<Dependency> throwsDeclarationDependenciesFromSelf() {
ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
for (JavaCodeUnit codeUnit : javaClass.getCodeUnits()) {
for (ThrowsDeclaration<? extends JavaCodeUnit> throwsDeclaration : codeUnit.getThrowsClause()) {
result.addAll(Dependency.tryCreateFromThrowsDeclaration(throwsDeclaration).asSet());
result.addAll(Dependency.tryCreateFromThrowsDeclaration(throwsDeclaration));
}
}
return result.build();
Expand All @@ -195,7 +195,7 @@ private Set<Dependency> constructorParameterDependenciesFromSelf() {
ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
for (JavaConstructor constructor : javaClass.getConstructors()) {
for (JavaClass parameter : constructor.getRawParameterTypes()) {
result.addAll(Dependency.tryCreateFromParameter(constructor, parameter).asSet());
result.addAll(Dependency.tryCreateFromParameter(constructor, parameter));
}
}
return result.build();
Expand All @@ -214,7 +214,7 @@ private Set<Dependency> instanceofCheckDependenciesFromSelf() {
ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
for (JavaCodeUnit codeUnit : javaClass.getCodeUnits()) {
for (InstanceofCheck instanceofCheck : codeUnit.getInstanceofChecks()) {
result.addAll(Dependency.tryCreateFromInstanceofCheck(instanceofCheck).asSet());
result.addAll(Dependency.tryCreateFromInstanceofCheck(instanceofCheck));
}
}
return result.build();
Expand All @@ -231,21 +231,21 @@ private <T extends HasDescription & HasAnnotations<?>> Set<Dependency> annotatio
private <T extends HasDescription & HasAnnotations<?>> Set<Dependency> annotationDependencies(T annotated) {
final ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
for (final JavaAnnotation<?> annotation : annotated.getAnnotations()) {
result.addAll(Dependency.tryCreateFromAnnotation(annotation).asSet());
result.addAll(Dependency.tryCreateFromAnnotation(annotation));
annotation.accept(new DefaultParameterVisitor() {
@Override
public void visitClass(String propertyName, JavaClass javaClass) {
result.addAll(Dependency.tryCreateFromAnnotationMember(annotation, javaClass).asSet());
result.addAll(Dependency.tryCreateFromAnnotationMember(annotation, javaClass));
}

@Override
public void visitEnumConstant(String propertyName, JavaEnumConstant enumConstant) {
result.addAll(Dependency.tryCreateFromAnnotationMember(annotation, enumConstant.getDeclaringClass()).asSet());
result.addAll(Dependency.tryCreateFromAnnotationMember(annotation, enumConstant.getDeclaringClass()));
}

@Override
public void visitAnnotation(String propertyName, JavaAnnotation<?> memberAnnotation) {
result.addAll(Dependency.tryCreateFromAnnotationMember(annotation, memberAnnotation.getRawType()).asSet());
result.addAll(Dependency.tryCreateFromAnnotationMember(annotation, memberAnnotation.getRawType()));
memberAnnotation.accept(this);
}
});
Expand All @@ -264,58 +264,58 @@ private Set<Dependency> inheritanceDependenciesToSelf() {
private Set<Dependency> fieldDependenciesToSelf() {
Set<Dependency> result = new HashSet<>();
for (JavaField field : javaClass.getFieldsWithTypeOfSelf()) {
result.addAll(Dependency.tryCreateFromField(field).asSet());
result.addAll(Dependency.tryCreateFromField(field));
}
return result;
}

private Set<Dependency> returnTypeDependenciesToSelf() {
Set<Dependency> result = new HashSet<>();
for (JavaMethod method : javaClass.getMethodsWithReturnTypeOfSelf()) {
result.addAll(Dependency.tryCreateFromReturnType(method).asSet());
result.addAll(Dependency.tryCreateFromReturnType(method));
}
return result;
}

private Set<Dependency> methodParameterDependenciesToSelf() {
Set<Dependency> result = new HashSet<>();
for (JavaMethod method : javaClass.getMethodsWithParameterTypeOfSelf()) {
result.addAll(Dependency.tryCreateFromParameter(method, javaClass).asSet());
result.addAll(Dependency.tryCreateFromParameter(method, javaClass));
}
return result;
}

private Set<Dependency> throwsDeclarationDependenciesToSelf() {
Set<Dependency> result = new HashSet<>();
for (ThrowsDeclaration<? extends JavaCodeUnit> throwsDeclaration : getThrowsDeclarationsWithTypeOfClass()) {
result.addAll(Dependency.tryCreateFromThrowsDeclaration(throwsDeclaration).asSet());
result.addAll(Dependency.tryCreateFromThrowsDeclaration(throwsDeclaration));
}
return result;
}

private Set<Dependency> constructorParameterDependenciesToSelf() {
Set<Dependency> result = new HashSet<>();
for (JavaConstructor constructor : javaClass.getConstructorsWithParameterTypeOfSelf()) {
result.addAll(Dependency.tryCreateFromParameter(constructor, javaClass).asSet());
result.addAll(Dependency.tryCreateFromParameter(constructor, javaClass));
}
return result;
}

private Iterable<? extends Dependency> annotationDependenciesToSelf() {
Set<Dependency> result = new HashSet<>();
for (JavaAnnotation<?> annotation : annotationsWithTypeOfClass) {
result.addAll(Dependency.tryCreateFromAnnotation(annotation).asSet());
result.addAll(Dependency.tryCreateFromAnnotation(annotation));
}
for (JavaAnnotation<?> annotation : annotationsWithParameterTypeOfClass) {
result.addAll(Dependency.tryCreateFromAnnotationMember(annotation, javaClass).asSet());
result.addAll(Dependency.tryCreateFromAnnotationMember(annotation, javaClass));
}
return result;
}

private Set<Dependency> instanceofCheckDependenciesToSelf() {
Set<Dependency> result = new HashSet<>();
for (InstanceofCheck instanceofCheck : getInstanceofChecksWithTypeOfClass()) {
result.addAll(Dependency.tryCreateFromInstanceofCheck(instanceofCheck).asSet());
result.addAll(Dependency.tryCreateFromInstanceofCheck(instanceofCheck));
}
return result;
}
Expand Down
Loading

0 comments on commit 956475b

Please sign in to comment.