Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
package com.tngtech.archunit.example.layers.service;

import java.util.Map;
import java.util.Set;

import com.tngtech.archunit.example.layers.controller.SomeUtility;
import com.tngtech.archunit.example.layers.controller.one.SomeEnum;
import com.tngtech.archunit.example.layers.security.Secured;

/**
* Well modelled code always has lots of 'helpers' ;-)
*/
public class ServiceHelper {
@SuppressWarnings("unused")
public class ServiceHelper<
TYPE_PARAMETER_VIOLATING_LAYER_RULE extends SomeUtility,
ANOTHER_TYPE_PARAMETER_VIOLATING_LAYER_RULE extends Map<?, Set<? super SomeEnum>>> {

public Object insecure = new Object();
@Secured
public Object properlySecured = new Object();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@
import static com.tngtech.archunit.testutils.ExpectedDependency.field;
import static com.tngtech.archunit.testutils.ExpectedDependency.inheritanceFrom;
import static com.tngtech.archunit.testutils.ExpectedDependency.method;
import static com.tngtech.archunit.testutils.ExpectedDependency.typeParameter;
import static com.tngtech.archunit.testutils.ExpectedLocation.javaClass;
import static com.tngtech.archunit.testutils.ExpectedNaming.simpleNameOf;
import static com.tngtech.archunit.testutils.ExpectedNaming.simpleNameOfAnonymousClassOf;
Expand Down Expand Up @@ -739,6 +740,8 @@ Stream<DynamicTest> LayerDependencyRulesTest() {
.by(callFromMethod(ServiceViolatingLayerRules.class, illegalAccessToController)
.toMethod(UseCaseTwoController.class, doSomethingTwo)
.inLine(25).asDependency())
.by(typeParameter(ServiceHelper.class, "TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeUtility.class))
.by(typeParameter(ServiceHelper.class, "ANOTHER_TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeEnum.class))
.by(method(ServiceViolatingLayerRules.class, dependentMethod).withParameter(UseCaseTwoController.class))
.by(method(ServiceViolatingLayerRules.class, dependentMethod).withReturnType(SomeGuiController.class))
.by(method(ServiceViolatingLayerRules.class, dependentOnComponentTypeMethod).withParameter(UseCaseTwoController[].class))
Expand Down Expand Up @@ -796,6 +799,8 @@ Stream<DynamicTest> LayerDependencyRulesTest() {
.by(callFromMethod(ServiceViolatingLayerRules.class, illegalAccessToController)
.toMethod(UseCaseTwoController.class, doSomethingTwo)
.inLine(25).asDependency())
.by(typeParameter(ServiceHelper.class, "TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeUtility.class))
.by(typeParameter(ServiceHelper.class, "ANOTHER_TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeEnum.class))
.by(method(ServiceViolatingLayerRules.class, dependentMethod).withParameter(UseCaseTwoController.class))
.by(method(ServiceViolatingLayerRules.class, dependentMethod).withReturnType(SomeGuiController.class))
.by(method(ServiceViolatingLayerRules.class, dependentOnComponentTypeMethod).withParameter(UseCaseTwoController[].class))
Expand Down Expand Up @@ -863,6 +868,8 @@ Stream<DynamicTest> LayeredArchitectureTest() {
.inLine(27)
.asDependency())

.by(typeParameter(ServiceHelper.class, "TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeUtility.class))
.by(typeParameter(ServiceHelper.class, "ANOTHER_TYPE_PARAMETER_VIOLATING_LAYER_RULE").dependingOn(SomeEnum.class))
.by(method(ServiceViolatingLayerRules.class, dependentMethod).withParameter(UseCaseTwoController.class))
.by(method(ServiceViolatingLayerRules.class, dependentMethod).withReturnType(SomeGuiController.class))
.by(method(ServiceViolatingLayerRules.class, dependentOnComponentTypeMethod)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ public static InheritanceCreator inheritanceFrom(Class<?> clazz) {
return new InheritanceCreator(clazz);
}

public static TypeParameterCreator typeParameter(Class<?> clazz, String typeParameterName) {
return new TypeParameterCreator(clazz, typeParameterName);
}

public static AnnotationDependencyCreator annotatedClass(Class<?> clazz) {
return new AnnotationDependencyCreator(clazz);
}
Expand Down Expand Up @@ -85,6 +89,21 @@ public ExpectedDependency implementing(Class<?> anInterface) {
}
}

public static class TypeParameterCreator {
private final Class<?> clazz;
private final String typeParameterName;

private TypeParameterCreator(Class<?> clazz, String typeParameterName) {
this.clazz = clazz;
this.typeParameterName = typeParameterName;
}

public ExpectedDependency dependingOn(Class<?> typeParameterDependency) {
return new ExpectedDependency(clazz, typeParameterDependency,
getDependencyPattern(clazz.getName(), "has type parameter '" + typeParameterName + "' depending on", typeParameterDependency.getName(), 0));
}
}

public static class AccessCreator {
private final Class<?> originClass;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,26 +120,31 @@ static Set<Dependency> tryCreateFromInstanceofCheck(InstanceofCheck instanceofCh
}

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

static Set<Dependency> tryCreateFromAnnotationMember(JavaAnnotation<?> annotation, JavaClass memberType) {
Origin origin = findSuitableOrigin(annotation);
Origin origin = findSuitableOrigin(annotation, annotation.getAnnotatedElement());
return tryCreateDependency(origin.originClass, origin.originDescription, "has annotation member of type", memberType);
}

private static Origin findSuitableOrigin(JavaAnnotation<?> annotation) {
Object annotatedElement = annotation.getAnnotatedElement();
if (annotatedElement instanceof JavaMember) {
JavaMember member = (JavaMember) annotatedElement;
static Set<Dependency> tryCreateFromTypeParameter(JavaTypeVariable<?> typeParameter, JavaClass typeParameterDependency) {
String dependencyType = "has type parameter '" + typeParameter.getName() + "' depending on";
Origin origin = findSuitableOrigin(typeParameter, typeParameter.getOwner());
return tryCreateDependency(origin.originClass, origin.originDescription, dependencyType, typeParameterDependency);
}

private static Origin findSuitableOrigin(Object dependencyCause, Object originCandidate) {
if (originCandidate instanceof JavaMember) {
JavaMember member = (JavaMember) originCandidate;
return new Origin(member.getOwner(), member.getDescription());
}
if (annotatedElement instanceof JavaClass) {
JavaClass clazz = (JavaClass) annotatedElement;
if (originCandidate instanceof JavaClass) {
JavaClass clazz = (JavaClass) originCandidate;
return new Origin(clazz, clazz.getDescription());
}
throw new IllegalStateException("Could not find suitable dependency origin for " + annotation);
throw new IllegalStateException("Could not find suitable dependency origin for " + dependencyCause);
}

private static Set<Dependency> tryCreateDependencyFromJavaMember(JavaMember origin, String dependencyType, JavaClass target) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,11 @@ public static InstanceofCheck createInstanceofCheck(JavaCodeUnit codeUnit, JavaC
return InstanceofCheck.from(codeUnit, target, lineNumber);
}

public static JavaTypeVariable createTypeVariable(String name, JavaClass erasure) {
return new JavaTypeVariable(name, erasure);
public static <OWNER extends HasDescription> JavaTypeVariable<OWNER> createTypeVariable(String name, OWNER owner, JavaClass erasure) {
return new JavaTypeVariable<>(name, owner, erasure);
}

public static void completeTypeVariable(JavaTypeVariable variable, List<JavaType> upperBounds) {
public static void completeTypeVariable(JavaTypeVariable<?> variable, List<JavaType> upperBounds) {
variable.setUpperBounds(upperBounds);
}

Expand All @@ -164,7 +164,7 @@ public static JavaGenericArrayType createGenericArrayType(JavaType componentType
return new JavaGenericArrayType(componentType.getName() + "[]", componentType, erasure);
}

public static JavaWildcardType createWildcardType(JavaWildcardTypeBuilder builder) {
public static JavaWildcardType createWildcardType(JavaWildcardTypeBuilder<?> builder) {
return new JavaWildcardType(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public interface ImportContext {

Set<JavaClass> createInterfaces(JavaClass owner);

List<JavaTypeVariable> createTypeParameters(JavaClass owner);
List<JavaTypeVariable<JavaClass>> createTypeParameters(JavaClass owner);

Set<JavaField> createFields(JavaClass owner);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public class JavaClass implements JavaType, HasName.AndFullName, HasAnnotations<
private final boolean isAnonymousClass;
private final boolean isMemberClass;
private final Set<JavaModifier> modifiers;
private List<JavaTypeVariable> typeParameters = emptyList();
private List<JavaTypeVariable<JavaClass>> typeParameters = emptyList();
private final Supplier<Class<?>> reflectSupplier;
private Set<JavaField> fields = emptySet();
private Set<JavaCodeUnit> codeUnits = emptySet();
Expand Down Expand Up @@ -644,7 +644,7 @@ public Optional<JavaAnnotation<JavaClass>> tryGetAnnotationOfType(String typeNam
}

@PublicAPI(usage = ACCESS)
public List<JavaTypeVariable> getTypeParameters() {
public List<JavaTypeVariable<JavaClass>> getTypeParameters() {
return typeParameters;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.tngtech.archunit.core.domain.properties.HasAnnotations;

import static com.google.common.base.Suppliers.memoize;
import static com.google.common.collect.Iterables.concat;

class JavaClassDependencies {
private final JavaClass javaClass;
Expand All @@ -49,6 +50,7 @@ public Set<Dependency> get() {
result.addAll(constructorParameterDependenciesFromSelf());
result.addAll(annotationDependenciesFromSelf());
result.addAll(instanceofCheckDependenciesFromSelf());
result.addAll(typeParameterDependenciesFromSelf());
return result.build();
}
});
Expand Down Expand Up @@ -139,6 +141,53 @@ private Set<Dependency> instanceofCheckDependenciesFromSelf() {
return result.build();
}

private Set<Dependency> typeParameterDependenciesFromSelf() {
ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
for (JavaTypeVariable<?> typeVariable : javaClass.getTypeParameters()) {
result.addAll(getDependenciesFromTypeParameter(typeVariable));
}
return result.build();
}

private Set<Dependency> getDependenciesFromTypeParameter(JavaTypeVariable<?> typeVariable) {
ImmutableSet.Builder<Dependency> dependenciesBuilder = ImmutableSet.builder();
for (JavaType bound : typeVariable.getUpperBounds()) {
for (JavaClass typeParameterDependency : dependenciesOfType(bound)) {
dependenciesBuilder.addAll(Dependency.tryCreateFromTypeParameter(typeVariable, typeParameterDependency));
}
}
return dependenciesBuilder.build();
}

private static Iterable<JavaClass> dependenciesOfType(JavaType javaType) {
ImmutableSet.Builder<JavaClass> result = ImmutableSet.builder();
if (javaType instanceof JavaClass) {
result.add((JavaClass) javaType);
} else if (javaType instanceof JavaParameterizedType) {
result.addAll(dependenciesOfParameterizedType((JavaParameterizedType) javaType));
} else if (javaType instanceof JavaWildcardType) {
result.addAll(dependenciesOfWildcardType((JavaWildcardType) javaType));
}
return result.build();
}

private static Set<JavaClass> dependenciesOfParameterizedType(JavaParameterizedType parameterizedType) {
ImmutableSet.Builder<JavaClass> result = ImmutableSet.<JavaClass>builder()
.add(parameterizedType.toErasure());
for (JavaType typeArgument : parameterizedType.getActualTypeArguments()) {
result.addAll(dependenciesOfType(typeArgument));
}
return result.build();
}

private static Set<JavaClass> dependenciesOfWildcardType(JavaWildcardType javaType) {
ImmutableSet.Builder<JavaClass> result = ImmutableSet.builder();
for (JavaType bound : concat(javaType.getUpperBounds(), javaType.getLowerBounds())) {
result.addAll(dependenciesOfType(bound));
}
return result.build();
}

private <T extends HasDescription & HasAnnotations<?>> Set<Dependency> annotationDependencies(Set<T> annotatedObjects) {
ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
for (T annotated : annotatedObjects) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
*/
package com.tngtech.archunit.core.domain;

import java.lang.reflect.TypeVariable;
import java.util.List;

import com.google.common.base.Joiner;
import com.google.common.collect.FluentIterable;
import com.tngtech.archunit.PublicAPI;
import com.tngtech.archunit.base.HasDescription;
import com.tngtech.archunit.core.domain.properties.HasOwner;
import com.tngtech.archunit.core.domain.properties.HasUpperBounds;

import static com.google.common.collect.Iterables.getOnlyElement;
Expand All @@ -41,13 +44,15 @@
* {@code SomeInterfaceOne} and {@code SomeInterfaceTwo}.
*/
@PublicAPI(usage = ACCESS)
public final class JavaTypeVariable implements JavaType, HasUpperBounds {
public final class JavaTypeVariable<OWNER extends HasDescription> implements JavaType, HasOwner<OWNER>, HasUpperBounds {
private final String name;
private final OWNER owner;
private List<JavaType> upperBounds = emptyList();
private JavaClass erasure;

JavaTypeVariable(String name, JavaClass erasure) {
JavaTypeVariable(String name, OWNER owner, JavaClass erasure) {
this.name = name;
this.owner = owner;
this.erasure = erasure;
}

Expand All @@ -67,7 +72,31 @@ public String getName() {
}

/**
* @see #getUpperBounds()
* This method is simply an alias for {@link #getOwner()} that is more familiar to users
* of the Java Reflection API.
*
* @see TypeVariable#getGenericDeclaration()
*/
@PublicAPI(usage = ACCESS)
public OWNER getGenericDeclaration() {
return getOwner();
}

/**
* @return The 'owner' of this type parameter, i.e. the Java object that declared this
* {@link TypeVariable} as a type parameter. For type parameter {@code T} of
* {@code SomeClass<T>} this would be the {@code JavaClass} representing {@code SomeClass}
*/
@Override
public OWNER getOwner() {
return owner;
}

/**
* This method is simply an alias for {@link #getUpperBounds()} that is more familiar to users
* of the Java Reflection API.
*
* @see TypeVariable#getBounds()
*/
@PublicAPI(usage = ACCESS)
public List<JavaType> getBounds() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class JavaWildcardType implements JavaType, HasUpperBounds {
private final List<JavaType> lowerBounds;
private final JavaClass erasure;

JavaWildcardType(JavaWildcardTypeBuilder builder) {
JavaWildcardType(JavaWildcardTypeBuilder<?> builder) {
upperBounds = builder.getUpperBounds();
lowerBounds = builder.getLowerBounds();
erasure = builder.getUnboundErasureType(upperBounds);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class ClassFileImportRecord {
private static final Logger LOG = LoggerFactory.getLogger(ClassFileImportRecord.class);

private static final TypeParametersBuilder NO_TYPE_PARAMETERS =
new TypeParametersBuilder(Collections.<JavaTypeParameterBuilder>emptySet());
new TypeParametersBuilder(Collections.<JavaTypeParameterBuilder<JavaClass>>emptySet());

private final Map<String, JavaClass> classes = new HashMap<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ public Set<JavaClass> createInterfaces(JavaClass owner) {
}

@Override
public List<JavaTypeVariable> createTypeParameters(JavaClass owner) {
public List<JavaTypeVariable<JavaClass>> createTypeParameters(JavaClass owner) {
TypeParametersBuilder typeParametersBuilder = importRecord.getTypeParameterBuildersFor(owner.getName());
return typeParametersBuilder.build(owner, classes.byTypeName());
}
Expand Down
Loading