From 06e39acee4da9cbb0bbee5aa9eb1f0c52dd15b09 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Fri, 21 Jan 2022 10:39:38 +0700 Subject: [PATCH 01/22] remove obsolete throws declaration of test method Signed-off-by: Peter Gafert --- .../archunit/lang/extension/ArchUnitExtensionsTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/archunit/src/test/java/com/tngtech/archunit/lang/extension/ArchUnitExtensionsTest.java b/archunit/src/test/java/com/tngtech/archunit/lang/extension/ArchUnitExtensionsTest.java index 2452ba86b7..c02f1d7bbe 100644 --- a/archunit/src/test/java/com/tngtech/archunit/lang/extension/ArchUnitExtensionsTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/lang/extension/ArchUnitExtensionsTest.java @@ -1,6 +1,5 @@ package com.tngtech.archunit.lang.extension; -import java.io.IOException; import java.util.Properties; import com.google.common.collect.ImmutableSet; @@ -39,7 +38,7 @@ public class ArchUnitExtensionsTest { private ArchUnitExtensions extensions; @Test - public void extensions_are_configured() throws IOException { + public void extensions_are_configured() { TestExtension extensionOne = new TestExtension("one"); TestExtension extensionTwo = new TestExtension("two"); when(extensionLoader.getAll()).thenReturn(ImmutableSet.of(extensionOne, extensionTwo)); From 23377927d8ecc74fdb1139841023bb871e603331 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sun, 9 Jan 2022 23:13:05 +0700 Subject: [PATCH 02/22] import `DomainBuilders.*` to improve readability Signed-off-by: Peter Gafert --- .../core/importer/ClassFileImportRecord.java | 59 ++++++++++--------- .../core/importer/ClassFileProcessor.java | 24 +++++--- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java index d0e0778180..603c753a29 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java @@ -33,10 +33,15 @@ import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaMember; import com.tngtech.archunit.core.domain.JavaMethod; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaAnnotationBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaClassTypeParametersBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaCodeUnitBuilder; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaConstructorBuilder; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaFieldBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaMemberBuilder; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaMethodBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaParameterizedTypeBuilder; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaStaticInitializerBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaTypeParameterBuilder; import com.tngtech.archunit.core.importer.RawAccessRecord.CodeUnit; @@ -54,12 +59,12 @@ class ClassFileImportRecord { private final Map typeParametersBuilderByOwner = new HashMap<>(); private final Map> genericSuperclassBuilderByOwner = new HashMap<>(); private final Map>> genericInterfaceBuildersByOwner = new HashMap<>(); - private final SetMultimap fieldBuildersByOwner = HashMultimap.create(); - private final SetMultimap methodBuildersByOwner = HashMultimap.create(); - private final SetMultimap constructorBuildersByOwner = HashMultimap.create(); - private final Map staticInitializerBuildersByOwner = new HashMap<>(); - private final SetMultimap annotationsByOwner = HashMultimap.create(); - private final Map annotationDefaultValuesByOwner = new HashMap<>(); + private final SetMultimap fieldBuildersByOwner = HashMultimap.create(); + private final SetMultimap methodBuildersByOwner = HashMultimap.create(); + private final SetMultimap constructorBuildersByOwner = HashMultimap.create(); + private final Map staticInitializerBuildersByOwner = new HashMap<>(); + private final SetMultimap annotationsByOwner = HashMultimap.create(); + private final Map annotationDefaultValuesByOwner = new HashMap<>(); private final EnclosingDeclarationsByInnerClasses enclosingDeclarationsByOwner = new EnclosingDeclarationsByInnerClasses(); private final Set rawFieldAccessRecords = new HashSet<>(); @@ -91,34 +96,34 @@ public void addGenericInterfaces(String ownerName, List annotations) { + void addClassAnnotations(String ownerName, Set annotations) { this.annotationsByOwner.putAll(ownerName, annotations); } - void addMemberAnnotations(String declaringClassName, String memberName, String descriptor, Set annotations) { + void addMemberAnnotations(String declaringClassName, String memberName, String descriptor, Set annotations) { this.annotationsByOwner.putAll(getMemberKey(declaringClassName, memberName, descriptor), annotations); } - void addAnnotationDefaultValue(String declaringClassName, String methodName, String descriptor, DomainBuilders.JavaAnnotationBuilder.ValueBuilder valueBuilder) { + void addAnnotationDefaultValue(String declaringClassName, String methodName, String descriptor, JavaAnnotationBuilder.ValueBuilder valueBuilder) { annotationDefaultValuesByOwner.put(getMemberKey(declaringClassName, methodName, descriptor), valueBuilder); } @@ -155,15 +160,15 @@ Optional>> getGenericInterfacesFor( Set getMemberSignatureTypeNames() { ImmutableSet.Builder result = ImmutableSet.builder(); - for (DomainBuilders.JavaFieldBuilder fieldBuilder : fieldBuildersByOwner.values()) { + for (JavaFieldBuilder fieldBuilder : fieldBuildersByOwner.values()) { result.add(fieldBuilder.getTypeName()); } - for (DomainBuilders.JavaConstructorBuilder constructorBuilder : constructorBuildersByOwner.values()) { + for (JavaConstructorBuilder constructorBuilder : constructorBuildersByOwner.values()) { for (String parameterTypeName : constructorBuilder.getParameterTypeNames()) { result.add(parameterTypeName); } } - for (DomainBuilders.JavaMethodBuilder methodBuilder : methodBuildersByOwner.values()) { + for (JavaMethodBuilder methodBuilder : methodBuildersByOwner.values()) { result.add(methodBuilder.getReturnTypeName()); for (String parameterTypeName : methodBuilder.getParameterTypeNames()) { result.add(parameterTypeName); @@ -172,31 +177,31 @@ Set getMemberSignatureTypeNames() { return result.build(); } - Set getFieldBuildersFor(String ownerName) { + Set getFieldBuildersFor(String ownerName) { return fieldBuildersByOwner.get(ownerName); } - Set getMethodBuildersFor(String ownerName) { + Set getMethodBuildersFor(String ownerName) { return methodBuildersByOwner.get(ownerName); } - Set getConstructorBuildersFor(String ownerName) { + Set getConstructorBuildersFor(String ownerName) { return constructorBuildersByOwner.get(ownerName); } - Optional getStaticInitializerBuilderFor(String ownerName) { + Optional getStaticInitializerBuilderFor(String ownerName) { return Optional.ofNullable(staticInitializerBuildersByOwner.get(ownerName)); } Set getAnnotationTypeNamesFor(JavaClass owner) { ImmutableSet.Builder result = ImmutableSet.builder(); - for (DomainBuilders.JavaAnnotationBuilder annotationBuilder : annotationsByOwner.get(owner.getName())) { + for (JavaAnnotationBuilder annotationBuilder : annotationsByOwner.get(owner.getName())) { result.add(annotationBuilder.getFullyQualifiedClassName()); } return result.build(); } - Set getAnnotationsFor(JavaClass owner) { + Set getAnnotationsFor(JavaClass owner) { return annotationsByOwner.get(owner.getName()); } @@ -209,7 +214,7 @@ Set getMemberAnnotationTypeNamesFor(JavaClass owner) { ImmutableSet.Builder result = ImmutableSet.builder(); for (JavaMemberBuilder memberBuilder : memberBuilders) { - for (DomainBuilders.JavaAnnotationBuilder annotationBuilder : annotationsByOwner.get(getMemberKey(owner.getName(), memberBuilder.getName(), memberBuilder.getDescriptor()))) { + for (JavaAnnotationBuilder annotationBuilder : annotationsByOwner.get(getMemberKey(owner.getName(), memberBuilder.getName(), memberBuilder.getDescriptor()))) { result.add(annotationBuilder.getFullyQualifiedClassName()); } } @@ -223,24 +228,24 @@ Set getParameterAnnotationTypeNamesFor(JavaClass owner) { ImmutableSet.Builder result = ImmutableSet.builder(); for (JavaCodeUnitBuilder codeUnitBuilder : codeUnitBuilders) { - for (DomainBuilders.JavaAnnotationBuilder annotationBuilder : codeUnitBuilder.getParameterAnnotationBuilders()) { + for (JavaAnnotationBuilder annotationBuilder : codeUnitBuilder.getParameterAnnotationBuilders()) { result.add(annotationBuilder.getFullyQualifiedClassName()); } } return result.build(); } - private Iterable> nullToEmpty(DomainBuilders.JavaStaticInitializerBuilder staticInitializerBuilder) { + private Iterable> nullToEmpty(JavaStaticInitializerBuilder staticInitializerBuilder) { return staticInitializerBuilder != null ? Collections.>singleton(staticInitializerBuilder) : Collections.>emptySet(); } - Set getAnnotationsFor(JavaMember owner) { + Set getAnnotationsFor(JavaMember owner) { return annotationsByOwner.get(getMemberKey(owner)); } - Optional getAnnotationDefaultValueBuilderFor(JavaMethod method) { + Optional getAnnotationDefaultValueBuilderFor(JavaMethod method) { return Optional.ofNullable(annotationDefaultValuesByOwner.get(getMemberKey(method))); } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java index 9f883db033..c031622f48 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java @@ -25,7 +25,13 @@ import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.domain.JavaFieldAccess.AccessType; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaAnnotationBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaClassTypeParametersBuilder; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaConstructorBuilder; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaFieldBuilder; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaMethodBuilder; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaParameterizedTypeBuilder; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaStaticInitializerBuilder; import com.tngtech.archunit.core.importer.JavaClassProcessor.AccessHandler; import com.tngtech.archunit.core.importer.JavaClassProcessor.DeclarationHandler; import com.tngtech.archunit.core.importer.RawAccessRecord.CodeUnit; @@ -92,47 +98,47 @@ public void onDeclaredTypeParameters(JavaClassTypeParametersBuilder typeParamete } @Override - public void onGenericSuperclass(DomainBuilders.JavaParameterizedTypeBuilder genericSuperclassBuilder) { + public void onGenericSuperclass(JavaParameterizedTypeBuilder genericSuperclassBuilder) { importRecord.addGenericSuperclass(ownerName, genericSuperclassBuilder); } @Override - public void onGenericInterfaces(List> genericInterfaceBuilders) { + public void onGenericInterfaces(List> genericInterfaceBuilders) { importRecord.addGenericInterfaces(ownerName, genericInterfaceBuilders); } @Override - public void onDeclaredField(DomainBuilders.JavaFieldBuilder fieldBuilder) { + public void onDeclaredField(JavaFieldBuilder fieldBuilder) { importRecord.addField(ownerName, fieldBuilder); } @Override - public void onDeclaredConstructor(DomainBuilders.JavaConstructorBuilder constructorBuilder) { + public void onDeclaredConstructor(JavaConstructorBuilder constructorBuilder) { importRecord.addConstructor(ownerName, constructorBuilder); } @Override - public void onDeclaredMethod(DomainBuilders.JavaMethodBuilder methodBuilder) { + public void onDeclaredMethod(JavaMethodBuilder methodBuilder) { importRecord.addMethod(ownerName, methodBuilder); } @Override - public void onDeclaredStaticInitializer(DomainBuilders.JavaStaticInitializerBuilder staticInitializerBuilder) { + public void onDeclaredStaticInitializer(JavaStaticInitializerBuilder staticInitializerBuilder) { importRecord.setStaticInitializer(ownerName, staticInitializerBuilder); } @Override - public void onDeclaredClassAnnotations(Set annotationBuilders) { + public void onDeclaredClassAnnotations(Set annotationBuilders) { importRecord.addClassAnnotations(ownerName, annotationBuilders); } @Override - public void onDeclaredMemberAnnotations(String memberName, String descriptor, Set annotations) { + public void onDeclaredMemberAnnotations(String memberName, String descriptor, Set annotations) { importRecord.addMemberAnnotations(ownerName, memberName, descriptor, annotations); } @Override - public void onDeclaredAnnotationDefaultValue(String methodName, String methodDescriptor, DomainBuilders.JavaAnnotationBuilder.ValueBuilder valueBuilder) { + public void onDeclaredAnnotationDefaultValue(String methodName, String methodDescriptor, JavaAnnotationBuilder.ValueBuilder valueBuilder) { importRecord.addAnnotationDefaultValue(ownerName, methodName, methodDescriptor, valueBuilder); } From b16a2a09e2b8d357a0d3e7d0a79fa055944cc8e4 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sat, 15 Jan 2022 17:51:07 +0700 Subject: [PATCH 03/22] consolidate annotation `ValueBuilder` factory methods ... to make it easier to see all the cases that can occur when building annotation values in one place. Signed-off-by: Peter Gafert --- .../core/importer/DomainBuilders.java | 26 +++++++- .../core/importer/JavaClassProcessor.java | 62 ++++++++----------- .../core/importer/ImportTestUtils.java | 3 +- 3 files changed, 51 insertions(+), 40 deletions(-) diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/DomainBuilders.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/DomainBuilders.java index 8893ca148d..0910d46018 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/DomainBuilders.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/DomainBuilders.java @@ -589,7 +589,7 @@ public JavaAnnotation build(T owner, ImportedClass abstract static class ValueBuilder { abstract Optional build(T owner, ImportedClasses importedClasses); - static ValueBuilder ofFinished(final Object value) { + static ValueBuilder fromPrimitiveProperty(final Object value) { return new ValueBuilder() { @Override Optional build(T owner, ImportedClasses unused) { @@ -598,7 +598,29 @@ Optional build(T owner, ImportedClasses unuse }; } - static ValueBuilder from(final JavaAnnotationBuilder builder) { + public static ValueBuilder fromEnumProperty(final JavaClassDescriptor enumType, final String value) { + return new ValueBuilder() { + @Override + Optional build(T owner, ImportedClasses importedClasses) { + return Optional.of( + new DomainBuilders.JavaEnumConstantBuilder() + .withDeclaringClass(importedClasses.getOrResolve(enumType.getFullyQualifiedClassName())) + .withName(value) + .build()); + } + }; + } + + static ValueBuilder fromClassProperty(final JavaClassDescriptor value) { + return new ValueBuilder() { + @Override + Optional build(T owner, ImportedClasses importedClasses) { + return Optional.of(importedClasses.getOrResolve(value.getFullyQualifiedClassName())); + } + }; + } + + static ValueBuilder fromAnnotationProperty(final JavaAnnotationBuilder builder) { return new ValueBuilder() { @Override Optional build(T owner, ImportedClasses importedClasses) { diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java index 914ae793aa..6fc90a0aad 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java @@ -44,6 +44,7 @@ import com.tngtech.archunit.core.domain.JavaEnumConstant; import com.tngtech.archunit.core.domain.JavaField; import com.tngtech.archunit.core.domain.JavaModifier; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaAnnotationBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaAnnotationBuilder.ValueBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaTypeCreationProcess; import com.tngtech.archunit.core.importer.JavaCodeUnitSignatureImporter.JavaCodeUnitSignature; @@ -76,7 +77,7 @@ class JavaClassProcessor extends ClassVisitor { private static final AccessHandler NO_OP = new AccessHandler.NoOp(); private DomainBuilders.JavaClassBuilder javaClassBuilder; - private final Set annotations = new HashSet<>(); + private final Set annotations = new HashSet<>(); private final SourceDescriptor sourceDescriptor; private final DeclarationHandler declarationHandler; private final AccessHandler accessHandler; @@ -320,8 +321,8 @@ private static class MethodProcessor extends MethodVisitor { private final AccessHandler accessHandler; private final DomainBuilders.JavaCodeUnitBuilder codeUnitBuilder; private final DeclarationHandler declarationHandler; - private final Set annotations = new HashSet<>(); - private final SetMultimap parameterAnnotationsByIndex = HashMultimap.create(); + private final Set annotations = new HashSet<>(); + private final SetMultimap parameterAnnotationsByIndex = HashMultimap.create(); private int actualLineNumber; MethodProcessor(String declaringClassName, AccessHandler accessHandler, DomainBuilders.JavaCodeUnitBuilder codeUnitBuilder, DeclarationHandler declarationHandler) { @@ -464,8 +465,8 @@ private SetAsAnnotationDefault(String annotationTypeName, DomainBuilders.JavaMet } @Override - public void add(DomainBuilders.JavaAnnotationBuilder annotation) { - setArrayResult(ValueBuilder.from(annotation)); + public void add(JavaAnnotationBuilder annotation) { + setArrayResult(ValueBuilder.fromAnnotationProperty(annotation)); } @Override @@ -503,9 +504,9 @@ interface DeclarationHandler { void onDeclaredStaticInitializer(DomainBuilders.JavaStaticInitializerBuilder staticInitializerBuilder); - void onDeclaredClassAnnotations(Set annotationBuilders); + void onDeclaredClassAnnotations(Set annotationBuilders); - void onDeclaredMemberAnnotations(String memberName, String descriptor, Set annotations); + void onDeclaredMemberAnnotations(String memberName, String descriptor, Set annotations); void onDeclaredAnnotationDefaultValue(String methodName, String methodDescriptor, ValueBuilder valueBuilder); @@ -552,7 +553,7 @@ public void handleMethodReferenceInstruction(String owner, String name, String d private static class FieldProcessor extends FieldVisitor { private final DomainBuilders.JavaFieldBuilder fieldBuilder; private final DeclarationHandler declarationHandler; - private final Set annotations = new HashSet<>(); + private final Set annotations = new HashSet<>(); private FieldProcessor(DomainBuilders.JavaFieldBuilder fieldBuilder, DeclarationHandler declarationHandler) { super(ASM_API_VERSION); @@ -572,8 +573,8 @@ public void visitEnd() { } } - private static DomainBuilders.JavaAnnotationBuilder annotationBuilderFor(String desc) { - return new DomainBuilders.JavaAnnotationBuilder().withType(JavaClassDescriptorImporter.importAsmTypeFromDescriptor(desc)); + private static JavaAnnotationBuilder annotationBuilderFor(String desc) { + return new JavaAnnotationBuilder().withType(JavaClassDescriptorImporter.importAsmTypeFromDescriptor(desc)); } private static class AnnotationProcessor extends AnnotationVisitor { @@ -627,35 +628,35 @@ public void visitEnd() { } } - private static TakesAnnotationBuilder addAnnotationTo(final Collection collection) { + private static TakesAnnotationBuilder addAnnotationTo(final Collection collection) { return new TakesAnnotationBuilder() { @Override - public void add(DomainBuilders.JavaAnnotationBuilder annotation) { + public void add(JavaAnnotationBuilder annotation) { collection.add(annotation); } }; } - private static TakesAnnotationBuilder addAnnotationAtIndex(final SetMultimap annotations, final int index) { + private static TakesAnnotationBuilder addAnnotationAtIndex(final SetMultimap annotations, final int index) { return new TakesAnnotationBuilder() { @Override - public void add(DomainBuilders.JavaAnnotationBuilder annotation) { + public void add(JavaAnnotationBuilder annotation) { annotations.put(index, annotation); } }; } - private static TakesAnnotationBuilder addAnnotationAsProperty(final String name, final DomainBuilders.JavaAnnotationBuilder annotationBuilder) { + private static TakesAnnotationBuilder addAnnotationAsProperty(final String name, final JavaAnnotationBuilder annotationBuilder) { return new TakesAnnotationBuilder() { @Override - public void add(DomainBuilders.JavaAnnotationBuilder builder) { - annotationBuilder.addProperty(name, ValueBuilder.from(builder)); + public void add(JavaAnnotationBuilder builder) { + annotationBuilder.addProperty(name, ValueBuilder.fromAnnotationProperty(builder)); } }; } private interface TakesAnnotationBuilder { - void add(DomainBuilders.JavaAnnotationBuilder annotation); + void add(JavaAnnotationBuilder annotation); } private static class AnnotationArrayProcessor extends AnnotationVisitor { @@ -683,8 +684,8 @@ public AnnotationVisitor visitAnnotation(String name, String desc) { setDerivedComponentType(JavaAnnotation.class); return new AnnotationProcessor(new TakesAnnotationBuilder() { @Override - public void add(DomainBuilders.JavaAnnotationBuilder annotationBuilder) { - values.add(ValueBuilder.from(annotationBuilder)); + public void add(JavaAnnotationBuilder annotationBuilder) { + values.add(ValueBuilder.fromAnnotationProperty(annotationBuilder)); } }, annotationBuilderFor(desc)); } @@ -813,30 +814,17 @@ private interface AnnotationArrayContext { } private static ValueBuilder javaEnumBuilder(final String desc, final String value) { - return new ValueBuilder() { - @Override - public Optional build(T owner, ImportedClasses importContext) { - return Optional.of( - new DomainBuilders.JavaEnumConstantBuilder() - .withDeclaringClass(importContext.getOrResolve(JavaClassDescriptorImporter.importAsmTypeFromDescriptor(desc).getFullyQualifiedClassName())) - .withName(value) - .build()); - } - }; + JavaClassDescriptor enumType = JavaClassDescriptorImporter.importAsmTypeFromDescriptor(desc); + return ValueBuilder.fromEnumProperty(enumType, value); } private static class AnnotationTypeConversion { static ValueBuilder convert(Object input) { final Object value = JavaClassDescriptorImporter.importAsmTypeIfPossible(input); if (value instanceof JavaClassDescriptor) { - return new ValueBuilder() { - @Override - public Optional build(T owner, ImportedClasses importContext) { - return Optional.of(importContext.getOrResolve(((JavaClassDescriptor) value).getFullyQualifiedClassName())); - } - }; + return ValueBuilder.fromClassProperty((JavaClassDescriptor) value); } - return ValueBuilder.ofFinished(value); + return ValueBuilder.fromPrimitiveProperty(value); } } } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ImportTestUtils.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ImportTestUtils.java index ac20fcb648..00d6350a91 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ImportTestUtils.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ImportTestUtils.java @@ -43,6 +43,7 @@ import com.tngtech.archunit.core.domain.JavaType; import com.tngtech.archunit.core.domain.JavaTypeVariable; import com.tngtech.archunit.core.importer.DomainBuilders.FieldAccessTargetBuilder; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaAnnotationBuilder.ValueBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaMethodCallBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaTypeCreationProcess; import com.tngtech.archunit.core.importer.resolvers.ClassResolver; @@ -270,7 +271,7 @@ private static DomainBuilders.JavaAnnotationBuilder javaAnnotationBuilderFrom(An DomainBuilders.JavaAnnotationBuilder builder = new DomainBuilders.JavaAnnotationBuilder() .withType(JavaClassDescriptor.From.name(annotation.annotationType().getName())); for (Map.Entry entry : mapOf(annotation, annotatedClass, importedClasses).entrySet()) { - builder.addProperty(entry.getKey(), DomainBuilders.JavaAnnotationBuilder.ValueBuilder.ofFinished(entry.getValue())); + builder.addProperty(entry.getKey(), ValueBuilder.fromPrimitiveProperty(entry.getValue())); } return builder; } From 5c710de263d7a455b7e7fb23602a12be996614fa Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Fri, 14 Jan 2022 19:44:31 +0700 Subject: [PATCH 04/22] group automatic resolution tests together ... so it is easier to have an overview of all the automatic resolution processes that have been covered. I also added a test that parameter annotations are automatically resolved, and that access target owners are automatically resolved. Signed-off-by: Peter Gafert --- ...ssFileImporterAutomaticResolutionTest.java | 458 ++++++++++++++++++ .../ClassFileImporterAnnotationsTest.java | 130 +---- .../ClassFileImporterMembersTest.java | 57 --- ...lassWithMethodWithAnnotatedParameters.java | 2 +- 4 files changed, 460 insertions(+), 187 deletions(-) create mode 100644 archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java new file mode 100644 index 0000000000..16b006ac88 --- /dev/null +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java @@ -0,0 +1,458 @@ +package com.tngtech.archunit.core.importer; + +import java.io.File; +import java.io.PrintStream; +import java.nio.Buffer; +import java.nio.file.FileSystem; +import java.nio.file.Path; +import java.util.function.Supplier; + +import com.tngtech.archunit.core.domain.JavaAnnotation; +import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.domain.JavaConstructor; +import com.tngtech.archunit.core.domain.JavaConstructorCall; +import com.tngtech.archunit.core.domain.JavaConstructorReference; +import com.tngtech.archunit.core.domain.JavaEnumConstant; +import com.tngtech.archunit.core.domain.JavaFieldAccess; +import com.tngtech.archunit.core.domain.JavaMethod; +import com.tngtech.archunit.core.domain.JavaMethodCall; +import com.tngtech.archunit.core.domain.JavaMethodReference; +import com.tngtech.archunit.core.domain.properties.HasAnnotations; +import com.tngtech.archunit.core.importer.testexamples.SomeAnnotation; +import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassWithUnimportedAnnotation; +import com.tngtech.archunit.core.importer.testexamples.annotatedparameters.ClassWithMethodWithAnnotatedParameters; +import com.tngtech.archunit.core.importer.testexamples.annotatedparameters.ClassWithMethodWithAnnotatedParameters.SomeParameterAnnotation; +import com.tngtech.archunit.core.importer.testexamples.annotationfieldimport.ClassWithAnnotatedFields; +import com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods; +import com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.MethodAnnotationWithEnumAndArrayValue; +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.tngtech.archunit.core.importer.testexamples.SomeEnum.OTHER_VALUE; +import static com.tngtech.archunit.core.importer.testexamples.SomeEnum.SOME_VALUE; +import static com.tngtech.archunit.core.importer.testexamples.annotatedparameters.ClassWithMethodWithAnnotatedParameters.methodWithOneAnnotatedParameterWithTwoAnnotations; +import static com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.methodAnnotatedWithAnnotationFromParentPackage; +import static com.tngtech.archunit.testutil.Assertions.assertThat; +import static com.tngtech.archunit.testutil.Assertions.assertThatAnnotation; +import static com.tngtech.archunit.testutil.Assertions.assertThatType; +import static com.tngtech.archunit.testutil.assertion.JavaAnnotationAssertion.annotationProperty; +import static com.tngtech.java.junit.dataprovider.DataProviders.testForEach; + +@RunWith(DataProviderRunner.class) +public class ClassFileImporterAutomaticResolutionTest { + + @Test + public void automatically_resolves_field_types() { + @SuppressWarnings("unused") + class FieldTypeWithoutAnyFurtherReference { + String field; + } + + JavaClass javaClass = new ClassFileImporter().importClass(FieldTypeWithoutAnyFurtherReference.class); + + assertThat(javaClass.getField("field").getRawType()).as("field type").isFullyImported(true); + } + + @Test + public void automatically_resolves_constructor_parameter_types() { + @SuppressWarnings("unused") + class ConstructorParameterTypesWithoutAnyFurtherReference { + ConstructorParameterTypesWithoutAnyFurtherReference(FileSystem constructorParam1, Buffer constructorParam2) { + } + } + + JavaClass javaClass = new ClassFileImporter().importClass(ConstructorParameterTypesWithoutAnyFurtherReference.class); + + JavaConstructor constructor = javaClass.getConstructor(getClass(), FileSystem.class, Buffer.class); + assertThat(constructor.getRawParameterTypes().get(0)).as("constructor parameter type").isFullyImported(true); + assertThat(constructor.getRawParameterTypes().get(1)).as("constructor parameter type").isFullyImported(true); + } + + @Test + public void automatically_resolves_method_return_types() { + @SuppressWarnings("unused") + class MemberTypesWithoutAnyFurtherReference { + File returnType() { + return null; + } + } + + JavaClass javaClass = new ClassFileImporter().importClass(MemberTypesWithoutAnyFurtherReference.class); + + assertThat(javaClass.getMethod("returnType").getRawReturnType()).as("method return type").isFullyImported(true); + } + + @Test + public void automatically_resolves_method_parameter_types() { + @SuppressWarnings("unused") + class MemberTypesWithoutAnyFurtherReference { + void methodParameters(Path methodParam1, PrintStream methodParam2) { + } + } + + JavaClass javaClass = new ClassFileImporter().importClass(MemberTypesWithoutAnyFurtherReference.class); + + JavaMethod method = javaClass.getMethod("methodParameters", Path.class, PrintStream.class); + assertThat(method.getRawParameterTypes().get(0)).as("method parameter type").isFullyImported(true); + assertThat(method.getRawParameterTypes().get(1)).as("method parameter type").isFullyImported(true); + } + + @Test + public void automatically_resolves_class_annotations() { + JavaClass clazz = new ClassFileImporter().importClass(ClassWithUnimportedAnnotation.class); + + JavaAnnotation annotation = clazz.getAnnotationOfType(SomeAnnotation.class.getName()); + + assertThat(annotation.getRawType()).isFullyImported(true); + + assertThat(annotation.get("mandatory")).contains("mandatory"); + assertThat(annotation.get("optional")).contains("optional"); + assertThat((JavaEnumConstant) annotation.get("mandatoryEnum").get()).isEquivalentTo(SOME_VALUE); + assertThat((JavaEnumConstant) annotation.get("optionalEnum").get()).isEquivalentTo(OTHER_VALUE); + + SomeAnnotation reflected = clazz.getAnnotationOfType(SomeAnnotation.class); + assertThat(reflected.mandatory()).isEqualTo("mandatory"); + assertThat(reflected.optional()).isEqualTo("optional"); + assertThat(reflected.mandatoryEnum()).isEqualTo(SOME_VALUE); + assertThat(reflected.optionalEnum()).isEqualTo(OTHER_VALUE); + } + + @Test + public void automatically_resolves_field_annotations() { + JavaClass clazz = new ClassFileImporter().importClass(ClassWithAnnotatedFields.class); + + JavaAnnotation annotation = clazz.getField("fieldAnnotatedWithAnnotationFromParentPackage") + .getAnnotationOfType(SomeAnnotation.class.getName()); + + assertThat(annotation.getRawType()).isFullyImported(true); + + assertThat(annotation.get("mandatory")).contains("mandatory"); + assertThat(annotation.get("optional")).contains("optional"); + + SomeAnnotation reflected = annotation.as(SomeAnnotation.class); + assertThat(reflected.mandatory()).isEqualTo("mandatory"); + assertThat(reflected.optional()).isEqualTo("optional"); + } + + @Test + public void automatically_resolves_method_annotations() { + JavaClass clazz = new ClassFileImporter().importClass(ClassWithAnnotatedMethods.class); + + JavaAnnotation annotation = clazz.getMethod(methodAnnotatedWithAnnotationFromParentPackage) + .getAnnotationOfType(SomeAnnotation.class.getName()); + + assertThat(annotation.getRawType()).isFullyImported(true); + + assertThat(annotation.get("mandatory")).contains("mandatory"); + assertThat(annotation.get("optional")).contains("optional"); + + SomeAnnotation reflected = annotation.as(SomeAnnotation.class); + assertThat(reflected.mandatory()).isEqualTo("mandatory"); + assertThat(reflected.optional()).isEqualTo("optional"); + } + + @Test + public void automatically_resolves_constructor_annotations() { + JavaClass clazz = new ClassFileImporter().importClass(ClassWithAnnotatedMethods.class); + + JavaAnnotation annotation = clazz.getConstructor() + .getAnnotationOfType(MethodAnnotationWithEnumAndArrayValue.class.getName()); + + assertThat(annotation.getRawType()).isFullyImported(true); + + assertThat((JavaEnumConstant) annotation.get("value").get()).isEquivalentTo(OTHER_VALUE); + + MethodAnnotationWithEnumAndArrayValue reflected = annotation.as(MethodAnnotationWithEnumAndArrayValue.class); + assertThat(reflected.value()).isEqualTo(OTHER_VALUE); + } + + @Test + public void automatically_resolves_parameter_annotations() { + JavaClass clazz = new ClassFileImporter().importClass(ClassWithMethodWithAnnotatedParameters.class); + + JavaAnnotation annotation = clazz.getMethod(methodWithOneAnnotatedParameterWithTwoAnnotations, String.class) + .getParameters().get(0) + .getAnnotationOfType(SomeParameterAnnotation.class.getName()); + + assertThat(annotation.getRawType()).isFullyImported(true); + + assertThat((JavaEnumConstant) annotation.get("value").get()).isEquivalentTo(OTHER_VALUE); + assertThat((JavaEnumConstant) annotation.get("valueWithDefault").get()).isEquivalentTo(SOME_VALUE); + + SomeParameterAnnotation reflected = annotation.as(SomeParameterAnnotation.class); + assertThat(reflected.value()).isEqualTo(OTHER_VALUE); + assertThat(reflected.valueWithDefault()).isEqualTo(SOME_VALUE); + } + + @Test + public void automatically_resolves_meta_annotation_types() { + JavaClass javaClass = new ClassFileImporter().importClass(MetaAnnotatedClass.class); + JavaAnnotation someAnnotation = javaClass.getAnnotationOfType(MetaAnnotatedAnnotation.class.getName()); + JavaAnnotation someMetaAnnotation = someAnnotation.getRawType() + .getAnnotationOfType(SomeMetaAnnotation.class.getName()); + JavaAnnotation someMetaMetaAnnotation = someMetaAnnotation.getRawType() + .getAnnotationOfType(SomeMetaMetaAnnotation.class.getName()); + JavaAnnotation someMetaMetaMetaAnnotation = someMetaMetaAnnotation.getRawType() + .getAnnotationOfType(SomeMetaMetaMetaAnnotationWithParameters.class.getName()); + + assertThatType(someMetaMetaMetaAnnotation.getType()).matches(SomeMetaMetaMetaAnnotationWithParameters.class); + } + + @DataProvider + public static Object[][] elementsAnnotatedWithSomeAnnotation() { + return testForEach( + new ClassFileImporter().importClass(MetaAnnotatedClass.class), + new ClassFileImporter().importClass(ClassWithMetaAnnotatedField.class).getField("metaAnnotatedField"), + new ClassFileImporter().importClass(ClassWithMetaAnnotatedMethod.class).getMethod("metaAnnotatedMethod"), + new ClassFileImporter().importClass(ClassWithMetaAnnotatedConstructor.class).getConstructor(), + getOnlyElement(new ClassFileImporter().importClass(ClassWithMetaAnnotatedConstructorParameter.class).getConstructor(String.class).getParameters()), + getOnlyElement(new ClassFileImporter().importClass(ClassWithMetaAnnotatedMethodParameter.class).getMethod("method", String.class).getParameters()) + ); + } + + @Test + @UseDataProvider("elementsAnnotatedWithSomeAnnotation") + public void automatically_resolves_parameters_of_meta_annotations(HasAnnotations annotatedWithSomeAnnotation) { + JavaAnnotation someAnnotation = annotatedWithSomeAnnotation + .getAnnotationOfType(MetaAnnotatedAnnotation.class.getName()); + JavaAnnotation metaAnnotationWithParameters = someAnnotation.getRawType() + .getAnnotationOfType(MetaAnnotationWithParameters.class.getName()); + + assertThatAnnotation(metaAnnotationWithParameters) + .hasEnumProperty("someEnum", SomeAnnotationEnum.CONSTANT) + .hasEnumProperty("someEnumDefault", SomeAnnotationEnum.VARIABLE) + .hasAnnotationProperty("parameterAnnotation", + annotationProperty() + .withAnnotationType(ParameterAnnotation.class) + .withClassProperty("value", SomeAnnotationParameterType.class)) + .hasAnnotationProperty("parameterAnnotationDefault", + annotationProperty() + .withAnnotationType(ParameterAnnotation.class) + .withClassProperty("value", SomeAnnotationDefaultParameterType.class)); + + JavaAnnotation metaMetaMetaAnnotation = someAnnotation + .getRawType().getAnnotationOfType(SomeMetaAnnotation.class.getName()) + .getRawType().getAnnotationOfType(SomeMetaMetaAnnotation.class.getName()) + .getRawType().getAnnotationOfType(SomeMetaMetaMetaAnnotationWithParameters.class.getName()); + + assertThatAnnotation(metaMetaMetaAnnotation) + .hasClassProperty("classParam", SomeMetaMetaMetaAnnotationClassParameter.class) + .hasClassProperty("classParamDefault", String.class) + .hasEnumProperty("enumParam", SomeMetaMetaMetaAnnotationEnumParameter.VALUE) + .hasEnumProperty("enumParamDefault", SomeMetaMetaMetaAnnotationEnumParameter.CONSTANT) + .hasAnnotationProperty("annotationParam", + annotationProperty() + .withAnnotationType(SomeMetaMetaMetaParameterAnnotation.class) + .withClassProperty("value", SomeMetaMetaMetaParameterAnnotationClassParameter.class)) + .hasAnnotationProperty("annotationParamDefault", + annotationProperty() + .withAnnotationType(SomeMetaMetaMetaParameterAnnotation.class)); + } + + @Test + public void automatically_resolves_field_access_target_owners() { + class Target { + String field; + } + @SuppressWarnings({"unused", "ConstantConditions"}) + class Origin { + Object resolvesFieldAccessOwner() { + Target target = null; + return target.field; + } + } + + JavaFieldAccess access = getOnlyElement(new ClassFileImporter().importClass(Origin.class).getMethod("resolvesFieldAccessOwner").getFieldAccesses()); + + assertThat(access.getTargetOwner()).isFullyImported(true); + } + + @Test + public void automatically_resolves_method_call_target_owners() { + class Target { + void method() { + } + } + @SuppressWarnings({"unused", "ConstantConditions"}) + class Origin { + void resolvesMethodCallTargetOwner() { + Target target = null; + target.method(); + } + } + + JavaMethodCall call = getOnlyElement(new ClassFileImporter().importClass(Origin.class).getMethodCallsFromSelf()); + + assertThat(call.getTargetOwner()).isFullyImported(true); + } + + @Test + public void automatically_resolves_method_reference_target_owners() { + class Target { + void method() { + } + } + @SuppressWarnings({"unused", "ConstantConditions"}) + class Origin { + Runnable resolvesMethodCallTargetOwner() { + Target target = null; + return target::method; + } + } + + JavaMethodReference reference = getOnlyElement(new ClassFileImporter().importClass(Origin.class).getMethodReferencesFromSelf()); + + assertThat(reference.getTargetOwner()).isFullyImported(true); + } + + @Test + public void automatically_resolves_constructor_call_target_owners() { + class Target { + } + @SuppressWarnings("unused") + class Origin { + void resolvesConstructorCallTargetOwner() { + new Target(); + } + } + + JavaClass javaClass = new ClassFileImporter().importClass(Origin.class); + JavaConstructorCall call = getOnlyElement(javaClass.getMethod("resolvesConstructorCallTargetOwner").getConstructorCallsFromSelf()); + + assertThat(call.getTargetOwner()).isFullyImported(true); + } + + private static class Data_automatically_resolves_constructor_reference_target_owners { + static class Target { + } + } + + @Test + public void automatically_resolves_constructor_reference_target_owners() { + @SuppressWarnings("unused") + class Origin { + Supplier resolvesConstructorReferenceTargetOwner() { + return Data_automatically_resolves_constructor_reference_target_owners.Target::new; + } + } + + JavaClass javaClass = new ClassFileImporter().importClass(Origin.class); + JavaConstructorReference reference = getOnlyElement(javaClass.getMethod("resolvesConstructorReferenceTargetOwner").getConstructorReferencesFromSelf()); + + assertThat(reference.getTargetOwner()).isFullyImported(true); + } + + @MetaAnnotatedAnnotation + private static class MetaAnnotatedClass { + } + + @SuppressWarnings("unused") + private @interface MetaAnnotationWithParameters { + SomeAnnotationEnum someEnum(); + + SomeAnnotationEnum someEnumDefault() default SomeAnnotationEnum.VARIABLE; + + ParameterAnnotation parameterAnnotation(); + + ParameterAnnotation parameterAnnotationDefault() default @ParameterAnnotation(SomeAnnotationDefaultParameterType.class); + } + + private @interface SomeMetaMetaMetaAnnotationWithParameters { + Class classParam(); + + Class classParamDefault() default String.class; + + SomeMetaMetaMetaAnnotationEnumParameter enumParam(); + + SomeMetaMetaMetaAnnotationEnumParameter enumParamDefault() default SomeMetaMetaMetaAnnotationEnumParameter.CONSTANT; + + SomeMetaMetaMetaParameterAnnotation annotationParam(); + + SomeMetaMetaMetaParameterAnnotation annotationParamDefault() default @SomeMetaMetaMetaParameterAnnotation(Boolean.class); + } + + @SomeMetaMetaMetaAnnotationWithParameters( + classParam = SomeMetaMetaMetaAnnotationClassParameter.class, + enumParam = SomeMetaMetaMetaAnnotationEnumParameter.VALUE, + annotationParam = @SomeMetaMetaMetaParameterAnnotation(SomeMetaMetaMetaParameterAnnotationClassParameter.class) + ) + private @interface SomeMetaMetaAnnotation { + } + + @SomeMetaMetaAnnotation + private @interface SomeMetaAnnotation { + } + + @MetaAnnotationWithParameters( + someEnum = SomeAnnotationEnum.CONSTANT, + parameterAnnotation = @ParameterAnnotation(SomeAnnotationParameterType.class) + ) + @SomeMetaAnnotation + private @interface MetaAnnotatedAnnotation { + } + + private enum SomeAnnotationEnum { + CONSTANT, + VARIABLE + } + + private static class SomeAnnotationDefaultParameterType { + } + + private @interface ParameterAnnotation { + Class value(); + } + + private static class SomeAnnotationParameterType { + } + + @SuppressWarnings("unused") + private static class ClassWithMetaAnnotatedField { + @MetaAnnotatedAnnotation + int metaAnnotatedField; + } + + @SuppressWarnings("unused") + private static class ClassWithMetaAnnotatedMethod { + @MetaAnnotatedAnnotation + void metaAnnotatedMethod() { + } + } + + private static class ClassWithMetaAnnotatedConstructor { + @MetaAnnotatedAnnotation + ClassWithMetaAnnotatedConstructor() { + } + } + + @SuppressWarnings("unused") + private static class ClassWithMetaAnnotatedConstructorParameter { + ClassWithMetaAnnotatedConstructorParameter(@MetaAnnotatedAnnotation String param) { + } + } + + @SuppressWarnings("unused") + private static class ClassWithMetaAnnotatedMethodParameter { + void method(@MetaAnnotatedAnnotation String param) { + } + } + + private static class SomeMetaMetaMetaAnnotationClassParameter { + } + + private enum SomeMetaMetaMetaAnnotationEnumParameter { + VALUE, + CONSTANT + } + + private @interface SomeMetaMetaMetaParameterAnnotation { + Class value(); + } + + private static class SomeMetaMetaMetaParameterAnnotationClassParameter { + } +} diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterAnnotationsTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterAnnotationsTest.java index 3481d874df..d607bd96bf 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterAnnotationsTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterAnnotationsTest.java @@ -18,13 +18,10 @@ import com.tngtech.archunit.core.domain.JavaField; import com.tngtech.archunit.core.domain.JavaMethod; import com.tngtech.archunit.core.domain.JavaParameter; -import com.tngtech.archunit.core.domain.properties.HasAnnotations; -import com.tngtech.archunit.core.importer.testexamples.SomeAnnotation; import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassAnnotationWithArrays; import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassWithAnnotationWithEmptyArrays; import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassWithComplexAnnotations; import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassWithOneAnnotation; -import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassWithUnimportedAnnotation; import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.SimpleAnnotation; import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.TypeAnnotationWithEnumAndArrayValue; import com.tngtech.archunit.core.importer.testexamples.annotatedparameters.ClassWithMethodWithAnnotatedParameters; @@ -35,9 +32,7 @@ import com.tngtech.archunit.core.importer.testexamples.simpleimport.AnnotationParameter; import com.tngtech.archunit.core.importer.testexamples.simpleimport.AnnotationToImport; import com.tngtech.archunit.core.importer.testexamples.simpleimport.EnumToImport; -import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; -import com.tngtech.java.junit.dataprovider.UseDataProvider; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,7 +40,6 @@ import static com.tngtech.archunit.core.importer.testexamples.SomeEnum.OTHER_VALUE; import static com.tngtech.archunit.core.importer.testexamples.SomeEnum.SOME_VALUE; import static com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.enumAndArrayAnnotatedMethod; -import static com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.methodAnnotatedWithAnnotationFromParentPackage; import static com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.methodAnnotatedWithEmptyArrays; import static com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.stringAndIntAnnotatedMethod; import static com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.stringAnnotatedMethod; @@ -53,8 +47,6 @@ import static com.tngtech.archunit.testutil.Assertions.assertThatAnnotation; import static com.tngtech.archunit.testutil.Assertions.assertThatAnnotations; import static com.tngtech.archunit.testutil.Assertions.assertThatType; -import static com.tngtech.archunit.testutil.assertion.JavaAnnotationAssertion.annotationProperty; -import static com.tngtech.java.junit.dataprovider.DataProviders.testForEach; @RunWith(DataProviderRunner.class) public class ClassFileImporterAnnotationsTest { @@ -204,25 +196,6 @@ public void imports_class_with_annotation_with_empty_array() { assertThat(reflected.annotations()).isEmpty(); } - @Test - public void imports_class_annotated_with_unimported_annotation() { - JavaClass clazz = new ClassFileImporter().importPackagesOf(ClassWithUnimportedAnnotation.class) - .get(ClassWithUnimportedAnnotation.class); - - JavaAnnotation annotation = clazz.getAnnotationOfType(SomeAnnotation.class.getName()); - - assertThat(annotation.get("mandatory")).contains("mandatory"); - assertThat(annotation.get("optional")).contains("optional"); - assertThat((JavaEnumConstant) annotation.get("mandatoryEnum").get()).isEquivalentTo(SOME_VALUE); - assertThat((JavaEnumConstant) annotation.get("optionalEnum").get()).isEquivalentTo(OTHER_VALUE); - - SomeAnnotation reflected = clazz.getAnnotationOfType(SomeAnnotation.class); - assertThat(reflected.mandatory()).isEqualTo("mandatory"); - assertThat(reflected.optional()).isEqualTo("optional"); - assertThat(reflected.mandatoryEnum()).isEqualTo(SOME_VALUE); - assertThat(reflected.optionalEnum()).isEqualTo(OTHER_VALUE); - } - @Test public void imports_fields_with_one_annotation_correctly() throws Exception { JavaField field = new ClassFileImporter().importPackagesOf(ClassWithAnnotatedFields.class) @@ -321,21 +294,6 @@ public void imports_fields_with_annotation_with_empty_array() { assertThat(reflected.annotations()).isEmpty(); } - @Test - public void imports_fields_annotated_with_unimported_annotation() { - JavaClass clazz = new ClassFileImporter().importPackagesOf(ClassWithAnnotatedFields.class).get(ClassWithAnnotatedFields.class); - - JavaAnnotation annotation = clazz.getField("fieldAnnotatedWithAnnotationFromParentPackage") - .getAnnotationOfType(SomeAnnotation.class.getName()); - - assertThat(annotation.get("mandatory")).contains("mandatory"); - assertThat(annotation.get("optional")).contains("optional"); - - SomeAnnotation reflected = annotation.as(SomeAnnotation.class); - assertThat(reflected.mandatory()).isEqualTo("mandatory"); - assertThat(reflected.optional()).isEqualTo("optional"); - } - @Test public void imports_methods_with_one_annotation_correctly() throws Exception { JavaMethod method = new ClassFileImporter().importPackagesOf(ClassWithAnnotatedMethods.class) @@ -432,21 +390,6 @@ public void imports_method_with_annotation_with_empty_array() { assertThat(reflected.annotations()).isEmpty(); } - @Test - public void imports_methods_annotated_with_unimported_annotation() { - JavaClass clazz = new ClassFileImporter().importPackagesOf(ClassWithAnnotatedMethods.class).get(ClassWithAnnotatedMethods.class); - - JavaAnnotation annotation = clazz.getMethod(methodAnnotatedWithAnnotationFromParentPackage) - .getAnnotationOfType(SomeAnnotation.class.getName()); - - assertThat(annotation.get("mandatory")).contains("mandatory"); - assertThat(annotation.get("optional")).contains("optional"); - - SomeAnnotation reflected = annotation.as(SomeAnnotation.class); - assertThat(reflected.mandatory()).isEqualTo("mandatory"); - assertThat(reflected.optional()).isEqualTo("optional"); - } - @Test public void imports_empty_parameter_annotations_for_method_without_annotated_parameters() { JavaMethod method = new ClassFileImporter().importPackagesOf(ClassWithMethodWithAnnotatedParameters.class) @@ -640,71 +583,6 @@ void notToFind() { assertThat(annotations).as("annotations with parameter type " + String.class.getSimpleName()).containsOnlyElementsOf(expected); } - @Test - public void meta_annotation_types_are_transitively_imported() { - JavaClass javaClass = new ClassFileImporter().importClass(MetaAnnotatedClass.class); - JavaAnnotation someAnnotation = javaClass.getAnnotationOfType(MetaAnnotatedAnnotation.class.getName()); - JavaAnnotation someMetaAnnotation = someAnnotation.getRawType() - .getAnnotationOfType(SomeMetaAnnotation.class.getName()); - JavaAnnotation someMetaMetaAnnotation = someMetaAnnotation.getRawType() - .getAnnotationOfType(SomeMetaMetaAnnotation.class.getName()); - JavaAnnotation someMetaMetaMetaAnnotation = someMetaMetaAnnotation.getRawType() - .getAnnotationOfType(SomeMetaMetaMetaAnnotationWithParameters.class.getName()); - - assertThatType(someMetaMetaMetaAnnotation.getType()).matches(SomeMetaMetaMetaAnnotationWithParameters.class); - } - - @DataProvider - public static Object[][] elementsAnnotatedWithSomeAnnotation() { - return testForEach( - new ClassFileImporter().importClass(MetaAnnotatedClass.class), - new ClassFileImporter().importClass(ClassWithMetaAnnotatedField.class).getField("metaAnnotatedField"), - new ClassFileImporter().importClass(ClassWithMetaAnnotatedMethod.class).getMethod("metaAnnotatedMethod"), - new ClassFileImporter().importClass(ClassWithMetaAnnotatedConstructor.class).getConstructor(), - getOnlyElement(new ClassFileImporter().importClass(ClassWithMetaAnnotatedConstructorParameter.class).getConstructor(String.class).getParameters()), - getOnlyElement(new ClassFileImporter().importClass(ClassWithMetaAnnotatedMethodParameter.class).getMethod("method", String.class).getParameters()) - ); - } - - @Test - @UseDataProvider("elementsAnnotatedWithSomeAnnotation") - public void parameters_of_meta_annotations_are_transitively_imported(HasAnnotations annotatedWithSomeAnnotation) { - JavaAnnotation someAnnotation = annotatedWithSomeAnnotation - .getAnnotationOfType(MetaAnnotatedAnnotation.class.getName()); - JavaAnnotation metaAnnotationWithParameters = someAnnotation.getRawType() - .getAnnotationOfType(MetaAnnotationWithParameters.class.getName()); - - assertThatAnnotation(metaAnnotationWithParameters) - .hasEnumProperty("someEnum", SomeEnum.CONSTANT) - .hasEnumProperty("someEnumDefault", SomeEnum.VARIABLE) - .hasAnnotationProperty("parameterAnnotation", - annotationProperty() - .withAnnotationType(ParameterAnnotation.class) - .withClassProperty("value", SomeAnnotationParameterType.class)) - .hasAnnotationProperty("parameterAnnotationDefault", - annotationProperty() - .withAnnotationType(ParameterAnnotation.class) - .withClassProperty("value", SomeAnnotationDefaultParameterType.class)); - - JavaAnnotation metaMetaMetaAnnotation = someAnnotation - .getRawType().getAnnotationOfType(SomeMetaAnnotation.class.getName()) - .getRawType().getAnnotationOfType(SomeMetaMetaAnnotation.class.getName()) - .getRawType().getAnnotationOfType(SomeMetaMetaMetaAnnotationWithParameters.class.getName()); - - assertThatAnnotation(metaMetaMetaAnnotation) - .hasClassProperty("classParam", SomeMetaMetaMetaAnnotationClassParameter.class) - .hasClassProperty("classParamDefault", String.class) - .hasEnumProperty("enumParam", SomeMetaMetaMetaAnnotationEnumParameter.VALUE) - .hasEnumProperty("enumParamDefault", SomeMetaMetaMetaAnnotationEnumParameter.CONSTANT) - .hasAnnotationProperty("annotationParam", - annotationProperty() - .withAnnotationType(SomeMetaMetaMetaParameterAnnotation.class) - .withClassProperty("value", SomeMetaMetaMetaParameterAnnotationClassParameter.class)) - .hasAnnotationProperty("annotationParamDefault", - annotationProperty() - .withAnnotationType(SomeMetaMetaMetaParameterAnnotation.class)); - } - @SuppressWarnings({"unchecked", "unused"}) private static T getAnnotationDefaultValue(JavaClass javaClass, String methodName, Class valueType) { return (T) javaClass.getMethod(methodName).getDefaultValue().get(); @@ -729,7 +607,7 @@ private static T getAnnotationDefaultValue(JavaClass javaClass, String metho ParameterAnnotation parameterAnnotation(); - ParameterAnnotation parameterAnnotationDefault() default @ParameterAnnotation(SomeAnnotationDefaultParameterType.class); + ParameterAnnotation parameterAnnotationDefault() default @ParameterAnnotation(Integer.class); } @SuppressWarnings("unused") @@ -779,12 +657,6 @@ private enum SomeEnum { private static class SomeAnnotationParameterType { } - private static class SomeAnnotationDefaultParameterType { - } - - @MetaAnnotatedAnnotation - private static class MetaAnnotatedClass { - } @SuppressWarnings("unused") private static class ClassWithMetaAnnotatedField { diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterMembersTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterMembersTest.java index bd8011b334..ca80b26188 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterMembersTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterMembersTest.java @@ -2,7 +2,6 @@ import java.io.File; import java.io.FilterInputStream; -import java.io.PrintStream; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -352,60 +351,4 @@ public void classes_know_which_instanceof_checks_check_their_type() { } assertThatTypes(origins).matchInAnyOrder(ChecksInstanceofInMethod.class, ChecksInstanceofInConstructor.class, ChecksInstanceofInStaticInitializer.class); } - - @Test - public void automatically_resolves_field_types() { - @SuppressWarnings("unused") - class FieldTypeWithoutAnyFurtherReference { - String field; - } - - JavaClass javaClass = new ClassFileImporter().importClass(FieldTypeWithoutAnyFurtherReference.class); - - assertThat(javaClass.getField("field").getRawType()).as("field type").isFullyImported(true); - } - - @Test - public void automatically_resolves_constructor_parameter_types() { - @SuppressWarnings("unused") - class ConstructorParameterTypesWithoutAnyFurtherReference { - ConstructorParameterTypesWithoutAnyFurtherReference(FileSystem constructorParam1, Buffer constructorParam2) { - } - } - - JavaClass javaClass = new ClassFileImporter().importClass(ConstructorParameterTypesWithoutAnyFurtherReference.class); - - JavaConstructor constructor = javaClass.getConstructor(getClass(), FileSystem.class, Buffer.class); - assertThat(constructor.getRawParameterTypes().get(0)).as("constructor parameter type").isFullyImported(true); - assertThat(constructor.getRawParameterTypes().get(1)).as("constructor parameter type").isFullyImported(true); - } - - @Test - public void automatically_resolves_method_return_types() { - @SuppressWarnings("unused") - class MemberTypesWithoutAnyFurtherReference { - File returnType() { - return null; - } - } - - JavaClass javaClass = new ClassFileImporter().importClass(MemberTypesWithoutAnyFurtherReference.class); - - assertThat(javaClass.getMethod("returnType").getRawReturnType()).as("method return type").isFullyImported(true); - } - - @Test - public void automatically_resolves_method_parameter_types() { - @SuppressWarnings("unused") - class MemberTypesWithoutAnyFurtherReference { - void methodParameters(Path methodParam1, PrintStream methodParam2) { - } - } - - JavaClass javaClass = new ClassFileImporter().importClass(MemberTypesWithoutAnyFurtherReference.class); - - JavaMethod method = javaClass.getMethod("methodParameters", Path.class, PrintStream.class); - assertThat(method.getRawParameterTypes().get(0)).as("method parameter type").isFullyImported(true); - assertThat(method.getRawParameterTypes().get(1)).as("method parameter type").isFullyImported(true); - } } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/annotatedparameters/ClassWithMethodWithAnnotatedParameters.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/annotatedparameters/ClassWithMethodWithAnnotatedParameters.java index 23f5e76f90..6ac0aa9cce 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/annotatedparameters/ClassWithMethodWithAnnotatedParameters.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/testexamples/annotatedparameters/ClassWithMethodWithAnnotatedParameters.java @@ -78,7 +78,7 @@ void methodWithAnnotatedParametersGap( } @Retention(RUNTIME) - @interface SomeParameterAnnotation { + public @interface SomeParameterAnnotation { SomeEnum value(); SomeEnum valueWithDefault() default SOME_VALUE; From 63ecee5850a07166885a315e2d09d460a808a038 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sat, 29 Jan 2022 11:27:51 +0700 Subject: [PATCH 05/22] never report stub classes as "fully imported" So far the semantic of `isFullyImported()` was a little broken. When we create the stub class before the general completion process starts it will still run through all the completion steps and thus be reported as "fully imported". This is not correct though, a stub class we create can never be fully imported, because we are missing vital information from the bytecode. I have adjusted this to propagate the information that this class is a stub class and in that case never report it as "fully imported". Signed-off-by: Peter Gafert --- ...ssFileImporterAutomaticResolutionTest.java | 24 +++++ .../archunit/core/domain/JavaClass.java | 88 +++++++++++++++++-- .../core/importer/DomainBuilders.java | 14 +++ .../core/importer/ImportedClasses.java | 9 +- 4 files changed, 122 insertions(+), 13 deletions(-) diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java index 16b006ac88..a9407de276 100644 --- a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java @@ -2,11 +2,13 @@ import java.io.File; import java.io.PrintStream; +import java.io.Serializable; import java.nio.Buffer; import java.nio.file.FileSystem; import java.nio.file.Path; import java.util.function.Supplier; +import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.core.domain.JavaAnnotation; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaConstructor; @@ -36,6 +38,7 @@ import static com.tngtech.archunit.core.importer.testexamples.SomeEnum.SOME_VALUE; import static com.tngtech.archunit.core.importer.testexamples.annotatedparameters.ClassWithMethodWithAnnotatedParameters.methodWithOneAnnotatedParameterWithTwoAnnotations; import static com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.methodAnnotatedWithAnnotationFromParentPackage; +import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThat; import static com.tngtech.archunit.testutil.Assertions.assertThatAnnotation; import static com.tngtech.archunit.testutil.Assertions.assertThatType; @@ -346,6 +349,27 @@ Supplier resolvesConstructorReferenceTargetOwner() { assertThat(reference.getTargetOwner()).isFullyImported(true); } + @Test + public void never_reports_stub_classes_as_fully_imported() { + @SuppressWarnings("unused") + class SomeClass { + Serializable withStubType; + } + + JavaClass javaClass = resetConfigurationAround(() -> { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return new ClassFileImporter().importClass(SomeClass.class); + }); + + JavaClass stubType = javaClass.getField("withStubType").getRawType(); + + // if we don't resolve the class from the classpath we don't know if it's an interface + assertThat(stubType).isInterface(false); + // then we also don't want to claim this class is fully imported, even though it went through the + // resolution steps to complete the hierarchy, etc. + assertThat(stubType).isFullyImported(false); + } + @MetaAnnotatedAnnotation private static class MetaAnnotatedClass { } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java index 3e597a40d4..4da56d0986 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java @@ -136,7 +136,7 @@ public Set get() { private Map> annotations = emptyMap(); private JavaClassDependencies javaClassDependencies = new JavaClassDependencies(this); // just for stubs; will be overwritten for imported classes private ReverseDependencies reverseDependencies = ReverseDependencies.EMPTY; // just for stubs; will be overwritten for imported classes - private final CompletionProcess completionProcess = CompletionProcess.start(); + private final CompletionProcess completionProcess; JavaClass(JavaClassBuilder builder) { source = checkNotNull(builder.getSource()); @@ -151,6 +151,7 @@ public Set get() { reflectSupplier = Suppliers.memoize(new ReflectClassSupplier()); sourceCodeLocation = SourceCodeLocation.of(this); javaPackage = JavaPackage.simple(this); + completionProcess = builder.isStub() ? CompletionProcess.stub() : CompletionProcess.start(); } /** @@ -1646,7 +1647,74 @@ static EnclosingDeclaration ofClass(Optional clazz) { } } - private static class CompletionProcess { + private abstract static class CompletionProcess { + private static final CompletionProcess stubCompletionProcess = new CompletionProcess() { + @Override + boolean hasFinished() { + return false; + } + + @Override + public void markClassHierarchyComplete() { + } + + @Override + public void markEnclosingDeclarationComplete() { + } + + @Override + public void markTypeParametersComplete() { + } + + @Override + public void markGenericSuperclassComplete() { + } + + @Override + public void markGenericInterfacesComplete() { + } + + @Override + public void markMembersComplete() { + } + + @Override + public void markAnnotationsComplete() { + } + + @Override + public void markDependenciesComplete() { + } + }; + + abstract boolean hasFinished(); + + public abstract void markClassHierarchyComplete(); + + public abstract void markEnclosingDeclarationComplete(); + + public abstract void markTypeParametersComplete(); + + public abstract void markGenericSuperclassComplete(); + + public abstract void markGenericInterfacesComplete(); + + public abstract void markMembersComplete(); + + public abstract void markAnnotationsComplete(); + + public abstract void markDependenciesComplete(); + + static CompletionProcess start() { + return new FullCompletionProcess(); + } + + static CompletionProcess stub() { + return stubCompletionProcess; + } + } + + private static class FullCompletionProcess extends CompletionProcess { private boolean classHierarchyComplete = false; private boolean enclosingDeclarationComplete = false; private boolean typeParametersComplete = false; @@ -1656,9 +1724,7 @@ private static class CompletionProcess { private boolean annotationsComplete = false; private boolean dependenciesComplete = false; - private CompletionProcess() { - } - + @Override boolean hasFinished() { return classHierarchyComplete && enclosingDeclarationComplete @@ -1670,41 +1736,45 @@ boolean hasFinished() { && dependenciesComplete; } + @Override public void markClassHierarchyComplete() { this.classHierarchyComplete = true; } + @Override public void markEnclosingDeclarationComplete() { this.enclosingDeclarationComplete = true; } + @Override public void markTypeParametersComplete() { this.typeParametersComplete = true; } + @Override public void markGenericSuperclassComplete() { this.genericSuperclassComplete = true; } + @Override public void markGenericInterfacesComplete() { this.genericInterfacesComplete = true; } + @Override public void markMembersComplete() { this.membersComplete = true; } + @Override public void markAnnotationsComplete() { this.annotationsComplete = true; } + @Override public void markDependenciesComplete() { this.dependenciesComplete = true; } - - static CompletionProcess start() { - return new CompletionProcess(); - } } public static final class Functions { diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/DomainBuilders.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/DomainBuilders.java index 0910d46018..fabd004a71 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/DomainBuilders.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/DomainBuilders.java @@ -431,6 +431,7 @@ JavaConstructor construct(JavaConstructorBuilder builder, ImportedClasses import @Internal public static final class JavaClassBuilder { + private final boolean stub; private Optional sourceDescriptor = Optional.empty(); private Optional sourceFileName = Optional.empty(); private JavaClassDescriptor descriptor; @@ -443,6 +444,11 @@ public static final class JavaClassBuilder { private Set modifiers = new HashSet<>(); JavaClassBuilder() { + this(false); + } + + private JavaClassBuilder(boolean stub) { + this.stub = stub; } JavaClassBuilder withSourceDescriptor(SourceDescriptor sourceDescriptor) { @@ -541,6 +547,14 @@ public boolean isMemberClass() { public Set getModifiers() { return modifiers; } + + public boolean isStub() { + return stub; + } + + static JavaClassBuilder forStub() { + return new JavaClassBuilder(true); + } } @Internal diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ImportedClasses.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ImportedClasses.java index ecc8061e60..febb119a7e 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ImportedClasses.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ImportedClasses.java @@ -27,6 +27,7 @@ import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaClassDescriptor; import com.tngtech.archunit.core.domain.JavaModifier; +import com.tngtech.archunit.core.importer.DomainBuilders.JavaClassBuilder; import com.tngtech.archunit.core.importer.resolvers.ClassResolver; import static com.tngtech.archunit.core.domain.JavaModifier.ABSTRACT; @@ -57,7 +58,7 @@ JavaClass getOrResolve(String typeName) { JavaClass javaClass = allClasses.get(typeName); if (javaClass == null) { Optional resolved = resolver.tryResolve(typeName); - javaClass = resolved.isPresent() ? resolved.get() : simpleClassOf(typeName); + javaClass = resolved.isPresent() ? resolved.get() : stubClassOf(typeName); allClasses.put(typeName, javaClass); } return javaClass; @@ -75,14 +76,14 @@ Collection getAllWithOuterClassesSortedBeforeInnerClasses() { return ImmutableSortedMap.copyOf(allClasses).values(); } - private static JavaClass simpleClassOf(String typeName) { + private static JavaClass stubClassOf(String typeName) { JavaClassDescriptor descriptor = JavaClassDescriptor.From.name(typeName); - DomainBuilders.JavaClassBuilder builder = new DomainBuilders.JavaClassBuilder().withDescriptor(descriptor); + JavaClassBuilder builder = JavaClassBuilder.forStub().withDescriptor(descriptor); addModifiersIfPossible(builder, descriptor); return builder.build(); } - private static void addModifiersIfPossible(DomainBuilders.JavaClassBuilder builder, JavaClassDescriptor descriptor) { + private static void addModifiersIfPossible(JavaClassBuilder builder, JavaClassDescriptor descriptor) { if (descriptor.isPrimitive() || descriptor.isArray()) { builder.withModifiers(PRIMITIVE_AND_ARRAY_TYPE_MODIFIERS); } From afca13a9754e333be6f9e34d7b9e16302ca8fecc Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sun, 9 Jan 2022 23:18:56 +0700 Subject: [PATCH 06/22] encapsulate resolving member types in `DependencyResolutionProcess` The automatic resolution of dependencies is one of the most complex parts of ArchUnit. We should thus separate it from the `ClassGraphCreator`. Also, at the moment the process is a little arbitrary. In each step (e.g. resolve member types) new types are added and handled in the following steps. Thus, for resolved member types we will resolve inheritance after, but vice versa we won't resolve member types for methods from superclasses resolved in the latter step. We should make this more predictable by separating the classes we found by a resolution step from the ones we found during the original import. For now, I have simply disabled further processing of type names as soon as the resolution starts, but in later steps we can make this more sophisticated. Signed-off-by: Peter Gafert --- .../core/importer/ClassFileImportRecord.java | 19 -------- .../core/importer/ClassFileProcessor.java | 20 ++++++--- .../core/importer/ClassGraphCreator.java | 12 ++--- .../importer/DependencyResolutionProcess.java | 44 +++++++++++++++++++ .../core/importer/JavaClassProcessor.java | 18 ++++---- 5 files changed, 71 insertions(+), 42 deletions(-) create mode 100644 archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java index 603c753a29..eb1c8c3e11 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java @@ -158,25 +158,6 @@ Optional>> getGenericInterfacesFor( return Optional.ofNullable(genericInterfaceBuildersByOwner.get(owner.getName())); } - Set getMemberSignatureTypeNames() { - ImmutableSet.Builder result = ImmutableSet.builder(); - for (JavaFieldBuilder fieldBuilder : fieldBuildersByOwner.values()) { - result.add(fieldBuilder.getTypeName()); - } - for (JavaConstructorBuilder constructorBuilder : constructorBuildersByOwner.values()) { - for (String parameterTypeName : constructorBuilder.getParameterTypeNames()) { - result.add(parameterTypeName); - } - } - for (JavaMethodBuilder methodBuilder : methodBuildersByOwner.values()) { - result.add(methodBuilder.getReturnTypeName()); - for (String parameterTypeName : methodBuilder.getParameterTypeNames()) { - result.add(parameterTypeName); - } - } - return result.build(); - } - Set getFieldBuildersFor(String ownerName) { return fieldBuildersByOwner.get(ownerName); } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java index c031622f48..8778c5eb5c 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java @@ -17,6 +17,7 @@ import java.io.InputStream; import java.net.URI; +import java.util.Collection; import java.util.List; import java.util.Set; @@ -55,8 +56,9 @@ class ClassFileProcessor { JavaClasses process(ClassFileSource source) { ClassFileImportRecord importRecord = new ClassFileImportRecord(); + DependencyResolutionProcess dependencyResolutionProcess = new DependencyResolutionProcess(); RecordAccessHandler accessHandler = new RecordAccessHandler(importRecord); - ClassDetailsRecorder classDetailsRecorder = new ClassDetailsRecorder(importRecord); + ClassDetailsRecorder classDetailsRecorder = new ClassDetailsRecorder(importRecord, dependencyResolutionProcess); for (ClassFileLocation location : source) { try (InputStream s = location.openStream()) { JavaClassProcessor javaClassProcessor = @@ -67,15 +69,17 @@ JavaClasses process(ClassFileSource source) { LOG.warn(String.format("Couldn't import class from %s", location.getUri()), e); } } - return new ClassGraphCreator(importRecord, getClassResolver(classDetailsRecorder)).complete(); + return new ClassGraphCreator(importRecord, dependencyResolutionProcess, getClassResolver(classDetailsRecorder)).complete(); } private static class ClassDetailsRecorder implements DeclarationHandler { private final ClassFileImportRecord importRecord; + private final DependencyResolutionProcess dependencyResolutionProcess; private String ownerName; - private ClassDetailsRecorder(ClassFileImportRecord importRecord) { + private ClassDetailsRecorder(ClassFileImportRecord importRecord, DependencyResolutionProcess dependencyResolutionProcess) { this.importRecord = importRecord; + this.dependencyResolutionProcess = dependencyResolutionProcess; } @Override @@ -108,18 +112,22 @@ public void onGenericInterfaces(List> ge } @Override - public void onDeclaredField(JavaFieldBuilder fieldBuilder) { + public void onDeclaredField(JavaFieldBuilder fieldBuilder, String fieldTypeName) { importRecord.addField(ownerName, fieldBuilder); + dependencyResolutionProcess.registerMemberType(fieldTypeName); } @Override - public void onDeclaredConstructor(JavaConstructorBuilder constructorBuilder) { + public void onDeclaredConstructor(JavaConstructorBuilder constructorBuilder, Collection rawParameterTypeNames) { importRecord.addConstructor(ownerName, constructorBuilder); + dependencyResolutionProcess.registerMemberTypes(rawParameterTypeNames); } @Override - public void onDeclaredMethod(JavaMethodBuilder methodBuilder) { + public void onDeclaredMethod(JavaMethodBuilder methodBuilder, Collection rawParameterTypeNames, String rawReturnTypeName) { importRecord.addMethod(ownerName, methodBuilder); + dependencyResolutionProcess.registerMemberTypes(rawParameterTypeNames); + dependencyResolutionProcess.registerMemberType(rawReturnTypeName); } @Override diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java index 28ea265723..40e2df2d1b 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java @@ -79,6 +79,7 @@ class ClassGraphCreator implements ImportContext { private final ImportedClasses classes; private final ClassFileImportRecord importRecord; + private final DependencyResolutionProcess dependencyResolutionProcess; private final SetMultimap processedFieldAccessRecords = HashMultimap.create(); private final SetMultimap> processedMethodCallRecords = HashMultimap.create(); @@ -88,8 +89,9 @@ class ClassGraphCreator implements ImportContext { private final Function> superclassStrategy; private final Function> interfaceStrategy; - ClassGraphCreator(ClassFileImportRecord importRecord, ClassResolver classResolver) { + ClassGraphCreator(ClassFileImportRecord importRecord, DependencyResolutionProcess dependencyResolutionProcess, ClassResolver classResolver) { this.importRecord = importRecord; + this.dependencyResolutionProcess = dependencyResolutionProcess; classes = new ImportedClasses(importRecord.getClasses(), classResolver, new MethodReturnTypeGetter() { @Override public Optional getReturnType(String declaringClassName, String methodName) { @@ -119,7 +121,7 @@ public List apply(JavaClass input) { } JavaClasses complete() { - ensureMemberTypesArePresent(); + dependencyResolutionProcess.resolve(classes); ensureCallTargetsArePresent(); ensureClassesOfInheritanceHierarchiesArePresent(); ensureMetaAnnotationsArePresent(); @@ -128,12 +130,6 @@ JavaClasses complete() { return createJavaClasses(classes.getDirectlyImported(), classes.getAllWithOuterClassesSortedBeforeInnerClasses(), this); } - private void ensureMemberTypesArePresent() { - for (String typeName : importRecord.getMemberSignatureTypeNames()) { - classes.ensurePresent(typeName); - } - } - private void ensureCallTargetsArePresent() { for (RawAccessRecord record : importRecord.getAccessRecords()) { classes.ensurePresent(record.target.owner.getFullyQualifiedClassName()); diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java new file mode 100644 index 0000000000..6b35339d71 --- /dev/null +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java @@ -0,0 +1,44 @@ +/* + * Copyright 2014-2022 TNG Technology Consulting GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.tngtech.archunit.core.importer; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +class DependencyResolutionProcess { + private final Set typeNames = new HashSet<>(); + private boolean initializationComplete = false; + + void registerMemberType(String typeName) { + if (!initializationComplete) { + typeNames.add(typeName); + } + } + + void registerMemberTypes(Collection typeNames) { + for (String typeName : typeNames) { + registerMemberType(typeName); + } + } + + void resolve(ImportedClasses importedClasses) { + initializationComplete = true; + for (String typeName : typeNames) { + importedClasses.ensurePresent(typeName); + } + } +} diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java index 6fc90a0aad..8243a3bc15 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java @@ -242,7 +242,7 @@ public FieldVisitor visitField(int access, String name, String desc, String sign .withType(genericType, rawType) .withModifiers(JavaModifier.getModifiersForField(access)) .withDescriptor(desc); - declarationHandler.onDeclaredField(fieldBuilder); + declarationHandler.onDeclaredField(fieldBuilder, rawType.getFullyQualifiedClassName()); return new FieldProcessor(fieldBuilder, declarationHandler); } @@ -256,9 +256,9 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si CodeUnit codeUnit = new CodeUnit(name, desc, className); accessHandler.setContext(codeUnit); - DomainBuilders.JavaCodeUnitBuilder codeUnitBuilder = addCodeUnitBuilder(name); - JavaCodeUnitSignature codeUnitSignature = JavaCodeUnitSignatureImporter.parseAsmMethodSignature(signature); JavaClassDescriptor rawReturnType = JavaClassDescriptorImporter.importAsmMethodReturnType(desc); + DomainBuilders.JavaCodeUnitBuilder codeUnitBuilder = addCodeUnitBuilder(name, codeUnit.getRawParameterTypeNames(), rawReturnType.getFullyQualifiedClassName()); + JavaCodeUnitSignature codeUnitSignature = JavaCodeUnitSignatureImporter.parseAsmMethodSignature(signature); codeUnitBuilder .withName(name) .withModifiers(JavaModifier.getModifiersForMethod(access)) @@ -281,10 +281,10 @@ private List typesFrom(String[] throwsDeclarations) { return result; } - private DomainBuilders.JavaCodeUnitBuilder addCodeUnitBuilder(String name) { + private DomainBuilders.JavaCodeUnitBuilder addCodeUnitBuilder(String name, Collection rawParameterTypeNames, String rawReturnTypeName) { if (CONSTRUCTOR_NAME.equals(name)) { DomainBuilders.JavaConstructorBuilder builder = new DomainBuilders.JavaConstructorBuilder(); - declarationHandler.onDeclaredConstructor(builder); + declarationHandler.onDeclaredConstructor(builder, rawParameterTypeNames); return builder; } else if (STATIC_INITIALIZER_NAME.equals(name)) { DomainBuilders.JavaStaticInitializerBuilder builder = new DomainBuilders.JavaStaticInitializerBuilder(); @@ -292,7 +292,7 @@ private List typesFrom(String[] throwsDeclarations) { return builder; } else { DomainBuilders.JavaMethodBuilder builder = new DomainBuilders.JavaMethodBuilder(); - declarationHandler.onDeclaredMethod(builder); + declarationHandler.onDeclaredMethod(builder, rawParameterTypeNames, rawReturnTypeName); return builder; } } @@ -496,11 +496,11 @@ interface DeclarationHandler { void onGenericInterfaces(List> genericInterfaceBuilders); - void onDeclaredField(DomainBuilders.JavaFieldBuilder fieldBuilder); + void onDeclaredField(DomainBuilders.JavaFieldBuilder fieldBuilder, String fieldTypeName); - void onDeclaredConstructor(DomainBuilders.JavaConstructorBuilder constructorBuilder); + void onDeclaredConstructor(DomainBuilders.JavaConstructorBuilder constructorBuilder, Collection rawParameterTypeNames); - void onDeclaredMethod(DomainBuilders.JavaMethodBuilder methodBuilder); + void onDeclaredMethod(DomainBuilders.JavaMethodBuilder methodBuilder, Collection rawParameterTypeNames, String rawReturnTypeName); void onDeclaredStaticInitializer(DomainBuilders.JavaStaticInitializerBuilder staticInitializerBuilder); From d3cb79704a34a95fb9a5c7e0cf2b72ad81cc1db6 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sun, 9 Jan 2022 23:42:24 +0700 Subject: [PATCH 07/22] move resolving access target owners to resolution process We should encapsulate all dependency resolution related logic into one dedicated place that only takes care of this one aspect. By having distinguishable methods for different types of dependencies (e.g. member-related, access-related, supertype-related, ...) we can later on decide how deeply we want to resolve the given types of dependencies each. Signed-off-by: Peter Gafert --- .../core/importer/ClassFileImportRecord.java | 10 ---------- .../core/importer/ClassFileProcessor.java | 15 ++++++++++----- .../archunit/core/importer/ClassGraphCreator.java | 7 ------- .../importer/DependencyResolutionProcess.java | 6 ++++++ 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java index eb1c8c3e11..05b963ce5e 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileImportRecord.java @@ -288,16 +288,6 @@ Map getClasses() { return classes; } - Set getAccessRecords() { - return ImmutableSet.builder() - .addAll(rawFieldAccessRecords) - .addAll(rawMethodCallRecords) - .addAll(rawConstructorCallRecords) - .addAll(rawMethodReferenceRecords) - .addAll(rawConstructorReferenceRecords) - .build(); - } - Set getAllSuperclassNames() { return ImmutableSet.copyOf(superclassNamesByOwner.values()); } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java index 8778c5eb5c..35c96d2ca2 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java @@ -57,7 +57,7 @@ class ClassFileProcessor { JavaClasses process(ClassFileSource source) { ClassFileImportRecord importRecord = new ClassFileImportRecord(); DependencyResolutionProcess dependencyResolutionProcess = new DependencyResolutionProcess(); - RecordAccessHandler accessHandler = new RecordAccessHandler(importRecord); + RecordAccessHandler accessHandler = new RecordAccessHandler(importRecord, dependencyResolutionProcess); ClassDetailsRecorder classDetailsRecorder = new ClassDetailsRecorder(importRecord, dependencyResolutionProcess); for (ClassFileLocation location : source) { try (InputStream s = location.openStream()) { @@ -165,11 +165,13 @@ private static class RecordAccessHandler implements AccessHandler { private static final Logger LOG = LoggerFactory.getLogger(RecordAccessHandler.class); private final ClassFileImportRecord importRecord; + private final DependencyResolutionProcess dependencyResolutionProcess; private CodeUnit codeUnit; private int lineNumber; - private RecordAccessHandler(ClassFileImportRecord importRecord) { + private RecordAccessHandler(ClassFileImportRecord importRecord, DependencyResolutionProcess dependencyResolutionProcess) { this.importRecord = importRecord; + this.dependencyResolutionProcess = dependencyResolutionProcess; } @Override @@ -190,6 +192,7 @@ public void handleFieldInstruction(int opcode, String owner, String name, String importRecord.registerFieldAccess(filled(new RawAccessRecord.ForField.Builder(), target) .withAccessType(accessType) .build()); + dependencyResolutionProcess.registerAccessToType(target.owner.getFullyQualifiedClassName()); } @Override @@ -201,17 +204,19 @@ public void handleMethodInstruction(String owner, String name, String desc) { } else { importRecord.registerMethodCall(filled(new RawAccessRecord.Builder(), target).build()); } + dependencyResolutionProcess.registerAccessToType(target.owner.getFullyQualifiedClassName()); } @Override public void handleMethodReferenceInstruction(String owner, String name, String desc) { LOG.trace("Found method reference {}.{}:{} in line {}", owner, name, desc, lineNumber); - TargetInfo targetInfo = new TargetInfo(owner, name, desc); + TargetInfo target = new TargetInfo(owner, name, desc); if (CONSTRUCTOR_NAME.equals(name)) { - importRecord.registerConstructorReference(filled(new RawAccessRecord.Builder(), targetInfo).build()); + importRecord.registerConstructorReference(filled(new RawAccessRecord.Builder(), target).build()); } else { - importRecord.registerMethodReference(filled(new RawAccessRecord.Builder(), targetInfo).build()); + importRecord.registerMethodReference(filled(new RawAccessRecord.Builder(), target).build()); } + dependencyResolutionProcess.registerAccessToType(target.owner.getFullyQualifiedClassName()); } private > BUILDER filled(BUILDER builder, TargetInfo target) { diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java index 40e2df2d1b..3c18067c6f 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java @@ -122,7 +122,6 @@ public List apply(JavaClass input) { JavaClasses complete() { dependencyResolutionProcess.resolve(classes); - ensureCallTargetsArePresent(); ensureClassesOfInheritanceHierarchiesArePresent(); ensureMetaAnnotationsArePresent(); completeClasses(); @@ -130,12 +129,6 @@ JavaClasses complete() { return createJavaClasses(classes.getDirectlyImported(), classes.getAllWithOuterClassesSortedBeforeInnerClasses(), this); } - private void ensureCallTargetsArePresent() { - for (RawAccessRecord record : importRecord.getAccessRecords()) { - classes.ensurePresent(record.target.owner.getFullyQualifiedClassName()); - } - } - private void ensureClassesOfInheritanceHierarchiesArePresent() { for (String superclassName : importRecord.getAllSuperclassNames()) { resolveInheritance(superclassName, superclassStrategy); diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java index 6b35339d71..0f76f0c748 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java @@ -35,6 +35,12 @@ void registerMemberTypes(Collection typeNames) { } } + void registerAccessToType(String typeName) { + if (!initializationComplete) { + typeNames.add(typeName); + } + } + void resolve(ImportedClasses importedClasses) { initializationComplete = true; for (String typeName : typeNames) { From 1a5da26d5c28466a5255dcc8cc40069c5a9e52f3 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sat, 15 Jan 2022 14:38:16 +0700 Subject: [PATCH 08/22] move resolving class hierarchy to resolution process We should encapsulate all dependency resolution related logic into one dedicated place that only takes care of this one aspect. Note that we need to declare the test examples separately, because otherwise there is always compiler magic for inner/local classes that can screw up the test result (i.e. the test is green, but it's not because of class hierarchy resolution, but because it was resolved as some constructor argument type, etc.) Signed-off-by: Peter Gafert --- ...ssFileImporterAutomaticResolutionTest.java | 21 ++++++++++ .../testexamples/classhierarchy/Child.java | 4 ++ .../classhierarchy/GrandParent.java | 4 ++ .../GrandParentInterfaceDirect.java | 4 ++ .../GrandParentInterfaceIndirect.java | 4 ++ .../testexamples/classhierarchy/Parent.java | 4 ++ .../classhierarchy/ParentInterfaceDirect.java | 4 ++ .../ParentInterfaceIndirect.java | 4 ++ .../core/importer/ClassFileProcessor.java | 2 + .../core/importer/ClassGraphCreator.java | 42 +------------------ .../importer/DependencyResolutionProcess.java | 31 +++++++++++++- 11 files changed, 81 insertions(+), 43 deletions(-) create mode 100644 archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/Child.java create mode 100644 archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/GrandParent.java create mode 100644 archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/GrandParentInterfaceDirect.java create mode 100644 archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/GrandParentInterfaceIndirect.java create mode 100644 archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/Parent.java create mode 100644 archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/ParentInterfaceDirect.java create mode 100644 archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/ParentInterfaceIndirect.java diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java index a9407de276..1762edfd11 100644 --- a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java @@ -27,6 +27,7 @@ import com.tngtech.archunit.core.importer.testexamples.annotationfieldimport.ClassWithAnnotatedFields; import com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods; import com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.MethodAnnotationWithEnumAndArrayValue; +import com.tngtech.archunit.core.importer.testexamples.classhierarchy.Child; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; @@ -104,6 +105,26 @@ void methodParameters(Path methodParam1, PrintStream methodParam2) { assertThat(method.getRawParameterTypes().get(1)).as("method parameter type").isFullyImported(true); } + @Test + public void automatically_resolves_class_hierarchy() { + JavaClass child = new ClassFileImporter().importClass(Child.class); + + JavaClass parent = child.getRawSuperclass().get(); + assertThat(parent).isFullyImported(true); + + JavaClass grandparent = parent.getRawSuperclass().get(); + assertThat(grandparent).isFullyImported(true); + + JavaClass parentInterfaceDirect = getOnlyElement(child.getRawInterfaces()); + assertThat(parentInterfaceDirect).isFullyImported(true); + + JavaClass grandParentInterfaceDirect = getOnlyElement(parentInterfaceDirect.getRawInterfaces()); + assertThat(grandParentInterfaceDirect).isFullyImported(true); + + JavaClass grandParentInterfaceIndirect = getOnlyElement(getOnlyElement(grandparent.getRawInterfaces()).getRawInterfaces()); + assertThat(grandParentInterfaceIndirect).isFullyImported(true); + } + @Test public void automatically_resolves_class_annotations() { JavaClass clazz = new ClassFileImporter().importClass(ClassWithUnimportedAnnotation.class); diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/Child.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/Child.java new file mode 100644 index 0000000000..b34e5956be --- /dev/null +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/Child.java @@ -0,0 +1,4 @@ +package com.tngtech.archunit.core.importer.testexamples.classhierarchy; + +public class Child extends Parent implements ParentInterfaceDirect { +} diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/GrandParent.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/GrandParent.java new file mode 100644 index 0000000000..d3a89462ee --- /dev/null +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/GrandParent.java @@ -0,0 +1,4 @@ +package com.tngtech.archunit.core.importer.testexamples.classhierarchy; + +public class GrandParent implements ParentInterfaceIndirect { +} diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/GrandParentInterfaceDirect.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/GrandParentInterfaceDirect.java new file mode 100644 index 0000000000..29989956f1 --- /dev/null +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/GrandParentInterfaceDirect.java @@ -0,0 +1,4 @@ +package com.tngtech.archunit.core.importer.testexamples.classhierarchy; + +public interface GrandParentInterfaceDirect { +} diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/GrandParentInterfaceIndirect.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/GrandParentInterfaceIndirect.java new file mode 100644 index 0000000000..d668927cc9 --- /dev/null +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/GrandParentInterfaceIndirect.java @@ -0,0 +1,4 @@ +package com.tngtech.archunit.core.importer.testexamples.classhierarchy; + +public interface GrandParentInterfaceIndirect { +} diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/Parent.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/Parent.java new file mode 100644 index 0000000000..ceebf26d70 --- /dev/null +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/Parent.java @@ -0,0 +1,4 @@ +package com.tngtech.archunit.core.importer.testexamples.classhierarchy; + +public class Parent extends GrandParent { +} diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/ParentInterfaceDirect.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/ParentInterfaceDirect.java new file mode 100644 index 0000000000..35c3d953c8 --- /dev/null +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/ParentInterfaceDirect.java @@ -0,0 +1,4 @@ +package com.tngtech.archunit.core.importer.testexamples.classhierarchy; + +public interface ParentInterfaceDirect extends GrandParentInterfaceDirect { +} diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/ParentInterfaceIndirect.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/ParentInterfaceIndirect.java new file mode 100644 index 0000000000..5cc0380c44 --- /dev/null +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/classhierarchy/ParentInterfaceIndirect.java @@ -0,0 +1,4 @@ +package com.tngtech.archunit.core.importer.testexamples.classhierarchy; + +public interface ParentInterfaceIndirect extends GrandParentInterfaceIndirect { +} diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java index 35c96d2ca2..24750f35ac 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java @@ -92,8 +92,10 @@ public void onNewClass(String className, Optional superclassName, List> processedConstructorCallRecords = HashMultimap.create(); private final SetMultimap> processedMethodReferenceRecords = HashMultimap.create(); private final SetMultimap> processedConstructorReferenceRecords = HashMultimap.create(); - private final Function> superclassStrategy; - private final Function> interfaceStrategy; ClassGraphCreator(ClassFileImportRecord importRecord, DependencyResolutionProcess dependencyResolutionProcess, ClassResolver classResolver) { this.importRecord = importRecord; @@ -98,53 +95,16 @@ public Optional getReturnType(String declaringClassName, String metho return getMethodReturnType(declaringClassName, methodName); } }); - superclassStrategy = createSuperclassStrategy(); - interfaceStrategy = createInterfaceStrategy(); - } - - private Function> createSuperclassStrategy() { - return new Function>() { - @Override - public Set apply(JavaClass input) { - return importRecord.getSuperclassFor(input.getName()).asSet(); - } - }; - } - - private Function> createInterfaceStrategy() { - return new Function>() { - @Override - public List apply(JavaClass input) { - return importRecord.getInterfaceNamesFor(input.getName()); - } - }; } JavaClasses complete() { - dependencyResolutionProcess.resolve(classes); - ensureClassesOfInheritanceHierarchiesArePresent(); + dependencyResolutionProcess.resolve(classes, importRecord); ensureMetaAnnotationsArePresent(); completeClasses(); completeAccesses(); return createJavaClasses(classes.getDirectlyImported(), classes.getAllWithOuterClassesSortedBeforeInnerClasses(), this); } - private void ensureClassesOfInheritanceHierarchiesArePresent() { - for (String superclassName : importRecord.getAllSuperclassNames()) { - resolveInheritance(superclassName, superclassStrategy); - } - - for (String superinterfaceName : importRecord.getAllSuperinterfaceNames()) { - resolveInheritance(superinterfaceName, interfaceStrategy); - } - } - - private void resolveInheritance(String currentTypeName, Function> inheritanceStrategy) { - for (String parent : inheritanceStrategy.apply(classes.getOrResolve(currentTypeName))) { - resolveInheritance(parent, inheritanceStrategy); - } - } - private void completeClasses() { for (JavaClass javaClass : classes.getAllWithOuterClassesSortedBeforeInnerClasses()) { completeClassHierarchy(javaClass, this); diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java index 0f76f0c748..a1eae0b5cd 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java @@ -19,6 +19,8 @@ import java.util.HashSet; import java.util.Set; +import com.tngtech.archunit.base.Optional; + class DependencyResolutionProcess { private final Set typeNames = new HashSet<>(); private boolean initializationComplete = false; @@ -41,10 +43,35 @@ void registerAccessToType(String typeName) { } } - void resolve(ImportedClasses importedClasses) { + void registerSupertype(String typeName) { + if (!initializationComplete) { + typeNames.add(typeName); + } + } + + void registerSupertypes(Collection typeNames) { + for (String typeName : typeNames) { + registerSupertype(typeName); + } + } + + void resolve(ImportedClasses classes, ClassFileImportRecord importRecord) { initializationComplete = true; for (String typeName : typeNames) { - importedClasses.ensurePresent(typeName); + classes.ensurePresent(typeName); + resolveInheritance(typeName, classes, importRecord); + } + } + + private void resolveInheritance(String typeName, ImportedClasses classes, ClassFileImportRecord importRecord) { + Optional superclass = importRecord.getSuperclassFor(typeName); + if (superclass.isPresent()) { + classes.ensurePresent(superclass.get()); + resolveInheritance(superclass.get(), classes, importRecord); + } + for (String interfaceName : importRecord.getInterfaceNamesFor(typeName)) { + classes.ensurePresent(interfaceName); + resolveInheritance(interfaceName, classes, importRecord); } } } From 049ed034493caeea01d9b8c802c739a6d1cc8329 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sat, 15 Jan 2022 18:14:29 +0700 Subject: [PATCH 09/22] refactor `AnnotationTypeConversion` to custom visitor This way we are more flexible if we want to react to one or the other case (in particular when we want to add annotation class property types to the resolution process already when we visit the annotations). Signed-off-by: Peter Gafert --- .../core/importer/JavaClassProcessor.java | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java index 8243a3bc15..b604c6313b 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java @@ -414,13 +414,12 @@ public void visitEnd() { declarationHandler.onDeclaredMemberAnnotations(codeUnitBuilder.getName(), codeUnitBuilder.getDescriptor(), annotations); } - private static class AnnotationDefaultProcessor extends AnnotationVisitor { + private static class AnnotationDefaultProcessor extends ClassAndPrimitiveDistinguishingAnnotationVisitor { private final String annotationTypeName; private final DeclarationHandler declarationHandler; private final DomainBuilders.JavaMethodBuilder methodBuilder; AnnotationDefaultProcessor(String annotationTypeName, DomainBuilders.JavaCodeUnitBuilder codeUnitBuilder, DeclarationHandler declarationHandler) { - super(ClassFileProcessor.ASM_API_VERSION); this.annotationTypeName = annotationTypeName; this.declarationHandler = declarationHandler; checkArgument(codeUnitBuilder instanceof DomainBuilders.JavaMethodBuilder, @@ -432,8 +431,13 @@ private static class AnnotationDefaultProcessor extends AnnotationVisitor { } @Override - public void visit(String name, Object value) { - declarationHandler.onDeclaredAnnotationDefaultValue(methodBuilder.getName(), methodBuilder.getDescriptor(), AnnotationTypeConversion.convert(value)); + void visitClass(String name, JavaClassDescriptor clazz) { + declarationHandler.onDeclaredAnnotationDefaultValue(methodBuilder.getName(), methodBuilder.getDescriptor(), ValueBuilder.fromClassProperty(clazz)); + } + + @Override + void visitPrimitive(String name, Object value) { + declarationHandler.onDeclaredAnnotationDefaultValue(methodBuilder.getName(), methodBuilder.getDescriptor(), ValueBuilder.fromPrimitiveProperty(value)); } @Override @@ -577,19 +581,23 @@ private static JavaAnnotationBuilder annotationBuilderFor(String desc) { return new JavaAnnotationBuilder().withType(JavaClassDescriptorImporter.importAsmTypeFromDescriptor(desc)); } - private static class AnnotationProcessor extends AnnotationVisitor { + private static class AnnotationProcessor extends ClassAndPrimitiveDistinguishingAnnotationVisitor { private final TakesAnnotationBuilder takesAnnotationBuilder; private final DomainBuilders.JavaAnnotationBuilder annotationBuilder; private AnnotationProcessor(TakesAnnotationBuilder takesAnnotationBuilder, DomainBuilders.JavaAnnotationBuilder annotationBuilder) { - super(ASM_API_VERSION); this.takesAnnotationBuilder = takesAnnotationBuilder; this.annotationBuilder = annotationBuilder; } @Override - public void visit(String name, Object value) { - annotationBuilder.addProperty(name, AnnotationTypeConversion.convert(value)); + void visitClass(String name, JavaClassDescriptor clazz) { + annotationBuilder.addProperty(name, ValueBuilder.fromClassProperty(clazz)); + } + + @Override + void visitPrimitive(String name, Object value) { + annotationBuilder.addProperty(name, ValueBuilder.fromPrimitiveProperty(value)); } @Override @@ -659,24 +667,25 @@ private interface TakesAnnotationBuilder { void add(JavaAnnotationBuilder annotation); } - private static class AnnotationArrayProcessor extends AnnotationVisitor { + private static class AnnotationArrayProcessor extends ClassAndPrimitiveDistinguishingAnnotationVisitor { private final AnnotationArrayContext annotationArrayContext; private Class derivedComponentType; private final List values = new ArrayList<>(); private AnnotationArrayProcessor(AnnotationArrayContext annotationArrayContext) { - super(ASM_API_VERSION); this.annotationArrayContext = annotationArrayContext; } @Override - public void visit(String name, Object value) { - if (JavaClassDescriptorImporter.isAsmType(value)) { - setDerivedComponentType(JavaClass.class); - } else { - setDerivedComponentType(value.getClass()); - } - values.add(AnnotationTypeConversion.convert(value)); + void visitClass(String name, JavaClassDescriptor clazz) { + setDerivedComponentType(JavaClass.class); + values.add(ValueBuilder.fromClassProperty(clazz)); + } + + @Override + void visitPrimitive(String name, Object value) { + setDerivedComponentType(value.getClass()); + values.add(ValueBuilder.fromPrimitiveProperty(value)); } @Override @@ -818,13 +827,23 @@ private static ValueBuilder javaEnumBuilder(final String desc, final String valu return ValueBuilder.fromEnumProperty(enumType, value); } - private static class AnnotationTypeConversion { - static ValueBuilder convert(Object input) { + private abstract static class ClassAndPrimitiveDistinguishingAnnotationVisitor extends AnnotationVisitor { + ClassAndPrimitiveDistinguishingAnnotationVisitor() { + super(ASM_API_VERSION); + } + + @Override + public final void visit(String name, Object input) { final Object value = JavaClassDescriptorImporter.importAsmTypeIfPossible(input); if (value instanceof JavaClassDescriptor) { - return ValueBuilder.fromClassProperty((JavaClassDescriptor) value); + visitClass(name, (JavaClassDescriptor) value); + } else { + visitPrimitive(name, value); } - return ValueBuilder.fromPrimitiveProperty(value); } + + abstract void visitClass(String name, JavaClassDescriptor clazz); + + abstract void visitPrimitive(String name, Object value); } } From b659a0b39a9e8fe508894ff0e6fcc713a74f27b5 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sat, 15 Jan 2022 19:37:26 +0700 Subject: [PATCH 10/22] move resolving annotations to resolution process We should encapsulate all dependency resolution related logic into one dedicated place that only takes care of this one aspect. Signed-off-by: Peter Gafert --- .../core/importer/ClassGraphCreator.java | 26 ----------------- .../importer/DependencyResolutionProcess.java | 28 +++++++++++++++++++ 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java index 328b83cfcb..70f552de51 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java @@ -99,7 +99,6 @@ public Optional getReturnType(String declaringClassName, String metho JavaClasses complete() { dependencyResolutionProcess.resolve(classes, importRecord); - ensureMetaAnnotationsArePresent(); completeClasses(); completeAccesses(); return createJavaClasses(classes.getDirectlyImported(), classes.getAllWithOuterClassesSortedBeforeInnerClasses(), this); @@ -138,31 +137,6 @@ private void completeAccesses() { } } - private void ensureMetaAnnotationsArePresent() { - for (JavaClass javaClass : classes.getAllWithOuterClassesSortedBeforeInnerClasses()) { - resolveAnnotationHierarchy(javaClass); - } - } - - private void resolveAnnotationHierarchy(JavaClass javaClass) { - for (String annotationTypeName : getAnnotationTypeNamesToResolveFor(javaClass)) { - boolean hadBeenPreviouslyResolved = classes.isPresent(annotationTypeName); - JavaClass annotationType = classes.getOrResolve(annotationTypeName); - - if (!hadBeenPreviouslyResolved) { - resolveAnnotationHierarchy(annotationType); - } - } - } - - private Set getAnnotationTypeNamesToResolveFor(JavaClass javaClass) { - return ImmutableSet.builder() - .addAll(importRecord.getAnnotationTypeNamesFor(javaClass)) - .addAll(importRecord.getMemberAnnotationTypeNamesFor(javaClass)) - .addAll(importRecord.getParameterAnnotationTypeNamesFor(javaClass)) - .build(); - } - private , B extends RawAccessRecord> void tryProcess( B rawRecord, AccessRecord.Factory factory, diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java index a1eae0b5cd..963caabe29 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java @@ -19,7 +19,9 @@ import java.util.HashSet; import java.util.Set; +import com.google.common.collect.ImmutableSet; import com.tngtech.archunit.base.Optional; +import com.tngtech.archunit.core.domain.JavaClass; class DependencyResolutionProcess { private final Set typeNames = new HashSet<>(); @@ -61,6 +63,7 @@ void resolve(ImportedClasses classes, ClassFileImportRecord importRecord) { classes.ensurePresent(typeName); resolveInheritance(typeName, classes, importRecord); } + ensureMetaAnnotationsArePresent(classes, importRecord); } private void resolveInheritance(String typeName, ImportedClasses classes, ClassFileImportRecord importRecord) { @@ -74,4 +77,29 @@ private void resolveInheritance(String typeName, ImportedClasses classes, ClassF resolveInheritance(interfaceName, classes, importRecord); } } + + private void ensureMetaAnnotationsArePresent(ImportedClasses classes, ClassFileImportRecord importRecord) { + for (JavaClass javaClass : classes.getAllWithOuterClassesSortedBeforeInnerClasses()) { + resolveAnnotationHierarchy(javaClass, classes, importRecord); + } + } + + private void resolveAnnotationHierarchy(JavaClass javaClass, ImportedClasses classes, ClassFileImportRecord importRecord) { + for (String annotationTypeName : getAnnotationTypeNamesToResolveFor(javaClass, importRecord)) { + boolean hadBeenPreviouslyResolved = classes.isPresent(annotationTypeName); + JavaClass annotationType = classes.getOrResolve(annotationTypeName); + + if (!hadBeenPreviouslyResolved) { + resolveAnnotationHierarchy(annotationType, classes, importRecord); + } + } + } + + private Set getAnnotationTypeNamesToResolveFor(JavaClass javaClass, ClassFileImportRecord importRecord) { + return ImmutableSet.builder() + .addAll(importRecord.getAnnotationTypeNamesFor(javaClass)) + .addAll(importRecord.getMemberAnnotationTypeNamesFor(javaClass)) + .addAll(importRecord.getParameterAnnotationTypeNamesFor(javaClass)) + .build(); + } } From 296f6b412bf709026e68cd751f50b31c815d5101 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sat, 15 Jan 2022 21:31:43 +0700 Subject: [PATCH 11/22] refactor `DependencyResolutionProcess` to recursively resolve types Instead of doing specific actions like iterating all annotations or traversing up the class hierarchy we can just control the type registering process and execute runs for a specific number of times. E.g. we registered all member types on the initial import, so when we by default execute only 1 run of registering member types that means that the types resolved through the member path won't have their own members resolved in turn. We can mimic the current behavior by configuring the different `maxRuns` parameters to do 1 run for member types and access types (i.e. break the resolution after a chain length of 1) and infinitely many runs for inheritance and annotation runs. This way we will traverse inheritance of all types all the way and annotations including their parameters all the way. One benefit is that this opens the way to make this configurable. I.e. users can then control in a simple way how far they want to traverse the graph and decide about the tradeoff with regard to performance themselves. The way this whole process works is that further resolution will in turn invoke the import with the original `DeclarationHandler`. So, when we start the next run we check `classes.ensurePresent(..)`, if this fails the class is resolved with the configured `ClassResolver` (by default from classpath) which in turn will again register additional type names to resolve. So in each run we can configure which such type names we are still interested in or where we want to break the chain. Note, that this also fixes the problem that annotation class parameters were not imported correctly. The recursive nature of the annotation parameter resolution now ensures that all `Class` parameters of annotations are automatically resolved. Signed-off-by: Peter Gafert --- ...ssFileImporterAutomaticResolutionTest.java | 80 ++++++++++++++++++ ...therAnnotationWithAnnotationParameter.java | 5 ++ ...SomeAnnotationWithAnnotationParameter.java | 5 ++ .../SomeAnnotationWithClassParameter.java | 5 ++ .../core/importer/ClassFileProcessor.java | 17 +++- .../core/importer/ClassGraphCreator.java | 2 +- .../importer/DependencyResolutionProcess.java | 81 ++++++++----------- .../core/importer/ImportedClasses.java | 30 ++++--- .../core/importer/JavaClassProcessor.java | 61 ++++++++------ 9 files changed, 203 insertions(+), 83 deletions(-) create mode 100644 archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/annotations/AnotherAnnotationWithAnnotationParameter.java create mode 100644 archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/annotations/SomeAnnotationWithAnnotationParameter.java create mode 100644 archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/annotations/SomeAnnotationWithClassParameter.java diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java index 1762edfd11..6984a2d966 100644 --- a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java @@ -8,6 +8,8 @@ import java.nio.file.Path; import java.util.function.Supplier; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.core.domain.JavaAnnotation; import com.tngtech.archunit.core.domain.JavaClass; @@ -27,6 +29,9 @@ import com.tngtech.archunit.core.importer.testexamples.annotationfieldimport.ClassWithAnnotatedFields; import com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods; import com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.MethodAnnotationWithEnumAndArrayValue; +import com.tngtech.archunit.core.importer.testexamples.annotations.AnotherAnnotationWithAnnotationParameter; +import com.tngtech.archunit.core.importer.testexamples.annotations.SomeAnnotationWithAnnotationParameter; +import com.tngtech.archunit.core.importer.testexamples.annotations.SomeAnnotationWithClassParameter; import com.tngtech.archunit.core.importer.testexamples.classhierarchy.Child; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; @@ -44,6 +49,7 @@ import static com.tngtech.archunit.testutil.Assertions.assertThatAnnotation; import static com.tngtech.archunit.testutil.Assertions.assertThatType; import static com.tngtech.archunit.testutil.assertion.JavaAnnotationAssertion.annotationProperty; +import static com.tngtech.java.junit.dataprovider.DataProviders.$; import static com.tngtech.java.junit.dataprovider.DataProviders.testForEach; @RunWith(DataProviderRunner.class) @@ -391,6 +397,80 @@ class SomeClass { assertThat(stubType).isFullyImported(false); } + @DataProvider + public static Object[][] data_automatically_resolves_annotation_parameter_types() { + @SomeAnnotationWithClassParameter(String.class) + @SomeAnnotationWithAnnotationParameter(@SomeAnnotationWithClassParameter(File.class)) + @AnotherAnnotationWithAnnotationParameter(@SomeAnnotationWithAnnotationParameter(@SomeAnnotationWithClassParameter(Serializable.class))) + class OnClass { + } + @SuppressWarnings("unused") + class OnField { + @SomeAnnotationWithClassParameter(String.class) + @SomeAnnotationWithAnnotationParameter(@SomeAnnotationWithClassParameter(File.class)) + @AnotherAnnotationWithAnnotationParameter(@SomeAnnotationWithAnnotationParameter(@SomeAnnotationWithClassParameter(Serializable.class))) + Object field; + } + @SuppressWarnings("unused") + class OnMethod { + @SomeAnnotationWithClassParameter(String.class) + @SomeAnnotationWithAnnotationParameter(@SomeAnnotationWithClassParameter(File.class)) + @AnotherAnnotationWithAnnotationParameter(@SomeAnnotationWithAnnotationParameter(@SomeAnnotationWithClassParameter(Serializable.class))) + void method() { + } + } + class OnConstructor { + @SomeAnnotationWithClassParameter(String.class) + @SomeAnnotationWithAnnotationParameter(@SomeAnnotationWithClassParameter(File.class)) + @AnotherAnnotationWithAnnotationParameter(@SomeAnnotationWithAnnotationParameter(@SomeAnnotationWithClassParameter(Serializable.class))) + OnConstructor() { + } + } + class OnParameter { + OnParameter( + @SomeAnnotationWithClassParameter(String.class) + @SomeAnnotationWithAnnotationParameter(@SomeAnnotationWithClassParameter(File.class)) + @AnotherAnnotationWithAnnotationParameter(@SomeAnnotationWithAnnotationParameter(@SomeAnnotationWithClassParameter(Serializable.class))) Object parameter) { + } + } + + return FluentIterable.concat( + nestedAnnotationValueTestCases(new ClassFileImporter().importClass(OnClass.class)), + nestedAnnotationValueTestCases(new ClassFileImporter().importClass(OnField.class).getField("field")), + nestedAnnotationValueTestCases(new ClassFileImporter().importClass(OnMethod.class).getMethod("method")), + nestedAnnotationValueTestCases(getOnlyElement(new ClassFileImporter().importClass(OnConstructor.class).getConstructors())), + nestedAnnotationValueTestCases(getOnlyElement(new ClassFileImporter().importClass(OnParameter.class).getConstructors()).getParameters().get(0)) + ).toArray(Object[].class); + } + + private static Iterable nestedAnnotationValueTestCases(HasAnnotations hasAnnotations) { + return ImmutableList.of( + $(getNestedAnnotationClassValue(hasAnnotations, SomeAnnotationWithClassParameter.class), expect(String.class)), + $(getNestedAnnotationClassValue(hasAnnotations, SomeAnnotationWithAnnotationParameter.class), expect(File.class)), + $(getNestedAnnotationClassValue(hasAnnotations, AnotherAnnotationWithAnnotationParameter.class), expect(Serializable.class)) + ); + } + + private static JavaClass getNestedAnnotationClassValue(HasAnnotations hasAnnotations, Class annotationType) { + Object value = hasAnnotations.getAnnotationOfType(annotationType.getName()); + while (!(value instanceof JavaClass)) { + value = ((JavaAnnotation) value).get("value").get(); + } + return (JavaClass) value; + } + + // just syntactic sugar to improve readability what is actual and what is expected + private static Class expect(Class clazz) { + return clazz; + } + + @Test + @UseDataProvider + public void test_automatically_resolves_annotation_parameter_types(JavaClass annotationValue, Class expectedType) { + assertThat(annotationValue).isFullyImported(true); + assertThatType(annotationValue).matches(expectedType); + } + @MetaAnnotatedAnnotation private static class MetaAnnotatedClass { } diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/annotations/AnotherAnnotationWithAnnotationParameter.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/annotations/AnotherAnnotationWithAnnotationParameter.java new file mode 100644 index 0000000000..692339fb4b --- /dev/null +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/annotations/AnotherAnnotationWithAnnotationParameter.java @@ -0,0 +1,5 @@ +package com.tngtech.archunit.core.importer.testexamples.annotations; + +public @interface AnotherAnnotationWithAnnotationParameter { + SomeAnnotationWithAnnotationParameter value(); +} diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/annotations/SomeAnnotationWithAnnotationParameter.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/annotations/SomeAnnotationWithAnnotationParameter.java new file mode 100644 index 0000000000..01358b071f --- /dev/null +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/annotations/SomeAnnotationWithAnnotationParameter.java @@ -0,0 +1,5 @@ +package com.tngtech.archunit.core.importer.testexamples.annotations; + +public @interface SomeAnnotationWithAnnotationParameter { + SomeAnnotationWithClassParameter value(); +} diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/annotations/SomeAnnotationWithClassParameter.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/annotations/SomeAnnotationWithClassParameter.java new file mode 100644 index 0000000000..08856d09bd --- /dev/null +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/testexamples/annotations/SomeAnnotationWithClassParameter.java @@ -0,0 +1,5 @@ +package com.tngtech.archunit.core.importer.testexamples.annotations; + +public @interface SomeAnnotationWithClassParameter { + Class value(); +} diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java index 24750f35ac..5a2e21c0d6 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java @@ -140,11 +140,24 @@ public void onDeclaredStaticInitializer(JavaStaticInitializerBuilder staticIniti @Override public void onDeclaredClassAnnotations(Set annotationBuilders) { importRecord.addClassAnnotations(ownerName, annotationBuilders); + registerAnnotationTypesToResolve(annotationBuilders); } @Override - public void onDeclaredMemberAnnotations(String memberName, String descriptor, Set annotations) { - importRecord.addMemberAnnotations(ownerName, memberName, descriptor, annotations); + public void onDeclaredMemberAnnotations(String memberName, String descriptor, Set annotationBuilders) { + importRecord.addMemberAnnotations(ownerName, memberName, descriptor, annotationBuilders); + registerAnnotationTypesToResolve(annotationBuilders); + } + + private void registerAnnotationTypesToResolve(Set annotationBuilders) { + for (JavaAnnotationBuilder annotationBuilder : annotationBuilders) { + dependencyResolutionProcess.registerAnnotationType(annotationBuilder.getFullyQualifiedClassName()); + } + } + + @Override + public void onDeclaredAnnotationValueType(String valueTypeName) { + dependencyResolutionProcess.registerAnnotationType(valueTypeName); } @Override diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java index 70f552de51..3866429a5f 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java @@ -98,7 +98,7 @@ public Optional getReturnType(String declaringClassName, String metho } JavaClasses complete() { - dependencyResolutionProcess.resolve(classes, importRecord); + dependencyResolutionProcess.resolve(classes); completeClasses(); completeAccesses(); return createJavaClasses(classes.getDirectlyImported(), classes.getAllWithOuterClassesSortedBeforeInnerClasses(), this); diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java index 963caabe29..976078eb82 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java @@ -19,17 +19,23 @@ import java.util.HashSet; import java.util.Set; -import com.google.common.collect.ImmutableSet; -import com.tngtech.archunit.base.Optional; -import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.importer.ImportedClasses.ImportedClassState; + +import static com.tngtech.archunit.core.importer.ImportedClasses.ImportedClassState.HAD_TO_BE_IMPORTED; class DependencyResolutionProcess { - private final Set typeNames = new HashSet<>(); - private boolean initializationComplete = false; + private static final int maxRunsForMemberTypes = 1; + private static final int maxRunsForAccessesToTypes = 1; + private static final int maxRunsForSupertypes = -1; + private static final int maxRunsForAnnotationTypes = -1; + + private Set currentTypeNames = new HashSet<>(); + private int runNumber = 1; + private boolean shouldContinue; void registerMemberType(String typeName) { - if (!initializationComplete) { - typeNames.add(typeName); + if (runNumberHasNotExceeded(maxRunsForMemberTypes)) { + currentTypeNames.add(typeName); } } @@ -40,14 +46,14 @@ void registerMemberTypes(Collection typeNames) { } void registerAccessToType(String typeName) { - if (!initializationComplete) { - typeNames.add(typeName); + if (runNumberHasNotExceeded(maxRunsForAccessesToTypes)) { + currentTypeNames.add(typeName); } } void registerSupertype(String typeName) { - if (!initializationComplete) { - typeNames.add(typeName); + if (runNumberHasNotExceeded(maxRunsForSupertypes)) { + currentTypeNames.add(typeName); } } @@ -57,49 +63,30 @@ void registerSupertypes(Collection typeNames) { } } - void resolve(ImportedClasses classes, ClassFileImportRecord importRecord) { - initializationComplete = true; - for (String typeName : typeNames) { - classes.ensurePresent(typeName); - resolveInheritance(typeName, classes, importRecord); + void registerAnnotationType(String typeName) { + if (runNumberHasNotExceeded(maxRunsForAnnotationTypes)) { + currentTypeNames.add(typeName); } - ensureMetaAnnotationsArePresent(classes, importRecord); } - private void resolveInheritance(String typeName, ImportedClasses classes, ClassFileImportRecord importRecord) { - Optional superclass = importRecord.getSuperclassFor(typeName); - if (superclass.isPresent()) { - classes.ensurePresent(superclass.get()); - resolveInheritance(superclass.get(), classes, importRecord); - } - for (String interfaceName : importRecord.getInterfaceNamesFor(typeName)) { - classes.ensurePresent(interfaceName); - resolveInheritance(interfaceName, classes, importRecord); - } + void resolve(ImportedClasses classes) { + do { + executeRun(classes); + } while (shouldContinue); } - private void ensureMetaAnnotationsArePresent(ImportedClasses classes, ClassFileImportRecord importRecord) { - for (JavaClass javaClass : classes.getAllWithOuterClassesSortedBeforeInnerClasses()) { - resolveAnnotationHierarchy(javaClass, classes, importRecord); - } - } - - private void resolveAnnotationHierarchy(JavaClass javaClass, ImportedClasses classes, ClassFileImportRecord importRecord) { - for (String annotationTypeName : getAnnotationTypeNamesToResolveFor(javaClass, importRecord)) { - boolean hadBeenPreviouslyResolved = classes.isPresent(annotationTypeName); - JavaClass annotationType = classes.getOrResolve(annotationTypeName); - - if (!hadBeenPreviouslyResolved) { - resolveAnnotationHierarchy(annotationType, classes, importRecord); - } + private void executeRun(ImportedClasses classes) { + runNumber++; + Set typeNamesToResolve = this.currentTypeNames; + currentTypeNames = new HashSet<>(); + shouldContinue = false; + for (String typeName : typeNamesToResolve) { + ImportedClassState classState = classes.ensurePresent(typeName); + shouldContinue = shouldContinue || (classState == HAD_TO_BE_IMPORTED); } } - private Set getAnnotationTypeNamesToResolveFor(JavaClass javaClass, ClassFileImportRecord importRecord) { - return ImmutableSet.builder() - .addAll(importRecord.getAnnotationTypeNamesFor(javaClass)) - .addAll(importRecord.getMemberAnnotationTypeNamesFor(javaClass)) - .addAll(importRecord.getParameterAnnotationTypeNamesFor(javaClass)) - .build(); + private boolean runNumberHasNotExceeded(int maxRuns) { + return maxRuns < 0 || runNumber <= maxRuns; } } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ImportedClasses.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ImportedClasses.java index febb119a7e..2ed2e82a55 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ImportedClasses.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ImportedClasses.java @@ -33,6 +33,8 @@ import static com.tngtech.archunit.core.domain.JavaModifier.ABSTRACT; import static com.tngtech.archunit.core.domain.JavaModifier.FINAL; import static com.tngtech.archunit.core.domain.JavaModifier.PUBLIC; +import static com.tngtech.archunit.core.importer.ImportedClasses.ImportedClassState.HAD_TO_BE_IMPORTED; +import static com.tngtech.archunit.core.importer.ImportedClasses.ImportedClassState.WAS_ALREADY_PRESENT; class ImportedClasses { private static final ImmutableSet PRIMITIVE_AND_ARRAY_TYPE_MODIFIERS = @@ -56,20 +58,23 @@ Map getDirectlyImported() { JavaClass getOrResolve(String typeName) { JavaClass javaClass = allClasses.get(typeName); - if (javaClass == null) { - Optional resolved = resolver.tryResolve(typeName); - javaClass = resolved.isPresent() ? resolved.get() : stubClassOf(typeName); - allClasses.put(typeName, javaClass); - } - return javaClass; + return javaClass != null ? javaClass : resolve(typeName); } - boolean isPresent(String typeName) { - return allClasses.containsKey(typeName); + ImportedClassState ensurePresent(String typeName) { + if (allClasses.containsKey(typeName)) { + return WAS_ALREADY_PRESENT; + } + + resolve(typeName); + return HAD_TO_BE_IMPORTED; } - void ensurePresent(String typeName) { - getOrResolve(typeName); + private JavaClass resolve(String typeName) { + Optional resolved = resolver.tryResolve(typeName); + JavaClass javaClass = resolved.isPresent() ? resolved.get() : stubClassOf(typeName); + allClasses.put(typeName, javaClass); + return javaClass; } Collection getAllWithOuterClassesSortedBeforeInnerClasses() { @@ -96,4 +101,9 @@ public Optional getMethodReturnType(String declaringClassName, String interface MethodReturnTypeGetter { Optional getReturnType(String declaringClassName, String methodName); } + + enum ImportedClassState { + HAD_TO_BE_IMPORTED, + WAS_ALREADY_PRESENT + } } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java index b604c6313b..6d29aae8a2 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java @@ -303,7 +303,7 @@ public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return super.visitAnnotation(desc, visible); } - return new AnnotationProcessor(addAnnotationTo(annotations), annotationBuilderFor(desc)); + return new AnnotationProcessor(addAnnotationTo(annotations), declarationHandler, handleAnnotationAnnotationProperty(desc, declarationHandler)); } @Override @@ -341,7 +341,7 @@ public void visitCode() { @Override public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { - return new AnnotationProcessor(addAnnotationAtIndex(parameterAnnotationsByIndex, parameter), annotationBuilderFor(desc)); + return new AnnotationProcessor(addAnnotationAtIndex(parameterAnnotationsByIndex, parameter), declarationHandler, handleAnnotationAnnotationProperty(desc, declarationHandler)); } // NOTE: ASM does not reliably visit this method, so if this method is skipped, line number 0 is recorded @@ -380,7 +380,7 @@ public void visitTypeInsn(int opcode, String type) { @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - return new AnnotationProcessor(addAnnotationTo(annotations), annotationBuilderFor(desc)); + return new AnnotationProcessor(addAnnotationTo(annotations), declarationHandler, handleAnnotationAnnotationProperty(desc, declarationHandler)); } @Override @@ -432,7 +432,8 @@ private static class AnnotationDefaultProcessor extends ClassAndPrimitiveDisting @Override void visitClass(String name, JavaClassDescriptor clazz) { - declarationHandler.onDeclaredAnnotationDefaultValue(methodBuilder.getName(), methodBuilder.getDescriptor(), ValueBuilder.fromClassProperty(clazz)); + declarationHandler.onDeclaredAnnotationDefaultValue(methodBuilder.getName(), methodBuilder.getDescriptor(), handleAnnotationClassProperty(clazz, declarationHandler)); + declarationHandler.onDeclaredAnnotationValueType(clazz.getFullyQualifiedClassName()); } @Override @@ -442,17 +443,17 @@ void visitPrimitive(String name, Object value) { @Override public void visitEnum(String name, String desc, String value) { - declarationHandler.onDeclaredAnnotationDefaultValue(methodBuilder.getName(), methodBuilder.getDescriptor(), javaEnumBuilder(desc, value)); + declarationHandler.onDeclaredAnnotationDefaultValue(methodBuilder.getName(), methodBuilder.getDescriptor(), handleAnnotationEnumProperty(desc, value, declarationHandler)); } @Override public AnnotationVisitor visitAnnotation(String name, String desc) { - return new AnnotationProcessor(new SetAsAnnotationDefault(annotationTypeName, methodBuilder, declarationHandler), annotationBuilderFor(desc)); + return new AnnotationProcessor(new SetAsAnnotationDefault(annotationTypeName, methodBuilder, declarationHandler), declarationHandler, handleAnnotationAnnotationProperty(desc, declarationHandler)); } @Override public AnnotationVisitor visitArray(String name) { - return new AnnotationArrayProcessor(new SetAsAnnotationDefault(annotationTypeName, methodBuilder, declarationHandler)); + return new AnnotationArrayProcessor(new SetAsAnnotationDefault(annotationTypeName, methodBuilder, declarationHandler), declarationHandler); } } } @@ -512,6 +513,8 @@ interface DeclarationHandler { void onDeclaredMemberAnnotations(String memberName, String descriptor, Set annotations); + void onDeclaredAnnotationValueType(String valueTypeName); + void onDeclaredAnnotationDefaultValue(String methodName, String methodDescriptor, ValueBuilder valueBuilder); void registerEnclosingClass(String ownerName, String enclosingClassName); @@ -568,7 +571,7 @@ private FieldProcessor(DomainBuilders.JavaFieldBuilder fieldBuilder, Declaration @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - return new AnnotationProcessor(addAnnotationTo(annotations), annotationBuilderFor(desc)); + return new AnnotationProcessor(addAnnotationTo(annotations), declarationHandler, handleAnnotationAnnotationProperty(desc, declarationHandler)); } @Override @@ -577,22 +580,20 @@ public void visitEnd() { } } - private static JavaAnnotationBuilder annotationBuilderFor(String desc) { - return new JavaAnnotationBuilder().withType(JavaClassDescriptorImporter.importAsmTypeFromDescriptor(desc)); - } - private static class AnnotationProcessor extends ClassAndPrimitiveDistinguishingAnnotationVisitor { private final TakesAnnotationBuilder takesAnnotationBuilder; - private final DomainBuilders.JavaAnnotationBuilder annotationBuilder; + private final DeclarationHandler declarationHandler; + private final JavaAnnotationBuilder annotationBuilder; - private AnnotationProcessor(TakesAnnotationBuilder takesAnnotationBuilder, DomainBuilders.JavaAnnotationBuilder annotationBuilder) { + private AnnotationProcessor(TakesAnnotationBuilder takesAnnotationBuilder, DeclarationHandler declarationHandler, JavaAnnotationBuilder annotationBuilder) { this.takesAnnotationBuilder = takesAnnotationBuilder; + this.declarationHandler = declarationHandler; this.annotationBuilder = annotationBuilder; } @Override void visitClass(String name, JavaClassDescriptor clazz) { - annotationBuilder.addProperty(name, ValueBuilder.fromClassProperty(clazz)); + annotationBuilder.addProperty(name, handleAnnotationClassProperty(clazz, declarationHandler)); } @Override @@ -602,12 +603,12 @@ void visitPrimitive(String name, Object value) { @Override public void visitEnum(String name, String desc, String value) { - annotationBuilder.addProperty(name, javaEnumBuilder(desc, value)); + annotationBuilder.addProperty(name, handleAnnotationEnumProperty(desc, value, declarationHandler)); } @Override public AnnotationVisitor visitAnnotation(final String name, String desc) { - return new AnnotationProcessor(addAnnotationAsProperty(name, annotationBuilder), annotationBuilderFor(desc)); + return new AnnotationProcessor(addAnnotationAsProperty(name, this.annotationBuilder), declarationHandler, handleAnnotationAnnotationProperty(desc, declarationHandler)); } @Override @@ -627,7 +628,7 @@ public String getDeclaringAnnotationMemberName() { public void setArrayResult(ValueBuilder valueBuilder) { annotationBuilder.addProperty(name, valueBuilder); } - }); + }, declarationHandler); } @Override @@ -669,17 +670,19 @@ private interface TakesAnnotationBuilder { private static class AnnotationArrayProcessor extends ClassAndPrimitiveDistinguishingAnnotationVisitor { private final AnnotationArrayContext annotationArrayContext; + private final DeclarationHandler declarationHandler; private Class derivedComponentType; private final List values = new ArrayList<>(); - private AnnotationArrayProcessor(AnnotationArrayContext annotationArrayContext) { + private AnnotationArrayProcessor(AnnotationArrayContext annotationArrayContext, DeclarationHandler declarationHandler) { this.annotationArrayContext = annotationArrayContext; + this.declarationHandler = declarationHandler; } @Override void visitClass(String name, JavaClassDescriptor clazz) { setDerivedComponentType(JavaClass.class); - values.add(ValueBuilder.fromClassProperty(clazz)); + values.add(handleAnnotationClassProperty(clazz, declarationHandler)); } @Override @@ -696,13 +699,13 @@ public AnnotationVisitor visitAnnotation(String name, String desc) { public void add(JavaAnnotationBuilder annotationBuilder) { values.add(ValueBuilder.fromAnnotationProperty(annotationBuilder)); } - }, annotationBuilderFor(desc)); + }, declarationHandler, handleAnnotationAnnotationProperty(desc, declarationHandler)); } @Override public void visitEnum(String name, final String desc, final String value) { setDerivedComponentType(JavaEnumConstant.class); - values.add(javaEnumBuilder(desc, value)); + values.add(handleAnnotationEnumProperty(desc, value, declarationHandler)); } // NOTE: If the declared annotation is not imported itself, we can still use this heuristic, @@ -822,11 +825,23 @@ private interface AnnotationArrayContext { void setArrayResult(ValueBuilder valueBuilder); } - private static ValueBuilder javaEnumBuilder(final String desc, final String value) { + private static ValueBuilder handleAnnotationClassProperty(JavaClassDescriptor clazz, DeclarationHandler declarationHandler) { + declarationHandler.onDeclaredAnnotationValueType(clazz.getFullyQualifiedClassName()); + return ValueBuilder.fromClassProperty(clazz); + } + + private static ValueBuilder handleAnnotationEnumProperty(String desc, String value, DeclarationHandler declarationHandler) { JavaClassDescriptor enumType = JavaClassDescriptorImporter.importAsmTypeFromDescriptor(desc); + declarationHandler.onDeclaredAnnotationValueType(enumType.getFullyQualifiedClassName()); return ValueBuilder.fromEnumProperty(enumType, value); } + private static JavaAnnotationBuilder handleAnnotationAnnotationProperty(String desc, DeclarationHandler declarationHandler) { + JavaAnnotationBuilder subAnnotationBuilder = new JavaAnnotationBuilder().withType(JavaClassDescriptorImporter.importAsmTypeFromDescriptor(desc)); + declarationHandler.onDeclaredAnnotationValueType(subAnnotationBuilder.getFullyQualifiedClassName()); + return subAnnotationBuilder; + } + private abstract static class ClassAndPrimitiveDistinguishingAnnotationVisitor extends AnnotationVisitor { ClassAndPrimitiveDistinguishingAnnotationVisitor() { super(ASM_API_VERSION); From 9939e87317d18899daaf4ac3f16b14ee9b038dde Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sat, 15 Jan 2022 22:28:08 +0700 Subject: [PATCH 12/22] automatically resolve referenced class objects This will ensure that referenced class objects (like `String.class`) are fully imported. Otherwise, things like annotations or class hierarchy will be missing for types that are not directly imported and only referenced as class object. Signed-off-by: Peter Gafert --- ...ClassFileImporterAutomaticResolutionTest.java | 16 ++++++++++++++++ .../core/importer/ClassFileProcessor.java | 5 +++++ .../core/importer/JavaClassProcessor.java | 7 +++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java index 6984a2d966..fccf266623 100644 --- a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java @@ -21,6 +21,7 @@ import com.tngtech.archunit.core.domain.JavaMethod; import com.tngtech.archunit.core.domain.JavaMethodCall; import com.tngtech.archunit.core.domain.JavaMethodReference; +import com.tngtech.archunit.core.domain.ReferencedClassObject; import com.tngtech.archunit.core.domain.properties.HasAnnotations; import com.tngtech.archunit.core.importer.testexamples.SomeAnnotation; import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassWithUnimportedAnnotation; @@ -471,6 +472,21 @@ public void test_automatically_resolves_annotation_parameter_types(JavaClass ann assertThatType(annotationValue).matches(expectedType); } + @Test + public void automatically_resolves_class_objects() { + @SuppressWarnings("unused") + class Origin { + Class call() { + return String.class; + } + } + + ReferencedClassObject classObject = getOnlyElement(new ClassFileImporter().importClass(Origin.class).getReferencedClassObjects()); + + assertThat(classObject.getRawType()).isFullyImported(true); + assertThatType(classObject.getRawType()).matches(String.class); + } + @MetaAnnotatedAnnotation private static class MetaAnnotatedClass { } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java index 5a2e21c0d6..106310ae55 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java @@ -174,6 +174,11 @@ public void registerEnclosingClass(String ownerName, String enclosingClassName) public void registerEnclosingCodeUnit(String ownerName, CodeUnit enclosingCodeUnit) { importRecord.setEnclosingCodeUnit(ownerName, enclosingCodeUnit); } + + @Override + public void onDeclaredClassObject(String typeName) { + dependencyResolutionProcess.registerAccessToType(typeName); + } } private static class RecordAccessHandler implements AccessHandler { diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java index 6d29aae8a2..df989ca04f 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java @@ -356,8 +356,9 @@ public void visitLineNumber(int line, Label start) { @Override public void visitLdcInsn(Object value) { if (JavaClassDescriptorImporter.isAsmType(value)) { - codeUnitBuilder.addReferencedClassObject( - RawReferencedClassObject.from(JavaClassDescriptorImporter.importAsmType(value), actualLineNumber)); + JavaClassDescriptor type = JavaClassDescriptorImporter.importAsmType(value); + codeUnitBuilder.addReferencedClassObject(RawReferencedClassObject.from(type, actualLineNumber)); + declarationHandler.onDeclaredClassObject(type.getFullyQualifiedClassName()); } } @@ -520,6 +521,8 @@ interface DeclarationHandler { void registerEnclosingClass(String ownerName, String enclosingClassName); void registerEnclosingCodeUnit(String ownerName, CodeUnit enclosingCodeUnit); + + void onDeclaredClassObject(String typeName); } interface AccessHandler { From 1b856213eb5cb104625659388698189eddd5d041 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sat, 15 Jan 2022 22:41:01 +0700 Subject: [PATCH 13/22] introduce `JavaClass.getInstanceofChecks()` It seems surprising that there is currently no way to retrieve all instanceof checks of a `JavaClass`. Most other things that are bound to code units are also exposed as an aggregated version in `JavaClass` (e.g. referenced class objects). Signed-off-by: Peter Gafert --- .../java/com/tngtech/archunit/core/domain/JavaClass.java | 5 +++++ .../archunit/core/domain/JavaClassDependencies.java | 6 ++---- .../tngtech/archunit/core/domain/JavaClassMembers.java | 8 ++++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java index 4da56d0986..4c9dfbc0ba 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java @@ -649,6 +649,11 @@ public List> getTypeParameters() { return typeParameters; } + @PublicAPI(usage = ACCESS) + public Set getInstanceofChecks() { + return members.getInstanceofChecks(); + } + @PublicAPI(usage = ACCESS) public Set getReferencedClassObjects() { return members.getReferencedClassObjects(); diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassDependencies.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassDependencies.java index a1f2d6ce99..838ae055d3 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassDependencies.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassDependencies.java @@ -206,10 +206,8 @@ private Set annotationDependenciesFromSelf() { private Set instanceofCheckDependenciesFromSelf() { ImmutableSet.Builder result = ImmutableSet.builder(); - for (JavaCodeUnit codeUnit : javaClass.getCodeUnits()) { - for (InstanceofCheck instanceofCheck : codeUnit.getInstanceofChecks()) { - result.addAll(Dependency.tryCreateFromInstanceofCheck(instanceofCheck)); - } + for (InstanceofCheck instanceofCheck : javaClass.getInstanceofChecks()) { + result.addAll(Dependency.tryCreateFromInstanceofCheck(instanceofCheck)); } return result.build(); } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassMembers.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassMembers.java index 1ec01e7c1a..ab7b959c38 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassMembers.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassMembers.java @@ -196,6 +196,14 @@ Set getEnumConstants() { return result.build(); } + Set getInstanceofChecks() { + ImmutableSet.Builder result = ImmutableSet.builder(); + for (JavaCodeUnit codeUnit : codeUnits) { + result.addAll(codeUnit.getInstanceofChecks()); + } + return result.build(); + } + Set getReferencedClassObjects() { ImmutableSet.Builder result = ImmutableSet.builder(); for (JavaCodeUnit codeUnit : codeUnits) { From cf5141d7ebecf008e1db2adcfdd7789b94efada7 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sat, 15 Jan 2022 22:44:28 +0700 Subject: [PATCH 14/22] automatically resolve target types of instanceof checks This will ensure that types referenced by instanceof checks (e.g. `object instanceof String`) are fully imported. Otherwise, things like annotations or class hierarchy will be missing for types that are not directly imported and only referenced through instanceof checks. Signed-off-by: Peter Gafert --- ...ClassFileImporterAutomaticResolutionTest.java | 16 ++++++++++++++++ .../core/importer/ClassFileProcessor.java | 5 +++++ .../core/importer/JavaClassProcessor.java | 6 +++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java index fccf266623..eb07a416ef 100644 --- a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java @@ -11,6 +11,7 @@ import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.tngtech.archunit.ArchConfiguration; +import com.tngtech.archunit.core.domain.InstanceofCheck; import com.tngtech.archunit.core.domain.JavaAnnotation; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaConstructor; @@ -487,6 +488,21 @@ Class call() { assertThatType(classObject.getRawType()).matches(String.class); } + @Test + public void automatically_resolves_instanceof_check_targets() { + @SuppressWarnings("unused") + class Origin { + boolean call(Object obj) { + return obj instanceof String; + } + } + + InstanceofCheck instanceofCheck = getOnlyElement(new ClassFileImporter().importClass(Origin.class).getInstanceofChecks()); + + assertThat(instanceofCheck.getRawType()).isFullyImported(true); + assertThatType(instanceofCheck.getRawType()).matches(String.class); + } + @MetaAnnotatedAnnotation private static class MetaAnnotatedClass { } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java index 106310ae55..4cc83e85c5 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java @@ -179,6 +179,11 @@ public void registerEnclosingCodeUnit(String ownerName, CodeUnit enclosingCodeUn public void onDeclaredClassObject(String typeName) { dependencyResolutionProcess.registerAccessToType(typeName); } + + @Override + public void onDeclaredInstanceofCheck(String typeName) { + dependencyResolutionProcess.registerAccessToType(typeName); + } } private static class RecordAccessHandler implements AccessHandler { diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java index df989ca04f..d498496449 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java @@ -375,7 +375,9 @@ public void visitMethodInsn(int opcode, String owner, String name, String desc, @Override public void visitTypeInsn(int opcode, String type) { if (opcode == Opcodes.INSTANCEOF) { - codeUnitBuilder.addInstanceOfCheck(from(JavaClassDescriptorImporter.createFromAsmObjectTypeName(type), actualLineNumber)); + JavaClassDescriptor instanceOfCheckType = JavaClassDescriptorImporter.createFromAsmObjectTypeName(type); + codeUnitBuilder.addInstanceOfCheck(from(instanceOfCheckType, actualLineNumber)); + declarationHandler.onDeclaredInstanceofCheck(instanceOfCheckType.getFullyQualifiedClassName()); } } @@ -523,6 +525,8 @@ interface DeclarationHandler { void registerEnclosingCodeUnit(String ownerName, CodeUnit enclosingCodeUnit); void onDeclaredClassObject(String typeName); + + void onDeclaredInstanceofCheck(String typeName); } interface AccessHandler { From b75d9fc2a6d9d988c902baa8d132fe66dbecfcc2 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Fri, 21 Jan 2022 10:38:00 +0700 Subject: [PATCH 15/22] introduce `JavaClass.getThrowsDeclarations()` It seems surprising that there is currently no way to retrieve all throws declarations of a `JavaClass`. Most other things that are bound to code units are also exposed as an aggregated version in `JavaClass` (e.g. referenced class objects). Signed-off-by: Peter Gafert --- .../com/tngtech/archunit/core/domain/JavaClass.java | 13 +++++++++++++ .../archunit/core/domain/JavaClassDependencies.java | 6 ++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java index 4c9dfbc0ba..a0a303b15d 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java @@ -1551,6 +1551,19 @@ public static List namesOf(Iterable> paramTypes) { return formatNamesOf(paramTypes); } + /** + * @return all throws declarations on any method or constructor of this class + * (e.g. {@code void method() throws SomeException}) + */ + @PublicAPI(usage = ACCESS) + public Set> getThrowsDeclarations() { + ImmutableSet.Builder> result = ImmutableSet.builder(); + for (JavaCodeUnit codeUnit : getCodeUnits()) { + result.addAll(codeUnit.getThrowsClause()); + } + return result.build(); + } + private static class Superclass { private static final Superclass ABSENT = new Superclass(Optional.empty()); diff --git a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassDependencies.java b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassDependencies.java index 838ae055d3..667d3a3fc5 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassDependencies.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClassDependencies.java @@ -185,10 +185,8 @@ private Set genericParameterTypeArgumentDependencies(JavaCodeUnit co private Set throwsDeclarationDependenciesFromSelf() { ImmutableSet.Builder result = ImmutableSet.builder(); - for (JavaCodeUnit codeUnit : javaClass.getCodeUnits()) { - for (ThrowsDeclaration throwsDeclaration : codeUnit.getThrowsClause()) { - result.addAll(Dependency.tryCreateFromThrowsDeclaration(throwsDeclaration)); - } + for (ThrowsDeclaration throwsDeclaration : javaClass.getThrowsDeclarations()) { + result.addAll(Dependency.tryCreateFromThrowsDeclaration(throwsDeclaration)); } return result.build(); } From 2e3b4a8c07e40126efaf45f5a6a06dc52595b462 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Fri, 21 Jan 2022 10:53:13 +0700 Subject: [PATCH 16/22] automatically resolve types of throws declarations This will ensure that types referenced by throws declarations (e.g. `void method() throws SomeException`) are fully imported. Otherwise, things like annotations or class hierarchy will be missing for types that are not directly imported and only referenced through throws declarations. Signed-off-by: Peter Gafert --- .../ClassFileImporterAutomaticResolutionTest.java | 15 +++++++++++++++ .../core/importer/ClassFileProcessor.java | 5 +++++ .../core/importer/JavaClassProcessor.java | 14 +++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java index eb07a416ef..ab3d93fa39 100644 --- a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java @@ -23,6 +23,7 @@ import com.tngtech.archunit.core.domain.JavaMethodCall; import com.tngtech.archunit.core.domain.JavaMethodReference; import com.tngtech.archunit.core.domain.ReferencedClassObject; +import com.tngtech.archunit.core.domain.ThrowsDeclaration; import com.tngtech.archunit.core.domain.properties.HasAnnotations; import com.tngtech.archunit.core.importer.testexamples.SomeAnnotation; import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassWithUnimportedAnnotation; @@ -503,6 +504,20 @@ boolean call(Object obj) { assertThatType(instanceofCheck.getRawType()).matches(String.class); } + @Test + public void automatically_resolves_types_of_throws_declarations() { + @SuppressWarnings({"unused", "RedundantThrows"}) + class Origin { + void call() throws InterruptedException { + } + } + + ThrowsDeclaration throwsDeclaration = getOnlyElement(new ClassFileImporter().importClass(Origin.class).getThrowsDeclarations()); + + assertThat(throwsDeclaration.getRawType()).isFullyImported(true); + assertThatType(throwsDeclaration.getRawType()).matches(InterruptedException.class); + } + @MetaAnnotatedAnnotation private static class MetaAnnotatedClass { } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java index 4cc83e85c5..d4486c095b 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java @@ -184,6 +184,11 @@ public void onDeclaredClassObject(String typeName) { public void onDeclaredInstanceofCheck(String typeName) { dependencyResolutionProcess.registerAccessToType(typeName); } + + @Override + public void onDeclaredThrowsClause(Collection exceptionTypeNames) { + dependencyResolutionProcess.registerMemberTypes(exceptionTypeNames); + } } private static class RecordAccessHandler implements AccessHandler { diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java index d498496449..719e591983 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java @@ -259,6 +259,7 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si JavaClassDescriptor rawReturnType = JavaClassDescriptorImporter.importAsmMethodReturnType(desc); DomainBuilders.JavaCodeUnitBuilder codeUnitBuilder = addCodeUnitBuilder(name, codeUnit.getRawParameterTypeNames(), rawReturnType.getFullyQualifiedClassName()); JavaCodeUnitSignature codeUnitSignature = JavaCodeUnitSignatureImporter.parseAsmMethodSignature(signature); + List throwsDeclarations = typesFrom(exceptions); codeUnitBuilder .withName(name) .withModifiers(JavaModifier.getModifiersForMethod(access)) @@ -266,11 +267,20 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si .withParameterTypes(codeUnitSignature.getParameterTypes(), codeUnit.getRawParameterTypes()) .withReturnType(codeUnitSignature.getReturnType(), rawReturnType) .withDescriptor(desc) - .withThrowsClause(typesFrom(exceptions)); + .withThrowsClause(throwsDeclarations); + declarationHandler.onDeclaredThrowsClause(fullyQualifiedClassNamesOf(throwsDeclarations)); return new MethodProcessor(className, accessHandler, codeUnitBuilder, declarationHandler); } + private Collection fullyQualifiedClassNamesOf(List classDescriptors) { + ImmutableList.Builder result = ImmutableList.builder(); + for (JavaClassDescriptor classDescriptor : classDescriptors) { + result.add(classDescriptor.getFullyQualifiedClassName()); + } + return result.build(); + } + private List typesFrom(String[] throwsDeclarations) { List result = new ArrayList<>(); if (throwsDeclarations != null) { @@ -527,6 +537,8 @@ interface DeclarationHandler { void onDeclaredClassObject(String typeName); void onDeclaredInstanceofCheck(String typeName); + + void onDeclaredThrowsClause(Collection exceptionTypeNames); } interface AccessHandler { From 91ee9dcb64f6000be0912508d76e010d21e92e9c Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Wed, 26 Jan 2022 15:40:10 +0700 Subject: [PATCH 17/22] automatically resolve component types of array types This will ensure that component types of array types (e.g. `String` for `String[]`) are fully imported. Otherwise, things like annotations or class hierarchy will be missing for types that are not directly imported and only referenced through component types. In contrast to the other automatic resolutions I added this to the core resolve method in `ImportedClasses` because there are too many places where we also need to consider component types. Otherwise, this would never be robust for future changes (or at least always be a trip mine to overlook). To resolve the component type of any array type seems to be an intrinsic property of resolving any array type, so this seems fair to me. I thought about adding a shortcut to skip resolving further down the component type hierarchy if we encounter a type that has already been imported, but this would only help for repeated multi-dim arrays of the same component type but different dimensions (otherwise the cached type would be used anyway, and we don't resolve again). So, to me the benefit seems quite unclear, and we should probably wait until some bottleneck has shown before prematurely optimizing this at the cost of readability. Note that by this change the test `classes_not_fully_imported_have_flag_fullyImported_false_and_empty_dependencies` does not make sense anymore, because this can only happen if we resolve additional classes during the completion process. I do not think this will ever happen anymore (once we also resolve type parameters in the next step), because we strive to resolve all such dependencies ahead. So the cases appearing should only be fully imported or stub, which is already covered by the test `ClassFileImporterAutomaticResolutionTest.never_reports_stub_classes_as_fully_imported`. Likely to have the `FullCompletionProcess` within `JavaClass` will not make sense anymore, because we will not end up within the completion process needing a type that has not been resolved ahead. However, I am not 100% sure this is really the case under all conditions, so I will keep the `CompletionProcess` as it is. Signed-off-by: Peter Gafert --- ...ssFileImporterAutomaticResolutionTest.java | 20 +++++++ .../core/importer/ImportedClasses.java | 11 ++++ .../core/importer/ClassFileImporterTest.java | 52 ------------------- 3 files changed, 31 insertions(+), 52 deletions(-) diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java index ab3d93fa39..0c0ea35d40 100644 --- a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java @@ -518,6 +518,26 @@ void call() throws InterruptedException { assertThatType(throwsDeclaration.getRawType()).matches(InterruptedException.class); } + @Test + public void automatically_resolves_array_component_types() { + @SuppressWarnings("unused") + class Origin { + String[] oneDim; + + File[][] twoDim; + } + + JavaClass javaClass = new ClassFileImporter().importClass(Origin.class); + + JavaClass componentType = javaClass.getField("oneDim").getRawType().getComponentType(); + assertThat(componentType).isFullyImported(true); + assertThatType(componentType).matches(String.class); + + componentType = javaClass.getField("twoDim").getRawType().getComponentType().getComponentType(); + assertThat(componentType).isFullyImported(true); + assertThatType(componentType).matches(File.class); + } + @MetaAnnotatedAnnotation private static class MetaAnnotatedClass { } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ImportedClasses.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ImportedClasses.java index 2ed2e82a55..d6a2db2462 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ImportedClasses.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ImportedClasses.java @@ -73,10 +73,21 @@ ImportedClassState ensurePresent(String typeName) { private JavaClass resolve(String typeName) { Optional resolved = resolver.tryResolve(typeName); JavaClass javaClass = resolved.isPresent() ? resolved.get() : stubClassOf(typeName); + if (javaClass.isArray()) { + ensureAllComponentTypesPresent(javaClass); + } allClasses.put(typeName, javaClass); return javaClass; } + private void ensureAllComponentTypesPresent(JavaClass javaClass) { + JavaClassDescriptor current = JavaClassDescriptor.From.javaClass(javaClass); + while (current.tryGetComponentType().isPresent()) { + current = current.tryGetComponentType().get(); + ensurePresent(current.getFullyQualifiedClassName()); + } + } + Collection getAllWithOuterClassesSortedBeforeInnerClasses() { return ImmutableSortedMap.copyOf(allClasses).values(); } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterTest.java index 90c3dd98db..768fa27875 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterTest.java @@ -13,7 +13,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.Callable; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; @@ -111,8 +110,6 @@ import static com.tngtech.archunit.testutil.TestUtils.uriOf; import static com.tngtech.archunit.testutil.TestUtils.urlOf; import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteParameterizedType.parameterizedType; -import static com.tngtech.java.junit.dataprovider.DataProviders.$; -import static com.tngtech.java.junit.dataprovider.DataProviders.$$; import static com.tngtech.java.junit.dataprovider.DataProviders.testForEach; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assume.assumeTrue; @@ -786,55 +783,6 @@ public void resolve_missing_dependencies_from_classpath_can_be_toggled() { assertThat(clazz.getRawSuperclass().get().getMethods()).isEmpty(); } - @DataProvider - public static Object[][] classes_not_fully_imported() { - class Element { - } - @SuppressWarnings("unused") - class DependsOnArray { - Element[] array; - } - - return ArchConfigurationRule.resetConfigurationAround(new Callable() { - @Override - public Object[][] call() { - ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(true); - JavaClass resolvedFromClasspath = new ClassFileImporter().importClasses(DependsOnArray.class) - .get(DependsOnArray.class).getField("array").getRawType().getComponentType(); - - ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - JavaClass stub = new ClassFileImporter().importClasses(DependsOnArray.class) - .get(DependsOnArray.class).getField("array").getRawType().getComponentType(); - - return $$( - $("Resolved from classpath", resolvedFromClasspath), - $("Stub class", stub) - ); - } - }); - } - - @Test - @UseDataProvider("classes_not_fully_imported") - public void classes_not_fully_imported_have_flag_fullyImported_false_and_empty_dependencies(@SuppressWarnings("unused") String description, JavaClass notFullyImported) { - assertThat(notFullyImported).isFullyImported(false); - assertThat(notFullyImported.getDirectDependenciesFromSelf()).isEmpty(); - assertThat(notFullyImported.getDirectDependenciesToSelf()).isEmpty(); - assertThat(notFullyImported.getFieldAccessesToSelf()).isEmpty(); - assertThat(notFullyImported.getMethodCallsToSelf()).isEmpty(); - assertThat(notFullyImported.getConstructorCallsToSelf()).isEmpty(); - assertThat(notFullyImported.getAccessesToSelf()).isEmpty(); - assertThat(notFullyImported.getFieldsWithTypeOfSelf()).isEmpty(); - assertThat(notFullyImported.getMethodsWithParameterTypeOfSelf()).isEmpty(); - assertThat(notFullyImported.getMethodsWithReturnTypeOfSelf()).isEmpty(); - assertThat(notFullyImported.getMethodThrowsDeclarationsWithTypeOfSelf()).isEmpty(); - assertThat(notFullyImported.getConstructorsWithParameterTypeOfSelf()).isEmpty(); - assertThat(notFullyImported.getConstructorsWithThrowsDeclarationTypeOfSelf()).isEmpty(); - assertThat(notFullyImported.getAnnotationsWithTypeOfSelf()).isEmpty(); - assertThat(notFullyImported.getAnnotationsWithParameterTypeOfSelf()).isEmpty(); - assertThat(notFullyImported.getInstanceofChecksWithTypeOfSelf()).isEmpty(); - } - @Test public void import_is_resilient_against_broken_class_files() throws Exception { Class expectedClass = getClass(); From 8367ff7ee66cf8382ce52384e3210c81c703b2b3 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Wed, 2 Feb 2022 11:59:27 +0700 Subject: [PATCH 18/22] automatically resolve enclosing classes of nested classes This will ensure that enclosing classes (e.g. `class Outer { class Inner {} }`) are fully imported. Otherwise, things like annotations or class hierarchy will be missing for types that are not directly imported. I had to adjust `ClassesShouldEvaluator` to exclude all dependencies from test methods, because we now automatically resolve the test class (enclosing class of nested test classes) and by that obtain many new dependencies since the Test references the test example class objects. Signed-off-by: Peter Gafert --- ...ssFileImporterAutomaticResolutionTest.java | 36 +++++++++++++++++++ .../core/importer/ClassFileProcessor.java | 1 + .../importer/DependencyResolutionProcess.java | 7 ++++ .../elements/ClassesShouldEvaluator.java | 11 +++++- 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java index 0c0ea35d40..56c85234a0 100644 --- a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java @@ -538,6 +538,42 @@ class Origin { assertThatType(componentType).matches(File.class); } + @Test + public void automatically_resolves_enclosing_classes() throws ClassNotFoundException { + @SuppressWarnings("unused") + class Outermost { + class LessOuter { + class LeastOuter { + void call() { + class LessInner { + class Innermost { + } + } + } + } + } + } + + Class lessInnerClass = Class.forName(Outermost.LessOuter.LeastOuter.class.getName() + "$1LessInner"); + JavaClass innermost = new ClassFileImporter().importClass(Class.forName(lessInnerClass.getName() + "$Innermost")); + + JavaClass lessInner = innermost.getEnclosingClass().get(); + assertThat(lessInner).isFullyImported(true); + assertThatType(lessInner).matches(lessInnerClass); + + JavaClass leastOuter = lessInner.getEnclosingClass().get(); + assertThat(leastOuter).isFullyImported(true); + assertThatType(leastOuter).matches(Outermost.LessOuter.LeastOuter.class); + + JavaClass lessOuter = leastOuter.getEnclosingClass().get(); + assertThat(lessOuter).isFullyImported(true); + assertThatType(lessOuter).matches(Outermost.LessOuter.class); + + JavaClass outermost = lessOuter.getEnclosingClass().get(); + assertThat(outermost).isFullyImported(true); + assertThatType(outermost).matches(Outermost.class); + } + @MetaAnnotatedAnnotation private static class MetaAnnotatedClass { } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java index d4486c095b..3e7e382bbb 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java @@ -168,6 +168,7 @@ public void onDeclaredAnnotationDefaultValue(String methodName, String methodDes @Override public void registerEnclosingClass(String ownerName, String enclosingClassName) { importRecord.setEnclosingClass(ownerName, enclosingClassName); + dependencyResolutionProcess.registerEnclosingType(enclosingClassName); } @Override diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java index 976078eb82..4f17db32e0 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java @@ -27,6 +27,7 @@ class DependencyResolutionProcess { private static final int maxRunsForMemberTypes = 1; private static final int maxRunsForAccessesToTypes = 1; private static final int maxRunsForSupertypes = -1; + private static final int maxRunsForEnclosingTypes = -1; private static final int maxRunsForAnnotationTypes = -1; private Set currentTypeNames = new HashSet<>(); @@ -63,6 +64,12 @@ void registerSupertypes(Collection typeNames) { } } + void registerEnclosingType(String typeName) { + if (runNumberHasNotExceeded(maxRunsForEnclosingTypes)) { + currentTypeNames.add(typeName); + } + } + void registerAnnotationType(String typeName) { if (runNumberHasNotExceeded(maxRunsForAnnotationTypes)) { currentTypeNames.add(typeName); diff --git a/archunit/src/test/java/com/tngtech/archunit/lang/syntax/elements/ClassesShouldEvaluator.java b/archunit/src/test/java/com/tngtech/archunit/lang/syntax/elements/ClassesShouldEvaluator.java index 2479d598fd..84c650f8fa 100644 --- a/archunit/src/test/java/com/tngtech/archunit/lang/syntax/elements/ClassesShouldEvaluator.java +++ b/archunit/src/test/java/com/tngtech/archunit/lang/syntax/elements/ClassesShouldEvaluator.java @@ -64,7 +64,12 @@ private boolean anyLineMatches(List relevantFailures, JavaClass clazz) { private List getRelevantFailures(JavaClasses classes) { List relevant = new ArrayList<>(); for (String line : linesIn(rule.evaluate(classes).getFailureReport())) { - if (!isDefaultConstructor(line) && !isSelfReference(line) && !isExtendsJavaLangAnnotation(line)) { + if ( + !isDefaultConstructor(line) + && !isSelfReference(line) + && !isExtendsJavaLangAnnotation(line) + && !isDirectDependencyFromTest(line) + ) { relevant.add(line); } } @@ -83,6 +88,10 @@ private boolean isExtendsJavaLangAnnotation(String line) { return line.matches(String.format(".*extends.*<%s> in.*", Annotation.class.getName())); } + private boolean isDirectDependencyFromTest(String line) { + return line.matches("^Method <.*Test\\..*> .*"); + } + private List linesIn(FailureReport failureReport) { List result = new ArrayList<>(); for (String details : failureReport.getDetails()) { From b9d96008e1c85a23b77f0708bb00d2cfd27a52aa Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sun, 30 Jan 2022 10:28:23 +0700 Subject: [PATCH 19/22] move `JavaClassProcessor.DeclarationHandler` toplevel This is meanwhile also used by `JavaClassSignatureImporter`, so it is a strange location to have it declared within `JavaClassProcessor` which in turn uses the `JavaClassSignatureImporter`. This creates an unnecessary cyclic dependency between the files. Signed-off-by: Peter Gafert --- .../core/importer/ClassFileProcessor.java | 1 - .../core/importer/DeclarationHandler.java | 61 +++++++++++++++++++ .../core/importer/JavaClassProcessor.java | 38 ------------ .../importer/JavaClassSignatureImporter.java | 1 - 4 files changed, 61 insertions(+), 40 deletions(-) create mode 100644 archunit/src/main/java/com/tngtech/archunit/core/importer/DeclarationHandler.java diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java index 3e7e382bbb..e2db07b183 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java @@ -34,7 +34,6 @@ import com.tngtech.archunit.core.importer.DomainBuilders.JavaParameterizedTypeBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaStaticInitializerBuilder; import com.tngtech.archunit.core.importer.JavaClassProcessor.AccessHandler; -import com.tngtech.archunit.core.importer.JavaClassProcessor.DeclarationHandler; import com.tngtech.archunit.core.importer.RawAccessRecord.CodeUnit; import com.tngtech.archunit.core.importer.RawAccessRecord.TargetInfo; import com.tngtech.archunit.core.importer.resolvers.ClassResolver; diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/DeclarationHandler.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/DeclarationHandler.java new file mode 100644 index 0000000000..e527a1afda --- /dev/null +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/DeclarationHandler.java @@ -0,0 +1,61 @@ +/* + * Copyright 2014-2022 TNG Technology Consulting GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.tngtech.archunit.core.importer; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import com.tngtech.archunit.base.Optional; +import com.tngtech.archunit.core.domain.JavaClass; + +interface DeclarationHandler { + boolean isNew(String className); + + void onNewClass(String className, Optional superclassName, List interfaceNames); + + void onDeclaredTypeParameters(DomainBuilders.JavaClassTypeParametersBuilder typeParametersBuilder); + + void onGenericSuperclass(DomainBuilders.JavaParameterizedTypeBuilder genericSuperclassBuilder); + + void onGenericInterfaces(List> genericInterfaceBuilders); + + void onDeclaredField(DomainBuilders.JavaFieldBuilder fieldBuilder, String fieldTypeName); + + void onDeclaredConstructor(DomainBuilders.JavaConstructorBuilder constructorBuilder, Collection rawParameterTypeNames); + + void onDeclaredMethod(DomainBuilders.JavaMethodBuilder methodBuilder, Collection rawParameterTypeNames, String rawReturnTypeName); + + void onDeclaredStaticInitializer(DomainBuilders.JavaStaticInitializerBuilder staticInitializerBuilder); + + void onDeclaredClassAnnotations(Set annotationBuilders); + + void onDeclaredMemberAnnotations(String memberName, String descriptor, Set annotations); + + void onDeclaredAnnotationValueType(String valueTypeName); + + void onDeclaredAnnotationDefaultValue(String methodName, String methodDescriptor, DomainBuilders.JavaAnnotationBuilder.ValueBuilder valueBuilder); + + void registerEnclosingClass(String ownerName, String enclosingClassName); + + void registerEnclosingCodeUnit(String ownerName, RawAccessRecord.CodeUnit enclosingCodeUnit); + + void onDeclaredClassObject(String typeName); + + void onDeclaredInstanceofCheck(String typeName); + + void onDeclaredThrowsClause(Collection exceptionTypeNames); +} diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java index 719e591983..f135a65928 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java @@ -503,44 +503,6 @@ public void setArrayResult(ValueBuilder valueBuilder) { } } - interface DeclarationHandler { - boolean isNew(String className); - - void onNewClass(String className, Optional superclassName, List interfaceNames); - - void onDeclaredTypeParameters(DomainBuilders.JavaClassTypeParametersBuilder typeParametersBuilder); - - void onGenericSuperclass(DomainBuilders.JavaParameterizedTypeBuilder genericSuperclassBuilder); - - void onGenericInterfaces(List> genericInterfaceBuilders); - - void onDeclaredField(DomainBuilders.JavaFieldBuilder fieldBuilder, String fieldTypeName); - - void onDeclaredConstructor(DomainBuilders.JavaConstructorBuilder constructorBuilder, Collection rawParameterTypeNames); - - void onDeclaredMethod(DomainBuilders.JavaMethodBuilder methodBuilder, Collection rawParameterTypeNames, String rawReturnTypeName); - - void onDeclaredStaticInitializer(DomainBuilders.JavaStaticInitializerBuilder staticInitializerBuilder); - - void onDeclaredClassAnnotations(Set annotationBuilders); - - void onDeclaredMemberAnnotations(String memberName, String descriptor, Set annotations); - - void onDeclaredAnnotationValueType(String valueTypeName); - - void onDeclaredAnnotationDefaultValue(String methodName, String methodDescriptor, ValueBuilder valueBuilder); - - void registerEnclosingClass(String ownerName, String enclosingClassName); - - void registerEnclosingCodeUnit(String ownerName, CodeUnit enclosingCodeUnit); - - void onDeclaredClassObject(String typeName); - - void onDeclaredInstanceofCheck(String typeName); - - void onDeclaredThrowsClause(Collection exceptionTypeNames); - } - interface AccessHandler { void handleFieldInstruction(int opcode, String owner, String name, String desc); diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassSignatureImporter.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassSignatureImporter.java index 2806e256dc..a378f53a4d 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassSignatureImporter.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassSignatureImporter.java @@ -23,7 +23,6 @@ import com.tngtech.archunit.core.importer.DomainBuilders.JavaClassTypeParametersBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaParameterizedTypeBuilder; import com.tngtech.archunit.core.importer.DomainBuilders.JavaTypeParameterBuilder; -import com.tngtech.archunit.core.importer.JavaClassProcessor.DeclarationHandler; import org.objectweb.asm.signature.SignatureReader; import org.objectweb.asm.signature.SignatureVisitor; import org.slf4j.Logger; From 5d761ca7b7cd7bc34d52b7b6daa0bacdeba5f8ad Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sat, 5 Feb 2022 10:03:20 +0700 Subject: [PATCH 20/22] automatically resolve generic type signatures This will ensure that types referenced by generic signatures (e.g. `class SomeClass>`) are fully imported. Otherwise, things like annotations or class hierarchy will be missing for types that are not directly imported and only referenced as part of a generic type signature. Signed-off-by: Peter Gafert --- ...ssFileImporterAutomaticResolutionTest.java | 185 ++++++++++++++++++ .../core/importer/ClassFileProcessor.java | 5 + .../core/importer/DeclarationHandler.java | 2 + .../importer/DependencyResolutionProcess.java | 7 + .../importer/GenericMemberTypeProcessor.java | 6 +- .../core/importer/JavaClassProcessor.java | 4 +- .../importer/JavaClassSignatureImporter.java | 25 ++- .../JavaCodeUnitSignatureImporter.java | 16 +- .../JavaFieldTypeSignatureImporter.java | 9 +- .../SignatureTypeArgumentProcessor.java | 23 ++- .../SignatureTypeParameterProcessor.java | 7 +- .../ClassFileImporterGenericClassesTest.java | 161 +++++---------- ...rterGenericCodeUnitParameterTypesTest.java | 179 +++++++++-------- ...lassFileImporterGenericFieldTypesTest.java | 133 +++++-------- ...lassFileImporterGenericInterfacesTest.java | 160 ++++----------- ...eImporterGenericMethodReturnTypesTest.java | 135 +++++-------- ...leImporterGenericMethodSignaturesTest.java | 152 +++++++------- ...lassFileImporterGenericSuperclassTest.java | 120 ++++-------- 18 files changed, 635 insertions(+), 694 deletions(-) diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java index 56c85234a0..3744eb72b0 100644 --- a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java @@ -6,6 +6,10 @@ import java.nio.Buffer; import java.nio.file.FileSystem; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.function.Supplier; import com.google.common.collect.FluentIterable; @@ -22,6 +26,9 @@ import com.tngtech.archunit.core.domain.JavaMethod; import com.tngtech.archunit.core.domain.JavaMethodCall; import com.tngtech.archunit.core.domain.JavaMethodReference; +import com.tngtech.archunit.core.domain.JavaParameterizedType; +import com.tngtech.archunit.core.domain.JavaType; +import com.tngtech.archunit.core.domain.JavaWildcardType; import com.tngtech.archunit.core.domain.ReferencedClassObject; import com.tngtech.archunit.core.domain.ThrowsDeclaration; import com.tngtech.archunit.core.domain.properties.HasAnnotations; @@ -53,6 +60,7 @@ import static com.tngtech.archunit.testutil.Assertions.assertThatType; import static com.tngtech.archunit.testutil.assertion.JavaAnnotationAssertion.annotationProperty; import static com.tngtech.java.junit.dataprovider.DataProviders.$; +import static com.tngtech.java.junit.dataprovider.DataProviders.$$; import static com.tngtech.java.junit.dataprovider.DataProviders.testForEach; @RunWith(DataProviderRunner.class) @@ -574,6 +582,183 @@ class Innermost { assertThatType(outermost).matches(Outermost.class); } + @DataProvider + public static Object[][] data_automatically_resolves_generic_type_parameter_bounds() { + @SuppressWarnings("unused") + class TypeParameterOnClassWithClassBound { + } + @SuppressWarnings("unused") + class TypeParameterOnClassWithInterfaceBound { + } + @SuppressWarnings("unused") + class TypeParameterOnMethodWithClassBound { + void method() { + } + } + @SuppressWarnings("unused") + class TypeParameterOnMethodWithInterfaceBound { + void method() { + } + } + @SuppressWarnings("unused") + class TypeArgumentWithClassBoundOnField { + List field; + } + @SuppressWarnings("unused") + class TypeArgumentWithInterfaceBoundOnField { + List field; + } + @SuppressWarnings("unused") + class TypeArgumentWithClassBoundOnMethod { + void method(List param) { + } + } + @SuppressWarnings("unused") + class TypeArgumentWithInterfaceBoundOnMethod { + void method(List param) { + } + } + @SuppressWarnings("unused") + class TypeArgumentWithClassBoundOnConstructor { + TypeArgumentWithClassBoundOnConstructor(List param) { + } + } + @SuppressWarnings("unused") + class TypeArgumentWithInterfaceBoundOnConstructor { + TypeArgumentWithInterfaceBoundOnConstructor(List param) { + } + } + + return $$( + $(importFirstTypeParameterClassBound(TypeParameterOnClassWithClassBound.class), String.class), + $(importFirstTypeParameterClassBound(TypeParameterOnClassWithInterfaceBound.class), Serializable.class), + $(importFirstTypeParameterMethodBound(TypeParameterOnMethodWithClassBound.class), String.class), + $(importFirstTypeParameterMethodBound(TypeParameterOnMethodWithInterfaceBound.class), Serializable.class), + $(importFirstTypeArgumentFieldBound(TypeArgumentWithClassBoundOnField.class), String.class), + $(importFirstTypeArgumentFieldBound(TypeArgumentWithInterfaceBoundOnField.class), Serializable.class), + $(importFirstTypeArgumentMethodParameterBound(TypeArgumentWithClassBoundOnMethod.class), String.class), + $(importFirstTypeArgumentMethodParameterBound(TypeArgumentWithInterfaceBoundOnMethod.class), Serializable.class), + $(importFirstTypeArgumentConstructorParameterBound(TypeArgumentWithClassBoundOnConstructor.class), String.class), + $(importFirstTypeArgumentConstructorParameterBound(TypeArgumentWithInterfaceBoundOnConstructor.class), Serializable.class) + ); + } + + @Test + @UseDataProvider + public void test_automatically_resolves_generic_type_parameter_bounds(JavaClass bound, Class expectedType) { + assertThat(bound).isFullyImported(true); + assertThatType(bound).matches(expectedType); + } + + @DataProvider + public static Object[][] data_automatically_resolves_parameterized_generic_type_bounds() { + @SuppressWarnings("unused") + class InterfaceTypeParameterBoundsOnClass>> { + } + @SuppressWarnings("unused") + class ClassTypeParameterBoundsOnClass>> { + } + @SuppressWarnings("unused") + class InterfaceTypeParameterBoundsOnMethod { + >> void method() { + } + } + @SuppressWarnings("unused") + class ClassTypeParameterBoundsOnMethod { + >> void method() { + } + } + @SuppressWarnings("unused") + class InterfaceTypeArgumentBoundsOnField { + List>> field; + } + @SuppressWarnings("unused") + class ClassTypeArgumentBoundsOnField { + ArrayList>> field; + } + @SuppressWarnings("unused") + class InterfaceTypeArgumentBoundsOnMethod { + void method(List>> param) { + } + } + @SuppressWarnings("unused") + class ClassTypeArgumentBoundsOnMethod { + void method(ArrayList>> param) { + } + } + @SuppressWarnings("unused") + class InterfaceTypeArgumentBoundsOnConstructor { + InterfaceTypeArgumentBoundsOnConstructor(List>> param) { + } + } + @SuppressWarnings("unused") + class ClassTypeArgumentBoundsOnConstructor { + ClassTypeArgumentBoundsOnConstructor(ArrayList>> param) { + } + } + + return $$( + $(importFirstTypeParameterClassBound(InterfaceTypeParameterBoundsOnClass.class), List.class, Map.class, Serializable.class), + $(importFirstTypeParameterClassBound(ClassTypeParameterBoundsOnClass.class), ArrayList.class, HashMap.class, String.class), + $(importFirstTypeParameterMethodBound(InterfaceTypeParameterBoundsOnMethod.class), List.class, Map.class, Serializable.class), + $(importFirstTypeParameterMethodBound(ClassTypeParameterBoundsOnMethod.class), ArrayList.class, HashMap.class, String.class), + $(importFirstTypeArgumentFieldBound(InterfaceTypeArgumentBoundsOnField.class), List.class, Map.class, Serializable.class), + $(importFirstTypeArgumentFieldBound(ClassTypeArgumentBoundsOnField.class), ArrayList.class, HashMap.class, String.class), + $(importFirstTypeArgumentMethodParameterBound(InterfaceTypeArgumentBoundsOnMethod.class), List.class, Map.class, Serializable.class), + $(importFirstTypeArgumentMethodParameterBound(ClassTypeArgumentBoundsOnMethod.class), ArrayList.class, HashMap.class, String.class), + $(importFirstTypeArgumentConstructorParameterBound(InterfaceTypeArgumentBoundsOnConstructor.class), List.class, Map.class, Serializable.class), + $(importFirstTypeArgumentConstructorParameterBound(ClassTypeArgumentBoundsOnConstructor.class), ArrayList.class, HashMap.class, String.class) + ); + } + + @Test + @UseDataProvider + public void test_automatically_resolves_parameterized_generic_type_bounds( + JavaParameterizedType actual1stLevel, + Class expected1stLevel, Class expected2ndLevel, Class expected3rdLevel + ) { + assertThat(actual1stLevel.toErasure()).isFullyImported(true); + assertThatType(actual1stLevel.toErasure()).matches(expected1stLevel); + + JavaParameterizedType actual2ndLevel = (JavaParameterizedType) getOnlyElement(((JavaWildcardType) getOnlyElement(actual1stLevel.getActualTypeArguments())).getUpperBounds()); + + assertThat(actual2ndLevel.toErasure()).isFullyImported(true); + assertThatType(actual2ndLevel.toErasure()).matches(expected2ndLevel); + + JavaClass actual3rdLevel = (JavaClass) getOnlyElement(((JavaWildcardType) actual2ndLevel.getActualTypeArguments().get(1)).getLowerBounds()); + + assertThat(actual3rdLevel.toErasure()).isFullyImported(true); + assertThatType(actual3rdLevel.toErasure()).matches(expected3rdLevel); + } + + private static JavaType importFirstTypeParameterClassBound(Class clazz) { + return getOnlyElement(getOnlyElement(new ClassFileImporter().importClass(clazz).getTypeParameters()).getBounds()); + } + + private static JavaType importFirstTypeParameterMethodBound(Class clazz) { + return getOnlyElement(getOnlyElement(new ClassFileImporter().importClass(clazz).getMethod("method").getTypeParameters()).getBounds()); + } + + private static JavaType importFirstTypeArgumentFieldBound(Class clazz) { + return getFirstTypeArgumentUpperBound(new ClassFileImporter().importClass(clazz).getField("field").getType()); + } + + private static JavaType importFirstTypeArgumentMethodParameterBound(Class clazz) { + JavaMethod method = getOnlyElement(new ClassFileImporter().importClass(clazz).getMethods()); + return getFirstTypeArgumentUpperBound(method.getParameterTypes().get(0)); + } + + private static JavaType importFirstTypeArgumentConstructorParameterBound(Class clazz) { + JavaConstructor constructor = getOnlyElement(new ClassFileImporter().importClass(clazz).getConstructors()); + return getFirstTypeArgumentUpperBound(constructor.getParameterTypes().get(0)); + } + + private static JavaType getFirstTypeArgumentUpperBound(JavaType type) { + JavaParameterizedType parameterizedType = (JavaParameterizedType) type; + JavaWildcardType firstTypeArgument = (JavaWildcardType) parameterizedType.getActualTypeArguments().get(0); + return firstTypeArgument.getUpperBounds().get(0); + } + @MetaAnnotatedAnnotation private static class MetaAnnotatedClass { } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java index e2db07b183..dc70fa9711 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/ClassFileProcessor.java @@ -189,6 +189,11 @@ public void onDeclaredInstanceofCheck(String typeName) { public void onDeclaredThrowsClause(Collection exceptionTypeNames) { dependencyResolutionProcess.registerMemberTypes(exceptionTypeNames); } + + @Override + public void onDeclaredGenericSignatureType(String typeName) { + dependencyResolutionProcess.registerGenericSignatureType(typeName); + } } private static class RecordAccessHandler implements AccessHandler { diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/DeclarationHandler.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/DeclarationHandler.java index e527a1afda..885c6c1873 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/DeclarationHandler.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/DeclarationHandler.java @@ -58,4 +58,6 @@ interface DeclarationHandler { void onDeclaredInstanceofCheck(String typeName); void onDeclaredThrowsClause(Collection exceptionTypeNames); + + void onDeclaredGenericSignatureType(String typeName); } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java index 4f17db32e0..3943310f5b 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java @@ -29,6 +29,7 @@ class DependencyResolutionProcess { private static final int maxRunsForSupertypes = -1; private static final int maxRunsForEnclosingTypes = -1; private static final int maxRunsForAnnotationTypes = -1; + private static final int maxRunsForGenericSignatureTypes = -1; private Set currentTypeNames = new HashSet<>(); private int runNumber = 1; @@ -76,6 +77,12 @@ void registerAnnotationType(String typeName) { } } + void registerGenericSignatureType(String typeName) { + if (runNumberHasNotExceeded(maxRunsForGenericSignatureTypes)) { + currentTypeNames.add(typeName); + } + } + void resolve(ImportedClasses classes) { do { executeRun(classes); diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/GenericMemberTypeProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/GenericMemberTypeProcessor.java index 6b2ecceb56..e0c6ff8ab7 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/GenericMemberTypeProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/GenericMemberTypeProcessor.java @@ -28,12 +28,14 @@ import static com.tngtech.archunit.core.importer.DomainBuilders.JavaTypeCreationProcess.JavaTypeFinisher.ARRAY_CREATOR; class GenericMemberTypeProcessor extends SignatureVisitor { + private final DeclarationHandler declarationHandler; private JavaParameterizedTypeBuilder parameterizedType; private JavaTypeCreationProcess typeCreationProcess; private JavaTypeFinisher typeFinisher = JavaTypeFinisher.IDENTITY; - GenericMemberTypeProcessor() { + GenericMemberTypeProcessor(DeclarationHandler declarationHandler) { super(ASM_API_VERSION); + this.declarationHandler = declarationHandler; } Optional> getType() { @@ -62,7 +64,7 @@ public void visitTypeArgument() { @Override public SignatureVisitor visitTypeArgument(char wildcard) { - return SignatureTypeArgumentProcessor.create(wildcard, parameterizedType, JavaTypeFinisher.IDENTITY); + return SignatureTypeArgumentProcessor.create(wildcard, parameterizedType, JavaTypeFinisher.IDENTITY, declarationHandler); } @Override diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java index f135a65928..65a3b79c62 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassProcessor.java @@ -236,7 +236,7 @@ public FieldVisitor visitField(int access, String name, String desc, String sign } JavaClassDescriptor rawType = JavaClassDescriptorImporter.importAsmTypeFromDescriptor(desc); - Optional> genericType = JavaFieldTypeSignatureImporter.parseAsmFieldTypeSignature(signature); + Optional> genericType = JavaFieldTypeSignatureImporter.parseAsmFieldTypeSignature(signature, declarationHandler); DomainBuilders.JavaFieldBuilder fieldBuilder = new DomainBuilders.JavaFieldBuilder() .withName(name) .withType(genericType, rawType) @@ -258,7 +258,7 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si JavaClassDescriptor rawReturnType = JavaClassDescriptorImporter.importAsmMethodReturnType(desc); DomainBuilders.JavaCodeUnitBuilder codeUnitBuilder = addCodeUnitBuilder(name, codeUnit.getRawParameterTypeNames(), rawReturnType.getFullyQualifiedClassName()); - JavaCodeUnitSignature codeUnitSignature = JavaCodeUnitSignatureImporter.parseAsmMethodSignature(signature); + JavaCodeUnitSignature codeUnitSignature = JavaCodeUnitSignatureImporter.parseAsmMethodSignature(signature, declarationHandler); List throwsDeclarations = typesFrom(exceptions); codeUnitBuilder .withName(name) diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassSignatureImporter.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassSignatureImporter.java index a378f53a4d..8cb422c3c0 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassSignatureImporter.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaClassSignatureImporter.java @@ -40,7 +40,7 @@ static void parseAsmTypeSignature(String signature, DeclarationHandler declarati log.trace("Analyzing signature: {}", signature); - SignatureProcessor signatureProcessor = new SignatureProcessor(); + SignatureProcessor signatureProcessor = new SignatureProcessor(declarationHandler); new SignatureReader(signature).accept(signatureProcessor); declarationHandler.onDeclaredTypeParameters(new JavaClassTypeParametersBuilder(signatureProcessor.getTypeParameterBuilders())); @@ -53,12 +53,15 @@ static void parseAsmTypeSignature(String signature, DeclarationHandler declarati } private static class SignatureProcessor extends SignatureVisitor { - private final SignatureTypeParameterProcessor typeParameterProcessor = new SignatureTypeParameterProcessor<>(); - private final GenericSuperclassProcessor superclassProcessor = new GenericSuperclassProcessor(); - private final GenericInterfacesProcessor interfacesProcessor = new GenericInterfacesProcessor(); + private final SignatureTypeParameterProcessor typeParameterProcessor; + private final GenericSuperclassProcessor superclassProcessor; + private final GenericInterfacesProcessor interfacesProcessor; - SignatureProcessor() { + SignatureProcessor(DeclarationHandler declarationHandler) { super(ASM_API_VERSION); + typeParameterProcessor = new SignatureTypeParameterProcessor<>(declarationHandler); + superclassProcessor = new GenericSuperclassProcessor(declarationHandler); + interfacesProcessor = new GenericInterfacesProcessor(declarationHandler); } List> getTypeParameterBuilders() { @@ -100,10 +103,12 @@ public SignatureVisitor visitInterfaceBound() { } private static class GenericSuperclassProcessor extends SignatureVisitor { + private final DeclarationHandler declarationHandler; private JavaParameterizedTypeBuilder superclass; - GenericSuperclassProcessor() { + GenericSuperclassProcessor(DeclarationHandler declarationHandler) { super(ASM_API_VERSION); + this.declarationHandler = declarationHandler; } @Override @@ -118,16 +123,18 @@ public void visitInnerClassType(String name) { @Override public SignatureVisitor visitTypeArgument(char wildcard) { - return SignatureTypeArgumentProcessor.create(wildcard, superclass); + return SignatureTypeArgumentProcessor.create(wildcard, superclass, declarationHandler); } } private static class GenericInterfacesProcessor extends SignatureVisitor { + private final DeclarationHandler declarationHandler; private final List> interfaces = new ArrayList<>(); private JavaParameterizedTypeBuilder currentInterface; - GenericInterfacesProcessor() { + GenericInterfacesProcessor(DeclarationHandler declarationHandler) { super(ASM_API_VERSION); + this.declarationHandler = declarationHandler; } @Override @@ -138,7 +145,7 @@ public void visitClassType(String internalObjectName) { @Override public SignatureVisitor visitTypeArgument(char wildcard) { - return SignatureTypeArgumentProcessor.create(wildcard, currentInterface); + return SignatureTypeArgumentProcessor.create(wildcard, currentInterface, declarationHandler); } } } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaCodeUnitSignatureImporter.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaCodeUnitSignatureImporter.java index c7dcb94610..7a871deadf 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaCodeUnitSignatureImporter.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaCodeUnitSignatureImporter.java @@ -34,25 +34,29 @@ class JavaCodeUnitSignatureImporter { private static final Logger log = LoggerFactory.getLogger(JavaCodeUnitSignatureImporter.class); - public static JavaCodeUnitSignature parseAsmMethodSignature(String signature) { + public static JavaCodeUnitSignature parseAsmMethodSignature(String signature, DeclarationHandler declarationHandler) { if (signature == null) { return JavaCodeUnitSignature.ABSENT; } log.trace("Analyzing method signature: {}", signature); - SignatureProcessor signatureProcessor = new SignatureProcessor(); + SignatureProcessor signatureProcessor = new SignatureProcessor(declarationHandler); new SignatureReader(signature).accept(signatureProcessor); return signatureProcessor.getParsedSignature(); } private static class SignatureProcessor extends SignatureVisitor { - private final SignatureTypeParameterProcessor typeParameterProcessor = new SignatureTypeParameterProcessor<>(); + private final DeclarationHandler declarationHandler; + private final SignatureTypeParameterProcessor typeParameterProcessor; + private final GenericMemberTypeProcessor genericMethodReturnTypeProcessor; private final List> genericMethodParameterTypeProcessors = new ArrayList<>(); - private final GenericMemberTypeProcessor genericMethodReturnTypeProcessor = new GenericMemberTypeProcessor<>(); - SignatureProcessor() { + SignatureProcessor(DeclarationHandler declarationHandler) { super(ASM_API_VERSION); + this.declarationHandler = declarationHandler; + typeParameterProcessor = new SignatureTypeParameterProcessor<>(declarationHandler); + genericMethodReturnTypeProcessor = new GenericMemberTypeProcessor<>(declarationHandler); } @Override @@ -73,7 +77,7 @@ public SignatureVisitor visitInterfaceBound() { @Override public SignatureVisitor visitParameterType() { - GenericMemberTypeProcessor parameterTypeProcessor = new GenericMemberTypeProcessor<>(); + GenericMemberTypeProcessor parameterTypeProcessor = new GenericMemberTypeProcessor<>(declarationHandler); genericMethodParameterTypeProcessors.add(parameterTypeProcessor); return parameterTypeProcessor; } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaFieldTypeSignatureImporter.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaFieldTypeSignatureImporter.java index 590123a2c6..b169f59cfe 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaFieldTypeSignatureImporter.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/JavaFieldTypeSignatureImporter.java @@ -28,23 +28,24 @@ class JavaFieldTypeSignatureImporter { private static final Logger log = LoggerFactory.getLogger(JavaFieldTypeSignatureImporter.class); - static Optional> parseAsmFieldTypeSignature(String signature) { + static Optional> parseAsmFieldTypeSignature(String signature, DeclarationHandler declarationHandler) { if (signature == null) { return Optional.empty(); } log.trace("Analyzing field signature: {}", signature); - SignatureProcessor signatureProcessor = new SignatureProcessor(); + SignatureProcessor signatureProcessor = new SignatureProcessor(declarationHandler); new SignatureReader(signature).accept(signatureProcessor); return signatureProcessor.getFieldType(); } private static class SignatureProcessor extends SignatureVisitor { - private final GenericMemberTypeProcessor genericFieldTypeProcessor = new GenericMemberTypeProcessor<>(); + private final GenericMemberTypeProcessor genericFieldTypeProcessor; - SignatureProcessor() { + SignatureProcessor(DeclarationHandler declarationHandler) { super(ASM_API_VERSION); + genericFieldTypeProcessor = new GenericMemberTypeProcessor<>(declarationHandler); } @Override diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/SignatureTypeArgumentProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/SignatureTypeArgumentProcessor.java index 896cc77e28..c54dfd8709 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/SignatureTypeArgumentProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/SignatureTypeArgumentProcessor.java @@ -38,17 +38,20 @@ class SignatureTypeArgumentProcessor extends Signat private final TypeArgumentType typeArgumentType; private final JavaParameterizedTypeBuilder parameterizedType; private final JavaTypeFinisher typeFinisher; + private final DeclarationHandler declarationHandler; private JavaParameterizedTypeBuilder currentTypeArgument; SignatureTypeArgumentProcessor( TypeArgumentType typeArgumentType, JavaParameterizedTypeBuilder parameterizedType, - JavaTypeFinisher typeFinisher) { + JavaTypeFinisher typeFinisher, + DeclarationHandler declarationHandler) { super(ASM_API_VERSION); this.typeArgumentType = typeArgumentType; this.parameterizedType = parameterizedType; this.typeFinisher = typeFinisher; + this.declarationHandler = declarationHandler; } @Override @@ -57,6 +60,7 @@ public void visitClassType(String internalObjectName) { log.trace("Encountered {} for {}: Class type {}", typeArgumentType.description, parameterizedType.getTypeName(), type.getFullyQualifiedClassName()); currentTypeArgument = new JavaParameterizedTypeBuilder<>(type); typeArgumentType.addTypeArgumentToBuilder(parameterizedType, new NewJavaTypeCreationProcess<>(this.currentTypeArgument, typeFinisher)); + declarationHandler.onDeclaredGenericSignatureType(type.getFullyQualifiedClassName()); } @Override @@ -80,30 +84,31 @@ public void visitTypeVariable(String name) { @Override public SignatureVisitor visitTypeArgument(char wildcard) { - return SignatureTypeArgumentProcessor.create(wildcard, currentTypeArgument, JavaTypeFinisher.IDENTITY); + return SignatureTypeArgumentProcessor.create(wildcard, currentTypeArgument, JavaTypeFinisher.IDENTITY, declarationHandler); } @Override public SignatureVisitor visitArrayType() { - return new SignatureTypeArgumentProcessor<>(typeArgumentType, parameterizedType, typeFinisher.after(ARRAY_CREATOR)); + return new SignatureTypeArgumentProcessor<>(typeArgumentType, parameterizedType, typeFinisher.after(ARRAY_CREATOR), declarationHandler); } - static SignatureTypeArgumentProcessor create(char identifier, JavaParameterizedTypeBuilder parameterizedType) { - return create(identifier, parameterizedType, JavaTypeFinisher.IDENTITY); + static SignatureTypeArgumentProcessor create(char identifier, JavaParameterizedTypeBuilder parameterizedType, DeclarationHandler declarationHandler) { + return create(identifier, parameterizedType, JavaTypeFinisher.IDENTITY, declarationHandler); } static SignatureTypeArgumentProcessor create( char identifier, JavaParameterizedTypeBuilder parameterizedType, - JavaTypeFinisher typeFinisher) { + JavaTypeFinisher typeFinisher, + DeclarationHandler declarationHandler) { switch (identifier) { case INSTANCEOF: - return new SignatureTypeArgumentProcessor<>(PARAMETERIZED_TYPE, parameterizedType, typeFinisher); + return new SignatureTypeArgumentProcessor<>(PARAMETERIZED_TYPE, parameterizedType, typeFinisher, declarationHandler); case EXTENDS: - return new SignatureTypeArgumentProcessor<>(WILDCARD_WITH_UPPER_BOUND, parameterizedType, typeFinisher); + return new SignatureTypeArgumentProcessor<>(WILDCARD_WITH_UPPER_BOUND, parameterizedType, typeFinisher, declarationHandler); case SUPER: - return new SignatureTypeArgumentProcessor<>(WILDCARD_WITH_LOWER_BOUND, parameterizedType, typeFinisher); + return new SignatureTypeArgumentProcessor<>(WILDCARD_WITH_LOWER_BOUND, parameterizedType, typeFinisher, declarationHandler); default: throw new IllegalStateException(String.format("Cannot handle asm type argument identifier '%s'", identifier)); } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/SignatureTypeParameterProcessor.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/SignatureTypeParameterProcessor.java index 27ff53edbf..7c8d7ed0cb 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/SignatureTypeParameterProcessor.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/SignatureTypeParameterProcessor.java @@ -35,12 +35,14 @@ class SignatureTypeParameterProcessor extends Sign private static final Logger log = LoggerFactory.getLogger(SignatureTypeParameterProcessor.class); private final List> typeParameterBuilders = new ArrayList<>(); + private final DeclarationHandler declarationHandler; private JavaTypeParameterBuilder currentType; private JavaParameterizedTypeBuilder currentBound; - SignatureTypeParameterProcessor() { + SignatureTypeParameterProcessor(DeclarationHandler declarationHandler) { super(ASM_API_VERSION); + this.declarationHandler = declarationHandler; } List> getTypeParameterBuilders() { @@ -58,6 +60,7 @@ public void visitClassType(String internalObjectName) { JavaClassDescriptor type = JavaClassDescriptorImporter.createFromAsmObjectTypeName(internalObjectName); log.trace("Encountered upper bound for {}: Class type {}", currentType.getName(), type.getFullyQualifiedClassName()); currentBound = new JavaParameterizedTypeBuilder<>(type); + declarationHandler.onDeclaredGenericSignatureType(type.getFullyQualifiedClassName()); } @Override @@ -74,7 +77,7 @@ public void visitTypeVariable(String name) { @Override public SignatureVisitor visitTypeArgument(char wildcard) { - return SignatureTypeArgumentProcessor.create(wildcard, currentBound); + return SignatureTypeArgumentProcessor.create(wildcard, currentBound, declarationHandler); } @Override diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericClassesTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericClassesTest.java index 2334c5e3c5..14cfb17da8 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericClassesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericClassesTest.java @@ -7,18 +7,18 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Callable; +import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.base.Function; import com.tngtech.archunit.core.domain.JavaClass; -import com.tngtech.archunit.core.domain.JavaClasses; -import com.tngtech.archunit.testutil.ArchConfigurationRule; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThat; import static com.tngtech.archunit.testutil.Assertions.assertThatType; import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteClass.concreteClass; @@ -34,9 +34,6 @@ @RunWith(DataProviderRunner.class) public class ClassFileImporterGenericClassesTest { - @Rule - public final ArchConfigurationRule configurationRule = new ArchConfigurationRule().resolveAdditionalDependenciesFromClassPath(false); - @Test public void imports_empty_list_of_type_parameters_for_non_generic_class() { JavaClass javaClass = new ClassFileImporter().importClass(getClass()); @@ -50,9 +47,7 @@ public void imports_single_generic_type_parameter_of_class() { class ClassWithSingleTypeParameterWithoutBound { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithSingleTypeParameterWithoutBound.class, Object.class); - - JavaClass javaClass = classes.get(ClassWithSingleTypeParameterWithoutBound.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithoutBound.class); assertThatType(javaClass).hasOnlyTypeParameter("T").withBoundsMatching(Object.class); } @@ -74,9 +69,7 @@ public void imports_simple_class_bound_of_type_variable() { class ClassWithSingleTypeParameterWithSimpleClassBound { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithSingleTypeParameterWithSimpleClassBound.class, String.class); - - JavaClass javaClass = classes.get(ClassWithSingleTypeParameterWithSimpleClassBound.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithSimpleClassBound.class); assertThatType(javaClass).hasOnlyTypeParameter("T").withBoundsMatching(String.class); } @@ -87,10 +80,7 @@ public void imports_single_simple_class_bounds_of_multiple_type_variables() { class ClassWithThreeTypeParametersWithSimpleClassBounds { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithThreeTypeParametersWithSimpleClassBounds.class, - String.class, System.class, File.class); - - JavaClass javaClass = classes.get(ClassWithThreeTypeParametersWithSimpleClassBounds.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithThreeTypeParametersWithSimpleClassBounds.class); assertThatType(javaClass) .hasTypeParameters("A", "B", "C") @@ -105,9 +95,7 @@ public void imports_simple_interface_bound_of_single_type_variable() { class ClassWithSingleTypeParameterWithSimpleInterfaceBound { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithSingleTypeParameterWithSimpleInterfaceBound.class, Serializable.class); - - JavaClass javaClass = classes.get(ClassWithSingleTypeParameterWithSimpleInterfaceBound.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithSimpleInterfaceBound.class); assertThatType(javaClass).hasOnlyTypeParameter("T").withBoundsMatching(Serializable.class); } @@ -118,10 +106,7 @@ public void imports_multiple_simple_bounds_of_single_type_variable() { class ClassWithSingleTypeParameterWithMultipleSimpleClassAndInterfaceBounds { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithSingleTypeParameterWithMultipleSimpleClassAndInterfaceBounds.class, - String.class, Serializable.class, Runnable.class); - - JavaClass javaClass = classes.get(ClassWithSingleTypeParameterWithMultipleSimpleClassAndInterfaceBounds.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithMultipleSimpleClassAndInterfaceBounds.class); assertThatType(javaClass).hasOnlyTypeParameter("T").withBoundsMatching(String.class, Serializable.class, Runnable.class); } @@ -133,10 +118,7 @@ class ClassWithThreeTypeParametersWithMultipleSimpleClassAndInterfaceBounds< A extends String & Serializable, B extends System & Runnable, C extends File & Serializable & Closeable> { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithThreeTypeParametersWithMultipleSimpleClassAndInterfaceBounds.class, - String.class, Serializable.class, System.class, Runnable.class, File.class, Serializable.class, Closeable.class); - - JavaClass javaClass = classes.get(ClassWithThreeTypeParametersWithMultipleSimpleClassAndInterfaceBounds.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithThreeTypeParametersWithMultipleSimpleClassAndInterfaceBounds.class); assertThatType(javaClass) .hasTypeParameters("A", "B", "C") @@ -151,10 +133,7 @@ public void imports_single_class_bound_with_single_type_parameter_assigned_to_co class ClassWithSingleTypeParameterWithGenericClassBoundAssignedToConcreteClass> { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToConcreteClass.class, - ClassParameterWithSingleTypeParameter.class, String.class); - - JavaClass javaClass = classes.get(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToConcreteClass.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToConcreteClass.class); assertThatType(javaClass).hasOnlyTypeParameter("T") .withBoundsMatching(parameterizedType(ClassParameterWithSingleTypeParameter.class).withTypeArguments(String.class)); @@ -166,10 +145,7 @@ public void imports_single_class_bound_with_single_type_parameter_assigned_to_ar class ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType> { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType.class, - ClassParameterWithSingleTypeParameter.class, String.class); - - JavaClass javaClass = classes.get(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType.class); assertThatType(javaClass).hasOnlyTypeParameter("T") .withBoundsMatching(parameterizedType(ClassParameterWithSingleTypeParameter.class).withTypeArguments(String[].class)); @@ -181,10 +157,7 @@ public void imports_single_class_bound_with_single_type_parameter_assigned_to_pr class ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType> { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType.class, - ClassParameterWithSingleTypeParameter.class, String.class); - - JavaClass javaClass = classes.get(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType.class); assertThatType(javaClass).hasOnlyTypeParameter("T") .withBoundsMatching(parameterizedType(ClassParameterWithSingleTypeParameter.class).withTypeArguments(int[].class)); @@ -199,10 +172,7 @@ class ClassWithMultipleTypeParametersWithGenericClassOrInterfaceBoundsAssignedTo C extends InterfaceParameterWithSingleTypeParameter> { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithMultipleTypeParametersWithGenericClassOrInterfaceBoundsAssignedToConcreteTypes.class, - ClassParameterWithSingleTypeParameter.class, File.class, InterfaceParameterWithSingleTypeParameter.class, Serializable.class, String.class); - - JavaClass javaClass = classes.get(ClassWithMultipleTypeParametersWithGenericClassOrInterfaceBoundsAssignedToConcreteTypes.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithMultipleTypeParametersWithGenericClassOrInterfaceBoundsAssignedToConcreteTypes.class); assertThatType(javaClass).hasTypeParameters("A", "B", "C") .hasTypeParameter("A").withBoundsMatching(parameterizedType(ClassParameterWithSingleTypeParameter.class).withTypeArguments(File.class)) @@ -218,11 +188,7 @@ class ClassWithTwoTypeParametersWithMultipleGenericClassAndInterfaceBoundsAssign B extends Map & Iterable & Function> { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithTwoTypeParametersWithMultipleGenericClassAndInterfaceBoundsAssignedToConcreteTypes.class, - ClassParameterWithSingleTypeParameter.class, InterfaceParameterWithSingleTypeParameter.class, - Map.class, Iterable.class, Function.class, String.class, Serializable.class, File.class, Integer.class, Long.class); - - JavaClass javaClass = classes.get(ClassWithTwoTypeParametersWithMultipleGenericClassAndInterfaceBoundsAssignedToConcreteTypes.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithTwoTypeParametersWithMultipleGenericClassAndInterfaceBoundsAssignedToConcreteTypes.class); assertThatType(javaClass).hasTypeParameters("A", "B") .hasTypeParameter("A") @@ -242,9 +208,7 @@ public void imports_single_type_bound_with_unbound_wildcard() { class ClassWithSingleTypeParameterBoundByTypeWithUnboundWildcard> { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithSingleTypeParameterBoundByTypeWithUnboundWildcard.class, List.class, String.class); - - JavaClass javaClass = classes.get(ClassWithSingleTypeParameterBoundByTypeWithUnboundWildcard.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterBoundByTypeWithUnboundWildcard.class); assertThatType(javaClass) .hasTypeParameter("T").withBoundsMatching(parameterizedType(List.class).withWildcardTypeParameter()); @@ -268,9 +232,7 @@ class ClassWithSingleTypeParameterBoundByTypeWithWildcardWithUpperInterfaceBound @Test @UseDataProvider public void test_imports_single_type_bound_with_upper_bound_wildcard(Class classWithWildcard, Class expectedUpperBound) { - JavaClasses classes = new ClassFileImporter().importClasses(classWithWildcard, List.class, expectedUpperBound); - - JavaClass javaClass = classes.get(classWithWildcard); + JavaClass javaClass = new ClassFileImporter().importClass(classWithWildcard); assertThatType(javaClass) .hasTypeParameter("T").withBoundsMatching(parameterizedType(List.class).withWildcardTypeParameterWithUpperBound(expectedUpperBound)); @@ -294,9 +256,7 @@ class ClassWithSingleTypeParameterBoundByTypeWithWildcardWithLowerInterfaceBound @Test @UseDataProvider public void test_imports_single_type_bound_with_lower_bound_wildcard(Class classWithWildcard, Class expectedLowerBound) { - JavaClasses classes = new ClassFileImporter().importClasses(classWithWildcard, List.class, expectedLowerBound); - - JavaClass javaClass = classes.get(classWithWildcard); + JavaClass javaClass = new ClassFileImporter().importClass(classWithWildcard); assertThatType(javaClass) .hasTypeParameter("T").withBoundsMatching(parameterizedType(List.class).withWildcardTypeParameterWithLowerBound(expectedLowerBound)); @@ -308,10 +268,7 @@ public void imports_multiple_type_bounds_with_multiple_wildcards_with_various_bo class ClassWithMultipleTypeParametersBoundByTypesWithDifferentBounds, B extends Reference & Map> { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithMultipleTypeParametersBoundByTypesWithDifferentBounds.class, - Map.class, Serializable.class, File.class, Reference.class, String.class); - - JavaClass javaClass = classes.get(ClassWithMultipleTypeParametersBoundByTypesWithDifferentBounds.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithMultipleTypeParametersBoundByTypesWithDifferentBounds.class); assertThatType(javaClass).hasTypeParameters("A", "B") .hasTypeParameter("A") @@ -340,9 +297,7 @@ public void references_type_variable_bound() { class ClassWithTypeParameterWithTypeVariableBound { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithTypeParameterWithTypeVariableBound.class, String.class); - - JavaClass javaClass = classes.get(ClassWithTypeParameterWithTypeVariableBound.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithTypeParameterWithTypeVariableBound.class); assertThatType(javaClass).hasTypeParameters("U", "T", "V") .hasTypeParameter("U").withBoundsMatching(typeVariable("T").withUpperBounds(String.class)) @@ -362,13 +317,7 @@ class AndEvenMoreInner { } } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.class, - ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.class, - ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.EvenMoreInnerDeclaringOwn.class, - ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.EvenMoreInnerDeclaringOwn.AndEvenMoreInner.class, - String.class); - - JavaClass javaClass = classes.get(ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.EvenMoreInnerDeclaringOwn.AndEvenMoreInner.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.EvenMoreInnerDeclaringOwn.AndEvenMoreInner.class); assertThatType(javaClass).hasTypeParameters("MOST_INNER1", "MOST_INNER2") .hasTypeParameter("MOST_INNER1") @@ -392,12 +341,7 @@ class EvenMoreInner { } } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithTypeParameterBoundByInnerClass.class, - ClassWithTypeParameterBoundByInnerClass.SomeInner.class, - ClassWithTypeParameterBoundByInnerClass.SomeInner.EvenMoreInner.class, - String.class); - - JavaClass javaClass = classes.get(ClassWithTypeParameterBoundByInnerClass.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithTypeParameterBoundByInnerClass.class); assertThatType(javaClass).hasTypeParameters("T", "U") .hasTypeParameter("T") @@ -420,10 +364,14 @@ class AndEvenMoreInner { } } - JavaClasses classes = new ClassFileImporter().importClasses( - ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.EvenMoreInnerDeclaringOwn.AndEvenMoreInner.class); - - JavaClass javaClass = classes.get(ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.EvenMoreInnerDeclaringOwn.AndEvenMoreInner.class); + JavaClass javaClass = resetConfigurationAround(new Callable() { + @Override + public JavaClass call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return new ClassFileImporter().importClass( + ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.EvenMoreInnerDeclaringOwn.AndEvenMoreInner.class); + } + }); assertThatType(javaClass).hasTypeParameters("MOST_INNER1", "MOST_INNER2") .hasTypeParameter("MOST_INNER1") @@ -447,12 +395,8 @@ class Level5 { } Class innermostClass = Class.forName(Level1.class.getName() + "$1Level3$1Level5"); - JavaClasses classes = new ClassFileImporter().importClasses( - Class.forName(Level1.class.getName() + "$1Level3"), - innermostClass, - Level1.class, String.class); - JavaClass javaClass = classes.get(innermostClass); + JavaClass javaClass = new ClassFileImporter().importClass(innermostClass); assertThatType(javaClass).hasTypeParameters("T51", "T52") .hasTypeParameter("T51") @@ -471,9 +415,7 @@ public void imports_wild_cards_bound_by_type_variables() { class ClassWithWildcardWithTypeVariableBounds, V extends List> { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithWildcardWithTypeVariableBounds.class, List.class, String.class); - - JavaClass javaClass = classes.get(ClassWithWildcardWithTypeVariableBounds.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithWildcardWithTypeVariableBounds.class); assertThatType(javaClass).hasTypeParameters("T", "U", "V") .hasTypeParameter("U") @@ -496,13 +438,7 @@ class MoreInner, MOST_INNER2 extends List< } } - JavaClasses classes = new ClassFileImporter().importClasses( - ClassWithWildcardWithTypeVariableBounds.class, - ClassWithWildcardWithTypeVariableBounds.Inner.class, - ClassWithWildcardWithTypeVariableBounds.Inner.MoreInner.class, - List.class, String.class); - - JavaClass javaClass = classes.get(ClassWithWildcardWithTypeVariableBounds.Inner.MoreInner.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithWildcardWithTypeVariableBounds.Inner.MoreInner.class); assertThatType(javaClass).hasTypeParameters("MOST_INNER1", "MOST_INNER2") .hasTypeParameter("MOST_INNER1") @@ -527,10 +463,15 @@ class MoreInner, MOST_INNER2 extends List< } } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithWildcardWithTypeVariableBounds.Inner.MoreInner.class, - List.class, String.class); - - JavaClass javaClass = classes.get(ClassWithWildcardWithTypeVariableBounds.Inner.MoreInner.class); + JavaClass javaClass = resetConfigurationAround(new Callable() { + @Override + public JavaClass call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return new ClassFileImporter() + .importClasses(ClassWithWildcardWithTypeVariableBounds.Inner.MoreInner.class, List.class) + .get(ClassWithWildcardWithTypeVariableBounds.Inner.MoreInner.class); + } + }); assertThatType(javaClass).hasTypeParameters("MOST_INNER1", "MOST_INNER2") .hasTypeParameter("MOST_INNER1") @@ -558,10 +499,7 @@ class ClassWithComplexTypeParameters< RAW extends List> { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithComplexTypeParameters.class, - List.class, Serializable.class, Comparable.class, Map.class, Map.Entry.class, String.class, Set.class, Iterable.class, Object.class); - - JavaClass javaClass = classes.get(ClassWithComplexTypeParameters.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithComplexTypeParameters.class); assertThatType(javaClass) .hasTypeParameter("A") @@ -615,10 +553,7 @@ class ClassWithComplexTypeParametersWithConcreteArrayBounds< > { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithComplexTypeParametersWithConcreteArrayBounds.class, - List.class, Serializable.class, Map.class, String.class); - - JavaClass javaClass = classes.get(ClassWithComplexTypeParametersWithConcreteArrayBounds.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithComplexTypeParametersWithConcreteArrayBounds.class); assertThatType(javaClass) .hasTypeParameter("A") @@ -650,10 +585,7 @@ class ClassWithTypeParameterWithParameterizedArrayBounds< > { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithTypeParameterWithParameterizedArrayBounds.class, - ClassWithThreeTypeParameters.class, List.class, String.class); - - JavaClass javaClass = classes.get(ClassWithTypeParameterWithParameterizedArrayBounds.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithTypeParameterWithParameterizedArrayBounds.class); assertThatType(javaClass) .hasTypeParameter("T") @@ -682,10 +614,7 @@ class ClassWithComplexTypeParametersWithGenericArrayBounds< > { } - JavaClasses classes = new ClassFileImporter().importClasses(ClassWithComplexTypeParametersWithGenericArrayBounds.class, - List.class, Serializable.class, Map.class, String.class); - - JavaClass javaClass = classes.get(ClassWithComplexTypeParametersWithGenericArrayBounds.class); + JavaClass javaClass = new ClassFileImporter().importClass(ClassWithComplexTypeParametersWithGenericArrayBounds.class); assertThatType(javaClass) .hasTypeParameter("A") diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericCodeUnitParameterTypesTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericCodeUnitParameterTypesTest.java index d20ff047b4..20c02b1925 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericCodeUnitParameterTypesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericCodeUnitParameterTypesTest.java @@ -8,22 +8,20 @@ import java.util.Set; import java.util.concurrent.Callable; -import com.google.common.collect.FluentIterable; import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.domain.JavaCodeUnit; import com.tngtech.archunit.core.domain.JavaConstructor; import com.tngtech.archunit.core.domain.JavaType; import com.tngtech.archunit.core.domain.JavaTypeVariable; -import com.tngtech.archunit.testutil.ArchConfigurationRule; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import static com.google.common.collect.Iterables.getOnlyElement; +import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThat; import static com.tngtech.archunit.testutil.Assertions.assertThatType; import static com.tngtech.archunit.testutil.Assertions.assertThatTypes; @@ -41,9 +39,6 @@ @RunWith(DataProviderRunner.class) public class ClassFileImporterGenericCodeUnitParameterTypesTest { - @Rule - public final ArchConfigurationRule configurationRule = new ArchConfigurationRule().resolveAdditionalDependenciesFromClassPath(false); - @DataProvider public static Object[][] data_imports_non_generic_code_unit_parameter_type() { class NonGenericParameterType { @@ -60,8 +55,8 @@ void method(NonGenericParameterType param) { } Object[][] testCases = testCasesFromSameGenericSignatureOnConstructorAndMethod( NoGenericSignatureOnConstructor.class, - NoGenericSignatureOnMethod.class, - NonGenericParameterType.class); + NoGenericSignatureOnMethod.class + ); return $$( $(testCases[0][0], NonGenericParameterType.class), $(testCases[1][0], NonGenericParameterType.class) @@ -89,8 +84,8 @@ abstract class GenericSignatureOnMethod { } return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - Object.class); + GenericSignatureOnMethod.class + ); } @Test @@ -116,8 +111,8 @@ void method(ClassParameterWithSingleTypeParameter param) { } return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - ClassParameterWithSingleTypeParameter.class, String.class); + GenericSignatureOnMethod.class + ); } @Test @@ -139,10 +134,7 @@ public LocalClassThatWillHaveEnclosingClassAsFirstRawConstructorParameter(ClassP } JavaConstructor constructor = new ClassFileImporter() - .importClasses( - LocalClassThatWillHaveEnclosingClassAsFirstRawConstructorParameter.class, - getClass(), ClassParameterWithSingleTypeParameter.class, String.class) - .get(LocalClassThatWillHaveEnclosingClassAsFirstRawConstructorParameter.class) + .importClass(LocalClassThatWillHaveEnclosingClassAsFirstRawConstructorParameter.class) .getConstructor(getClass(), ClassParameterWithSingleTypeParameter.class); assertThatTypes(constructor.getRawParameterTypes()).matchExactly(getClass(), ClassParameterWithSingleTypeParameter.class); @@ -167,8 +159,8 @@ void method(ClassParameterWithSingleTypeParameter param) { return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - ClassParameterWithSingleTypeParameter.class); + GenericSignatureOnMethod.class + ); } @Test @@ -194,8 +186,8 @@ void method(ClassParameterWithSingleTypeParameter param) { return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - ClassParameterWithSingleTypeParameter.class, String.class); + GenericSignatureOnMethod.class + ); } @Test @@ -223,8 +215,8 @@ void method(ClassParameterWithSingleTypeParameter param) { return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - ClassParameterWithSingleTypeParameter.class); + GenericSignatureOnMethod.class + ); } @Test @@ -252,8 +244,8 @@ void method(ClassParameterWithThreeTypeParameters pa return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - ClassParameterWithThreeTypeParameters.class, Serializable.class, File.class, String.class); + GenericSignatureOnMethod.class + ); } @Test @@ -281,8 +273,8 @@ void method(ClassParameterWithSingleTypeParameter param) { return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - ClassParameterWithSingleTypeParameter.class); + GenericSignatureOnMethod.class + ); } @Test @@ -379,8 +370,8 @@ void method(ClassParameterWithSingleTypeParameter param) { return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - ClassParameterWithSingleTypeParameter.class); + GenericSignatureOnMethod.class + ); } @Test @@ -541,8 +532,8 @@ void method(ClassParameterWithSingleTypeParameter param) { return testCasesFromSameGenericSignatureOnConstructorAndMethod( OuterWithTypeParameter.SomeInner.GenericSignatureOnConstructor.class, - OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class, - OuterWithTypeParameter.class, OuterWithTypeParameter.SomeInner.class, ClassParameterWithSingleTypeParameter.class, String.class); + OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class + ); } @Test @@ -636,10 +627,20 @@ void method(ClassParameterWithSingleTypeParameter param) { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod( - OuterWithTypeParameter.SomeInner.GenericSignatureOnConstructor.class, - OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class, - ClassParameterWithSingleTypeParameter.class, String.class); + JavaClasses classes = resetConfigurationAround(new Callable() { + @Override + public JavaClasses call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return new ClassFileImporter().importClasses( + OuterWithTypeParameter.SomeInner.GenericSignatureOnConstructor.class, + OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class); + } + }); + + return testForEach( + getOnlyElement(classes.get(OuterWithTypeParameter.SomeInner.GenericSignatureOnConstructor.class).getConstructors()), + getOnlyElement(classes.get(OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class).getMethods()) + ); } @Test @@ -670,8 +671,8 @@ void method(T4 param) { return testCasesFromSameGenericSignatureOnConstructorAndMethod( Class.forName(Level1.class.getName() + "$1GenericSignatureOnConstructor"), - Class.forName(Level1.class.getName() + "$1GenericSignatureOnMethod"), - Level1.class, ClassParameterWithSingleTypeParameter.class, String.class); + Class.forName(Level1.class.getName() + "$1GenericSignatureOnMethod") + ); } @Test @@ -706,8 +707,8 @@ void method(ClassParameterWithTwoTypeParameters< return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - ClassParameterWithTwoTypeParameters.class, ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class); + GenericSignatureOnMethod.class + ); } @Test @@ -748,9 +749,8 @@ void method(ClassParameterWithTwoTypeParameters< return testCasesFromSameGenericSignatureOnConstructorAndMethod( OuterWithTypeParameter.SomeInner.GenericSignatureOnConstructor.class, - OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class, - OuterWithTypeParameter.class, OuterWithTypeParameter.SomeInner.class, ClassParameterWithTwoTypeParameters.class, - ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class); + OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class + ); } @Test @@ -789,10 +789,21 @@ void method(ClassParameterWithTwoTypeParameters< } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod( - OuterWithTypeParameter.SomeInner.GenericSignatureOnConstructor.class, - OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class, - ClassParameterWithTwoTypeParameters.class, ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class); + JavaClasses classes = resetConfigurationAround(new Callable() { + @Override + public JavaClasses call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return new ClassFileImporter().importClasses( + OuterWithTypeParameter.SomeInner.GenericSignatureOnConstructor.class, + OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class, + ClassParameterWithSingleTypeParameter.class); + } + }); + + return testForEach( + getOnlyElement(classes.get(OuterWithTypeParameter.SomeInner.GenericSignatureOnConstructor.class).getConstructors()), + getOnlyElement(classes.get(OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class).getMethods()) + ); } @Test @@ -843,9 +854,8 @@ void method(ClassParameterWithThreeTypeParameters< return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - ClassParameterWithThreeTypeParameters.class, String.class, Serializable.class, Cloneable.class, List.class, - Map.class, Map.Entry.class, Set.class, Iterable.class, Comparable.class); + GenericSignatureOnMethod.class + ); } @Test @@ -903,8 +913,8 @@ void method(List, Serializable>>> return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - List.class, Serializable.class, Map.class, String.class); + GenericSignatureOnMethod.class + ); } @Test @@ -949,8 +959,8 @@ void method(ClassParameterWithThreeTypeParameters< return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - ClassParameterWithThreeTypeParameters.class, List.class, Serializable.class, Map.class, String.class); + GenericSignatureOnMethod.class + ); } @Test @@ -985,8 +995,8 @@ void method(ClassParameterWithThreeTypeParameters[], List return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - ClassParameterWithThreeTypeParameters.class, List.class, String.class); + GenericSignatureOnMethod.class + ); } @Test @@ -1021,8 +1031,8 @@ void method(X[] first, Y[][] second) { return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - Serializable.class, String.class); + GenericSignatureOnMethod.class + ); } @Test @@ -1060,8 +1070,8 @@ void method(ClassParameterWithFourTypeParameters< return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - ClassParameterWithFourTypeParameters.class, List.class, Serializable.class, Map.class, String.class); + GenericSignatureOnMethod.class + ); } @Test @@ -1130,9 +1140,8 @@ void method( return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class, Cloneable.class, - List.class, Map.class, Map.Entry.class, Set.class, Iterable.class, Comparable.class); + GenericSignatureOnMethod.class + ); } @Test @@ -1176,18 +1185,8 @@ public void test_imports_multiple_generic_code_unit_parameter_types(JavaCodeUnit // @formatter:on } - private static Object[][] testCasesFromSameGenericSignatureOnConstructorAndMethod( - Class genericSignatureOnConstructor, - Class genericSignatureOnMethod, - Class... additionalImports) { - final List> toImport = FluentIterable.from(additionalImports).append(genericSignatureOnConstructor).append(genericSignatureOnMethod).toList(); - JavaClasses classes = ArchConfigurationRule.resetConfigurationAround(new Callable() { - @Override - public JavaClasses call() { - ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter().importClasses(toImport); - } - }); + private static Object[][] testCasesFromSameGenericSignatureOnConstructorAndMethod(Class genericSignatureOnConstructor, Class genericSignatureOnMethod) { + JavaClasses classes = new ClassFileImporter().importClasses(genericSignatureOnConstructor, genericSignatureOnMethod); return testForEach( getOnlyElement(classes.get(genericSignatureOnConstructor).getConstructors()), diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericFieldTypesTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericFieldTypesTest.java index a38e5d42a4..0411c75e32 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericFieldTypesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericFieldTypesTest.java @@ -6,17 +6,17 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Callable; +import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.core.domain.JavaClass; -import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.domain.JavaType; import com.tngtech.archunit.core.domain.JavaTypeVariable; -import com.tngtech.archunit.testutil.ArchConfigurationRule; import com.tngtech.java.junit.dataprovider.DataProviderRunner; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThatType; import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteClass.concreteClass; import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteGenericArray.genericArray; @@ -29,9 +29,6 @@ @RunWith(DataProviderRunner.class) public class ClassFileImporterGenericFieldTypesTest { - @Rule - public final ArchConfigurationRule configurationRule = new ArchConfigurationRule().resolveAdditionalDependenciesFromClassPath(false); - @Test public void imports_non_generic_field_type() { class NonGenericFieldType { @@ -56,8 +53,7 @@ class SomeClass { GenericFieldType field; } - JavaType genericFieldType = new ClassFileImporter().importClasses(SomeClass.class, String.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type") .hasErasure(GenericFieldType.class) @@ -74,8 +70,7 @@ class SomeClass { GenericFieldType field; } - JavaType rawGenericFieldType = new ClassFileImporter().importClasses(SomeClass.class) - .get(SomeClass.class).getField("field").getType(); + JavaType rawGenericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(rawGenericFieldType).as("raw generic field type").matches(GenericFieldType.class); } @@ -90,8 +85,7 @@ class SomeClass { GenericFieldType field; } - JavaType genericFieldType = new ClassFileImporter().importClasses(SomeClass.class, String.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type") .hasErasure(GenericFieldType.class) @@ -108,8 +102,7 @@ class SomeClass { GenericFieldType field; } - JavaType genericFieldType = new ClassFileImporter().importClasses(SomeClass.class, int.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type") .hasErasure(GenericFieldType.class) @@ -126,8 +119,7 @@ class SomeClass { GenericFieldType field; } - JavaType genericFieldType = new ClassFileImporter().importClasses(SomeClass.class, String.class, Serializable.class, File.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type") .hasErasure(GenericFieldType.class) @@ -144,8 +136,7 @@ class SomeClass { GenericFieldType> field; } - JavaType genericFieldType = new ClassFileImporter().importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class, String.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -166,11 +157,7 @@ class SomeClass { InterfaceParameterWithSingleTypeParameter> field; } - JavaType genericFieldType = new ClassFileImporter() - .importClasses( - SomeClass.class, ClassParameterWithSingleTypeParameter.class, InterfaceParameterWithSingleTypeParameter.class, - File.class, Serializable.class, String.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -192,8 +179,7 @@ class SomeClass { GenericFieldType field; } - JavaType genericFieldType = new ClassFileImporter().importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments(wildcardType()); } @@ -208,8 +194,7 @@ class SomeClass { GenericFieldType> field; } - JavaType genericFieldType = new ClassFileImporter().importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -229,8 +214,7 @@ class SomeClass { ClassParameterWithSingleTypeParameter> field; } - JavaType genericFieldType = new ClassFileImporter().importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class, String.class, File.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -252,11 +236,7 @@ class SomeClass { ClassParameterWithSingleTypeParameter>> field; } - JavaType genericFieldType = new ClassFileImporter() - .importClasses( - SomeClass.class, ClassParameterWithSingleTypeParameter.class, - Map.class, Serializable.class, File.class, Reference.class, String.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -277,8 +257,7 @@ class SomeClass { T field; } - JavaType genericFieldType = new ClassFileImporter().importClasses(SomeClass.class, String.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type") .isInstanceOf(JavaTypeVariable.class) @@ -295,8 +274,7 @@ class SomeClass { GenericFieldType field; } - JavaType genericFieldType = new ClassFileImporter().importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments(typeVariable("OF_CLASS")); } @@ -311,8 +289,7 @@ class SomeClass { GenericFieldType> field; } - JavaType genericFieldType = new ClassFileImporter().importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -330,8 +307,7 @@ class SomeClass { GenericFieldType> field; } - JavaType genericFieldType = new ClassFileImporter().importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class, String.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -354,13 +330,8 @@ class SomeClass { } } - JavaType genericFieldType = new ClassFileImporter() - .importClasses( - OuterWithTypeParameter.class, - OuterWithTypeParameter.SomeInner.class, - OuterWithTypeParameter.SomeInner.SomeClass.class, - String.class) - .get(OuterWithTypeParameter.SomeInner.SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.SomeClass.class) + .getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( typeVariable("OUTER").withUpperBounds(String.class) @@ -381,9 +352,14 @@ class SomeClass { } } - JavaType genericFieldType = new ClassFileImporter() - .importClasses(OuterWithTypeParameter.SomeInner.SomeClass.class, String.class) - .get(OuterWithTypeParameter.SomeInner.SomeClass.class).getField("field").getType(); + JavaType genericFieldType = resetConfigurationAround(new Callable() { + @Override + public JavaType call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.SomeClass.class) + .getField("field").getType(); + } + }); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( typeVariable("OUTER").withoutUpperBounds() @@ -403,8 +379,7 @@ class Level3 { Class innermostClass = Class.forName(Level1.class.getName() + "$1Level3"); JavaType genericFieldType = new ClassFileImporter() - .importClasses(innermostClass, Level1.class, String.class) - .get(innermostClass).getField("field").getType(); + .importClass(innermostClass).getField("field").getType(); assertThatType(genericFieldType).as("generic field type") .matches( @@ -426,8 +401,7 @@ class SomeClass { } JavaType genericFieldType = new ClassFileImporter() - .importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) - .get(SomeClass.class).getField("field").getType(); + .importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -456,12 +430,7 @@ class SomeClass { } JavaType genericFieldType = new ClassFileImporter() - .importClasses( - OuterWithTypeParameter.class, - OuterWithTypeParameter.SomeInner.class, - OuterWithTypeParameter.SomeInner.SomeClass.class, - ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) - .get(OuterWithTypeParameter.SomeInner.SomeClass.class).getField("field").getType(); + .importClass(OuterWithTypeParameter.SomeInner.SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -490,11 +459,15 @@ class SomeClass { } } - JavaType genericFieldType = new ClassFileImporter() - .importClasses( - OuterWithTypeParameter.SomeInner.SomeClass.class, - ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) - .get(OuterWithTypeParameter.SomeInner.SomeClass.class).getField("field").getType(); + JavaType genericFieldType = resetConfigurationAround(new Callable() { + @Override + public JavaType call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return new ClassFileImporter() + .importClasses(OuterWithTypeParameter.SomeInner.SomeClass.class, ClassParameterWithSingleTypeParameter.class) + .get(OuterWithTypeParameter.SomeInner.SomeClass.class).getField("field").getType(); + } + }); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -525,10 +498,7 @@ class SomeClass>> field; } - JavaType genericFieldType = new ClassFileImporter() - .importClasses(SomeClass.class, String.class, Serializable.class, Cloneable.class, - List.class, Map.class, Map.Entry.class, Set.class, Iterable.class, Comparable.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); // @formatter:off assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( @@ -575,10 +545,7 @@ class SomeClass { GenericFieldType, Serializable>>>[] field; } - JavaClasses classes = new ClassFileImporter().importClasses(SomeClass.class, - List.class, Serializable.class, Map.class, String.class); - - JavaType genericFieldType = classes.get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).matches( genericArray( @@ -609,10 +576,7 @@ class SomeClass { Map, Serializable[][]>>> field; } - JavaClasses classes = new ClassFileImporter().importClasses(SomeClass.class, - List.class, Serializable.class, Map.class, String.class); - - JavaType genericFieldType = classes.get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments(Serializable[].class), @@ -637,8 +601,7 @@ class SomeClass { GenericFieldType[], List[][], List[][][]> field; } - JavaType genericFieldType = new ClassFileImporter().importClasses(SomeClass.class, List.class, String.class) - .get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).hasActualTypeArguments( genericArray(parameterizedTypeArrayName(List.class, String.class, 1)).withComponentType( @@ -660,8 +623,7 @@ class SomeClass { T[][] field2Dim; } - JavaClass javaClass = new ClassFileImporter().importClasses(SomeClass.class, String.class) - .get(SomeClass.class); + JavaClass javaClass = new ClassFileImporter().importClass(SomeClass.class); assertThatType(javaClass.getField("field").getType()).as("generic field type") .hasErasure(String[].class) @@ -688,10 +650,7 @@ class SomeClass { Map, X[][]>>> field; } - JavaClasses classes = new ClassFileImporter().importClasses(SomeClass.class, - List.class, Serializable.class, Map.class, String.class); - - JavaType genericFieldType = classes.get(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments( diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericInterfacesTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericInterfacesTest.java index 079109ee4a..df793083a6 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericInterfacesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericInterfacesTest.java @@ -7,20 +7,21 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Callable; +import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaType; import com.tngtech.archunit.core.importer.ClassFileImporterGenericInterfacesTest.Outer.SomeNestedInterface; import com.tngtech.archunit.core.importer.ClassFileImporterGenericInterfacesTest.Outer.SomeNestedInterface.SomeDeeplyNestedInterface; -import com.tngtech.archunit.testutil.ArchConfigurationRule; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import static com.google.common.collect.Iterables.getOnlyElement; +import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThatType; import static com.tngtech.archunit.testutil.Assertions.assertThatTypes; import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteClass.concreteClass; @@ -35,16 +36,12 @@ @RunWith(DataProviderRunner.class) public class ClassFileImporterGenericInterfacesTest { - @Rule - public final ArchConfigurationRule configurationRule = new ArchConfigurationRule().resolveAdditionalDependenciesFromClassPath(false); - @Test public void imports_non_generic_interface() { class Child implements SomeInterface { } - Set genericInterfaces = new ClassFileImporter().importClasses(Child.class, SomeInterface.class) - .get(Child.class).getInterfaces(); + Set genericInterfaces = new ClassFileImporter().importClass(Child.class).getInterfaces(); assertThatTypes(genericInterfaces).as("generic interfaces").matchExactly(SomeInterface.class); } @@ -54,9 +51,7 @@ public void imports_generic_interface_with_one_type_argument() { class Child implements InterfaceWithOneTypeParameter { } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter().importClasses(Child.class, InterfaceWithOneTypeParameter.class, String.class) - .get(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface") .hasErasure(InterfaceWithOneTypeParameter.class) @@ -69,9 +64,7 @@ public void imports_raw_generic_superclass_as_JavaClass_instead_of_JavaParameter class Child implements InterfaceWithOneTypeParameter { } - JavaType rawGenericInterface = getOnlyElement( - new ClassFileImporter().importClasses(Child.class, InterfaceWithOneTypeParameter.class) - .get(Child.class).getInterfaces()); + JavaType rawGenericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(rawGenericInterface).as("raw generic interface").matches(InterfaceWithOneTypeParameter.class); } @@ -82,8 +75,7 @@ class Child implements InterfaceWithOneTypeParameter { } JavaType genericInterface = getOnlyElement( - new ClassFileImporter().importClasses(Child.class, InterfaceWithOneTypeParameter.class, String.class) - .get(Child.class).getInterfaces()); + new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface") .hasErasure(InterfaceWithOneTypeParameter.class) @@ -95,9 +87,7 @@ public void imports_generic_interface_with_primitive_array_type_argument() { class Child implements InterfaceWithOneTypeParameter { } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter().importClasses(Child.class, InterfaceWithOneTypeParameter.class, int.class) - .get(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface") .hasErasure(InterfaceWithOneTypeParameter.class) @@ -110,10 +100,7 @@ public void imports_generic_interface_with_multiple_type_arguments() { class Child implements InterfaceWithThreeTypeParameters { } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses(Child.class, InterfaceWithThreeTypeParameters.class, String.class, Serializable.class, File.class) - .get(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface") .hasErasure(InterfaceWithThreeTypeParameters.class) @@ -125,10 +112,7 @@ public void imports_generic_interface_with_single_actual_type_argument_parameter class Child implements InterfaceWithOneTypeParameter> { } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses(Child.class, InterfaceWithOneTypeParameter.class, ClassParameterWithSingleTypeParameter.class, String.class) - .get(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -144,11 +128,7 @@ class Child implements InterfaceWithThreeTypeParameters< InterfaceWithOneTypeParameter> { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter() - .importClasses( - Child.class, ClassParameterWithSingleTypeParameter.class, InterfaceWithThreeTypeParameters.class, - InterfaceWithOneTypeParameter.class, File.class, Serializable.class, String.class) - .get(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -165,10 +145,7 @@ public void imports_generic_interface_with_single_actual_type_argument_parameter class Child implements InterfaceWithOneTypeParameter> { } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses(Child.class, InterfaceWithOneTypeParameter.class, ClassParameterWithSingleTypeParameter.class) - .get(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -183,10 +160,7 @@ class Child implements InterfaceWithTwoTypeParameters< ClassParameterWithSingleTypeParameter> { } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses(Child.class, InterfaceWithTwoTypeParameters.class, ClassParameterWithSingleTypeParameter.class, String.class, File.class) - .get(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -203,12 +177,7 @@ class Child implements InterfaceWithTwoTypeParameters< ClassParameterWithSingleTypeParameter>> { } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses( - Child.class, ClassParameterWithSingleTypeParameter.class, - Map.class, Serializable.class, File.class, Reference.class, String.class) - .get(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -227,8 +196,7 @@ public void imports_generic_interface_parameterized_with_type_variable() { class Child implements InterfaceWithOneTypeParameter { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClasses(Child.class, InterfaceWithOneTypeParameter.class) - .get(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments(typeVariable("SUB")); } @@ -238,10 +206,7 @@ public void imports_generic_interface_with_actual_type_argument_parameterized_wi class Child implements InterfaceWithOneTypeParameter> { } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses(Child.class, InterfaceWithOneTypeParameter.class, ClassParameterWithSingleTypeParameter.class) - .get(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -254,10 +219,7 @@ public void references_type_variable_assigned_to_actual_type_argument_of_generic class Child implements InterfaceWithOneTypeParameter> { } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses(Child.class, InterfaceWithOneTypeParameter.class, ClassParameterWithSingleTypeParameter.class, String.class) - .get(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -275,15 +237,7 @@ class Child implements InterfaceWithOneTypeParameter { } } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses( - OuterWithTypeParameter.class, - OuterWithTypeParameter.SomeInner.class, - OuterWithTypeParameter.SomeInner.Child.class, - InterfaceWithOneTypeParameter.class, - String.class) - .get(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( typeVariable("OUTER").withUpperBounds(String.class) @@ -300,10 +254,13 @@ class Child implements InterfaceWithOneTypeParameter { } } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses(OuterWithTypeParameter.SomeInner.Child.class, InterfaceWithOneTypeParameter.class, String.class) - .get(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); + JavaType genericInterface = resetConfigurationAround(new Callable() { + @Override + public JavaType call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return getOnlyElement(new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); + } + }); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( typeVariable("OUTER").withoutUpperBounds() @@ -321,10 +278,7 @@ class Level3 implements InterfaceWithOneTypeParameter { } Class innermostClass = Class.forName(Level1.class.getName() + "$1Level3"); - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses(innermostClass, Level1.class, String.class) - .get(innermostClass).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(innermostClass).getInterfaces()); assertThatType(genericInterface).as("generic interface") .hasActualTypeArguments( @@ -340,10 +294,7 @@ class Child implements Interf ClassParameterWithSingleTypeParameter> { } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses(Child.class, InterfaceWithTwoTypeParameters.class, ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) - .get(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -367,14 +318,7 @@ class Child implements InterfaceWithTwoTypeParameters< } } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses( - OuterWithTypeParameter.class, - OuterWithTypeParameter.SomeInner.class, - OuterWithTypeParameter.SomeInner.Child.class, - InterfaceWithTwoTypeParameters.class, ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) - .get(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -398,12 +342,15 @@ class Child implements InterfaceWithTwoTypeParameters< } } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses( - OuterWithTypeParameter.SomeInner.Child.class, - ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) + JavaType genericInterface = resetConfigurationAround(new Callable() { + @Override + public JavaType call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return getOnlyElement(new ClassFileImporter() + .importClasses(OuterWithTypeParameter.SomeInner.Child.class, ClassParameterWithSingleTypeParameter.class) .get(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); + } + }); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -452,11 +399,7 @@ public static Object[][] data_imports_complex_type_with_multiple_nested_actual_t @Test @UseDataProvider public void test_imports_complex_type_with_multiple_nested_actual_type_arguments_of_generic_interface_with_self_referencing_type_definitions(Class testInput) { - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses(testInput, String.class, Serializable.class, Cloneable.class, - List.class, Map.class, Map.Entry.class, Set.class, Iterable.class, Comparable.class) - .get(testInput).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(testInput).getInterfaces()); // @formatter:off assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( @@ -518,9 +461,7 @@ public static Object[][] data_imports_complex_type_with_multiple_nested_actual_t @Test @UseDataProvider public void test_imports_complex_type_with_multiple_nested_actual_type_arguments_of_generic_interface_with_concrete_array_bounds(Class testInput) { - JavaType genericInterface = getOnlyElement( - new ClassFileImporter().importClasses(testInput, List.class, Serializable.class, Map.class, String.class) - .get(testInput).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(testInput).getInterfaces()); assertThatType(genericInterface).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments(Serializable[].class), @@ -553,9 +494,7 @@ public static Object[][] data_imports_type_of_generic_interface_with_parameteriz @Test @UseDataProvider public void test_imports_type_of_generic_interface_with_parameterized_array_bounds(Class testInput) { - JavaType genericInterface = getOnlyElement( - new ClassFileImporter().importClasses(testInput, InterfaceWithThreeTypeParameters.class, List.class, String.class) - .get(testInput).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(testInput).getInterfaces()); assertThatType(genericInterface).hasActualTypeArguments( genericArray(parameterizedTypeArrayName(List.class, String.class, 1)).withComponentType( @@ -594,9 +533,7 @@ public static Object[][] data_imports_complex_type_with_multiple_nested_actual_t @Test @UseDataProvider public void test_imports_complex_type_with_multiple_nested_actual_type_arguments_of_generic_interface_with_generic_array_bounds(Class testInput) { - JavaType genericInterface = getOnlyElement( - new ClassFileImporter().importClasses(testInput, List.class, Serializable.class, Map.class, String.class) - .get(testInput).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(testInput).getInterfaces()); assertThatType(genericInterface).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments( @@ -649,11 +586,7 @@ public static Object[][] data_imports_multiple_generic_interfaces() { @Test @UseDataProvider public void test_imports_multiple_generic_interfaces(Class testInput) { - JavaClass child = new ClassFileImporter() - .importClasses(testInput, - InterfaceWithOneTypeParameter.class, InterfaceWithTwoTypeParameters.class, InterfaceWithThreeTypeParameters.class, - Path.class, List.class, Map.class, String.class, Serializable.class, File.class) - .get(testInput); + JavaClass child = new ClassFileImporter().importClass(testInput); assertThatType(getGenericInterface(child, InterfaceWithOneTypeParameter.class)).as("generic interface") .hasActualTypeArguments(Path.class); @@ -680,11 +613,7 @@ class Child extends BaseClass implements InterfaceWithOneTypeParameter, InterfaceWithTwoTypeParameters { } - JavaClass child = new ClassFileImporter() - .importClasses(Child.class, - InterfaceWithOneTypeParameter.class, InterfaceWithTwoTypeParameters.class, InterfaceWithThreeTypeParameters.class, - Path.class, List.class, Map.class, String.class, Serializable.class, File.class) - .get(Child.class); + JavaClass child = new ClassFileImporter().importClass(Child.class); assertThatType(child.getSuperclass().get()) .hasErasure(BaseClass.class) @@ -704,10 +633,7 @@ class Child implements SomeDeeplyNestedInterface> { } - JavaType genericInterface = getOnlyElement( - new ClassFileImporter() - .importClasses(Child.class, SomeDeeplyNestedInterface.class, SomeNestedInterface.class, File.class, Path.class) - .get(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface") .hasErasure(SomeDeeplyNestedInterface.class) diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodReturnTypesTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodReturnTypesTest.java index ecd96e8e87..a4a710d75b 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodReturnTypesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodReturnTypesTest.java @@ -6,17 +6,17 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Callable; +import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.core.domain.JavaClass; -import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.domain.JavaType; import com.tngtech.archunit.core.domain.JavaTypeVariable; -import com.tngtech.archunit.testutil.ArchConfigurationRule; import com.tngtech.java.junit.dataprovider.DataProviderRunner; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThatType; import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteClass.concreteClass; import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteGenericArray.genericArray; @@ -29,9 +29,6 @@ @RunWith(DataProviderRunner.class) public class ClassFileImporterGenericMethodReturnTypesTest { - @Rule - public final ArchConfigurationRule configurationRule = new ArchConfigurationRule().resolveAdditionalDependenciesFromClassPath(false); - @Test public void imports_non_generic_method_return_type() { class NonGenericReturnType { @@ -61,7 +58,7 @@ int primitive(T irrelevant) { } } - JavaClass javaClass = new ClassFileImporter().importClasses(SomeClass.class, Object.class).get(SomeClass.class); + JavaClass javaClass = new ClassFileImporter().importClass(SomeClass.class); JavaType returnType = javaClass.getMethod("method", Object.class).getReturnType(); assertThatType(returnType).as("return type").matches(Object.class); @@ -82,8 +79,7 @@ GenericReturnType method() { } } - JavaType genericReturnType = new ClassFileImporter().importClasses(SomeClass.class, String.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type") .hasErasure(GenericReturnType.class) @@ -102,8 +98,7 @@ GenericReturnType method() { } } - JavaType rawGenericReturnType = new ClassFileImporter().importClasses(SomeClass.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType rawGenericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(rawGenericReturnType).as("raw generic method return type").matches(GenericReturnType.class); } @@ -120,8 +115,7 @@ GenericReturnType method() { } } - JavaType genericMethodReturnType = new ClassFileImporter().importClasses(SomeClass.class, String.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericMethodReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericMethodReturnType).as("generic method return type") .hasErasure(GenericReturnType.class) @@ -140,8 +134,7 @@ GenericReturnType method() { } } - JavaType genericMethodReturnType = new ClassFileImporter().importClasses(SomeClass.class, int.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericMethodReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericMethodReturnType).as("generic method return type") .hasErasure(GenericReturnType.class) @@ -160,8 +153,7 @@ GenericReturnType method() { } } - JavaType genericReturnType = new ClassFileImporter().importClasses(SomeClass.class, String.class, Serializable.class, File.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type") .hasErasure(GenericReturnType.class) @@ -180,8 +172,7 @@ GenericReturnType> method() { } } - JavaType genericReturnType = new ClassFileImporter().importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class, String.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -204,11 +195,7 @@ InterfaceParameterWithSingleTypeParameter> method() { } } - JavaType genericReturnType = new ClassFileImporter() - .importClasses( - SomeClass.class, ClassParameterWithSingleTypeParameter.class, InterfaceParameterWithSingleTypeParameter.class, - File.class, Serializable.class, String.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -232,8 +219,7 @@ GenericReturnType method() { } } - JavaType genericReturnType = new ClassFileImporter().importClasses(SomeClass.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments(wildcardType()); } @@ -250,8 +236,7 @@ GenericReturnType> method() { } } - JavaType genericReturnType = new ClassFileImporter().importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -273,8 +258,7 @@ ClassParameterWithSingleTypeParameter> method() { } } - JavaType genericReturnType = new ClassFileImporter().importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class, String.class, File.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -298,11 +282,7 @@ ClassParameterWithSingleTypeParameter>> method() { } } - JavaType genericReturnType = new ClassFileImporter() - .importClasses( - SomeClass.class, ClassParameterWithSingleTypeParameter.class, - Map.class, Serializable.class, File.class, Reference.class, String.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -325,8 +305,7 @@ T method() { } } - JavaType genericMethodReturnType = new ClassFileImporter().importClasses(SomeClass.class, String.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericMethodReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericMethodReturnType).as("generic method return type") .isInstanceOf(JavaTypeVariable.class) @@ -345,8 +324,7 @@ GenericReturnType method() { } } - JavaType genericReturnType = new ClassFileImporter().importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments(typeVariable("OF_CLASS")); } @@ -363,8 +341,7 @@ GenericReturnType> method() { } } - JavaType genericReturnType = new ClassFileImporter().importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -384,8 +361,7 @@ GenericReturnType> method() { } } - JavaType genericReturnType = new ClassFileImporter().importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class, String.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -410,13 +386,8 @@ GenericReturnType method() { } } - JavaType genericReturnType = new ClassFileImporter() - .importClasses( - OuterWithTypeParameter.class, - OuterWithTypeParameter.SomeInner.class, - OuterWithTypeParameter.SomeInner.SomeClass.class, - String.class) - .get(OuterWithTypeParameter.SomeInner.SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.SomeClass.class) + .getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( typeVariable("OUTER").withUpperBounds(String.class) @@ -439,9 +410,14 @@ GenericReturnType method() { } } - JavaType genericReturnType = new ClassFileImporter() - .importClasses(OuterWithTypeParameter.SomeInner.SomeClass.class, String.class) - .get(OuterWithTypeParameter.SomeInner.SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = resetConfigurationAround(new Callable() { + @Override + public JavaType call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.SomeClass.class) + .getMethod("method").getReturnType(); + } + }); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( typeVariable("OUTER").withoutUpperBounds() @@ -463,8 +439,7 @@ T4 method() { Class innermostClass = Class.forName(Level1.class.getName() + "$1Level3"); JavaType genericReturnType = new ClassFileImporter() - .importClasses(innermostClass, Level1.class, String.class) - .get(innermostClass).getMethod("method").getReturnType(); + .importClass(innermostClass).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type") .matches( @@ -489,8 +464,7 @@ ClassParameterWithSingleTypeParameter> method() { } JavaType genericReturnType = new ClassFileImporter() - .importClasses(SomeClass.class, ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + .importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -521,12 +495,7 @@ ClassParameterWithSingleTypeParameter> method() { } JavaType genericReturnType = new ClassFileImporter() - .importClasses( - OuterWithTypeParameter.class, - OuterWithTypeParameter.SomeInner.class, - OuterWithTypeParameter.SomeInner.SomeClass.class, - ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) - .get(OuterWithTypeParameter.SomeInner.SomeClass.class).getMethod("method").getReturnType(); + .importClass(OuterWithTypeParameter.SomeInner.SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -557,11 +526,15 @@ ClassParameterWithSingleTypeParameter> method() { } } - JavaType genericReturnType = new ClassFileImporter() - .importClasses( - OuterWithTypeParameter.SomeInner.SomeClass.class, - ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) - .get(OuterWithTypeParameter.SomeInner.SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = resetConfigurationAround(new Callable() { + @Override + public JavaType call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return new ClassFileImporter().importClasses(OuterWithTypeParameter.SomeInner.SomeClass.class, ClassParameterWithSingleTypeParameter.class) + .get(OuterWithTypeParameter.SomeInner.SomeClass.class) + .getMethod("method").getReturnType(); + } + }); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -594,10 +567,7 @@ Comparable>> method() { } } - JavaType genericReturnType = new ClassFileImporter() - .importClasses(SomeClass.class, String.class, Serializable.class, Cloneable.class, - List.class, Map.class, Map.Entry.class, Set.class, Iterable.class, Comparable.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); // @formatter:off assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( @@ -646,10 +616,7 @@ class SomeClass { } } - JavaClasses classes = new ClassFileImporter().importClasses(SomeClass.class, - Serializable.class, Map.class, String.class); - - JavaType genericReturnType = classes.get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).matches( genericArray( @@ -682,10 +649,7 @@ class SomeClass { } } - JavaClasses classes = new ClassFileImporter().importClasses(SomeClass.class, - List.class, Serializable.class, Map.class, String.class); - - JavaType genericReturnType = classes.get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments(Serializable[].class), @@ -712,8 +676,7 @@ GenericReturnType[], List[][], List[][][]> me } } - JavaType genericReturnType = new ClassFileImporter().importClasses(SomeClass.class, List.class, String.class) - .get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).hasActualTypeArguments( genericArray(parameterizedTypeArrayName(List.class, String.class, 1)).withComponentType( @@ -736,8 +699,7 @@ abstract class SomeClass { abstract T[][] method2Dim(); } - JavaClass javaClass = new ClassFileImporter().importClasses(SomeClass.class, String.class) - .get(SomeClass.class); + JavaClass javaClass = new ClassFileImporter().importClass(SomeClass.class); assertThatType(javaClass.getMethod("method").getReturnType()) .hasErasure(String[].class) @@ -767,10 +729,7 @@ class SomeClass { } } - JavaClasses classes = new ClassFileImporter().importClasses(SomeClass.class, - List.class, Serializable.class, Map.class, String.class); - - JavaType genericReturnType = classes.get(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).hasActualTypeArguments( genericArray("X[]").withComponentType( diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodSignaturesTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodSignaturesTest.java index 86fbc3bb0b..2d3f426e67 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodSignaturesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodSignaturesTest.java @@ -9,12 +9,10 @@ import java.util.Set; import java.util.concurrent.Callable; -import com.google.common.collect.FluentIterable; import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.base.Function; import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.domain.JavaCodeUnit; -import com.tngtech.archunit.testutil.ArchConfigurationRule; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; @@ -22,6 +20,7 @@ import org.junit.runner.RunWith; import static com.google.common.collect.Iterables.getOnlyElement; +import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThat; import static com.tngtech.archunit.testutil.Assertions.assertThatCodeUnit; import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteClass.concreteClass; @@ -69,8 +68,7 @@ class GenericSignatureOnMethod { void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - Object.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -112,8 +110,7 @@ class GenericSignatureOnMethod { void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - String.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -134,8 +131,7 @@ class GenericSignatureOnMethod { void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - String.class, System.class, File.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -160,8 +156,7 @@ class GenericSignatureOnMethod { void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - Serializable.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -182,8 +177,7 @@ class GenericSignatureOnMethod { void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - String.class, Serializable.class, Runnable.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -204,8 +198,7 @@ class GenericSignatureOnMethod { void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - String.class, Serializable.class, System.class, Runnable.class, File.class, Serializable.class, Closeable.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -230,8 +223,7 @@ class GenericSignatureOnMethod { > void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - ClassParameterWithSingleTypeParameter.class, String.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -253,8 +245,7 @@ class GenericSignatureOnMethod { > void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - ClassParameterWithSingleTypeParameter.class, String.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -276,8 +267,7 @@ class GenericSignatureOnMethod { > void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - ClassParameterWithSingleTypeParameter.class, String.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -307,8 +297,7 @@ class GenericSignatureOnMethod { > void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - ClassParameterWithSingleTypeParameter.class, File.class, InterfaceParameterWithSingleTypeParameter.class, Serializable.class, String.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -336,9 +325,7 @@ class GenericSignatureOnMethod { B extends Map & Iterable & Function> void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - ClassParameterWithSingleTypeParameter.class, InterfaceParameterWithSingleTypeParameter.class, - Map.class, Iterable.class, Function.class, String.class, Serializable.class, File.class, Integer.class, Long.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -368,7 +355,7 @@ class GenericSignatureOnMethod { > void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, List.class, String.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -391,8 +378,8 @@ > void genericSignatureOnMethod() { } } return testCasesFromSameGenericSignatureOnConstructorAndMethod( - GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - List.class, String.class); + GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class + ); } @Test @@ -417,8 +404,8 @@ > void genericSignatureOnMethod() { } } return testCasesFromSameGenericSignatureOnConstructorAndMethod( - GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - List.class, Serializable.class); + GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class + ); } @Test @@ -443,8 +430,8 @@ > void genericSignatureOnMethod() { } } return testCasesFromSameGenericSignatureOnConstructorAndMethod( - GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - List.class, String.class); + GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class + ); } @Test @@ -469,8 +456,8 @@ > void genericSignatureOnMethod() { } } return testCasesFromSameGenericSignatureOnConstructorAndMethod( - GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - List.class, Serializable.class); + GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class + ); } @Test @@ -494,8 +481,7 @@ class GenericSignatureOnMethod { , B extends Reference & Map> void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - Map.class, Serializable.class, File.class, Reference.class, String.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -534,7 +520,7 @@ class GenericSignatureOnMethod { void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, String.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -567,11 +553,8 @@ void genericSignatureOn } return testCasesFromSameGenericSignatureOnConstructorAndMethod( Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnConstructor.class, - Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnMethod.class, - Outer.class, - Outer.SomeInner.class, - Outer.SomeInner.EvenMoreInnerDeclaringOwn.class, - String.class); + Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnMethod.class + ); } @Test @@ -613,12 +596,8 @@ class EvenMoreInner { return testCasesFromSameGenericSignatureOnConstructorAndMethod( ConstructorWithTypeParameterBoundByInnerClass.class, - MethodWithTypeParameterBoundByInnerClass.class, - ConstructorWithTypeParameterBoundByInnerClass.SomeInner.class, - ConstructorWithTypeParameterBoundByInnerClass.SomeInner.EvenMoreInner.class, - MethodWithTypeParameterBoundByInnerClass.SomeInner.class, - MethodWithTypeParameterBoundByInnerClass.SomeInner.EvenMoreInner.class, - String.class); + MethodWithTypeParameterBoundByInnerClass.class + ); } @Test @@ -656,9 +635,21 @@ void genericSignatureOn } } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod( - Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnConstructor.class, - Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnMethod.class); + + JavaClasses classes = resetConfigurationAround(new Callable() { + @Override + public JavaClasses call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return new ClassFileImporter().importClasses( + Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnConstructor.class, + Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnMethod.class); + } + }); + + return testForEach( + getOnlyElement(classes.get(Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnConstructor.class).getConstructors()), + getOnlyElement(classes.get(Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnMethod.class).getMethods()) + ); } @Test @@ -694,8 +685,7 @@ void genericSignatureOnMethod() { String level3ClassName = Level1.class.getName() + "$1Level3"; return testCasesFromSameGenericSignatureOnConstructorAndMethod( Class.forName(level3ClassName + "$GenericSignatureOnConstructor"), - Class.forName(level3ClassName + "$GenericSignatureOnMethod"), - Class.forName(level3ClassName), Level1.class, String.class); + Class.forName(level3ClassName + "$GenericSignatureOnMethod")); } @Test @@ -725,8 +715,8 @@ , V extends List> void } return testCasesFromSameGenericSignatureOnConstructorAndMethod( GenericSignatureOnConstructor.class, - GenericSignatureOnMethod.class, - List.class, String.class); + GenericSignatureOnMethod.class + ); } @Test @@ -763,10 +753,8 @@ , MOST_INNER2 extends List> voi } return testCasesFromSameGenericSignatureOnConstructorAndMethod( Outer.Inner.GenericSignatureOnConstructor.class, - Outer.Inner.GenericSignatureOnMethod.class, - Outer.class, - Outer.Inner.class, - List.class, String.class); + Outer.Inner.GenericSignatureOnMethod.class + ); } @Test @@ -803,10 +791,22 @@ , MOST_INNER2 extends List> voi } } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod( - Outer.Inner.GenericSignatureOnConstructor.class, - Outer.Inner.GenericSignatureOnMethod.class, - List.class, String.class); + + JavaClasses classes = resetConfigurationAround(new Callable() { + @Override + public JavaClasses call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return new ClassFileImporter().importClasses( + Outer.Inner.GenericSignatureOnConstructor.class, + Outer.Inner.GenericSignatureOnMethod.class, + List.class); + } + }); + + return testForEach( + getOnlyElement(classes.get(Outer.Inner.GenericSignatureOnConstructor.class).getConstructors()), + getOnlyElement(classes.get(Outer.Inner.GenericSignatureOnMethod.class).getMethods()) + ); } @Test @@ -855,8 +855,7 @@ class GenericSignatureOnMethod { > void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - List.class, Serializable.class, Comparable.class, Map.class, Map.Entry.class, String.class, Set.class, Iterable.class, Object.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -916,8 +915,7 @@ class GenericSignatureOnMethod { > void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - List.class, Serializable.class, Map.class, String.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -958,8 +956,7 @@ class GenericSignatureOnMethod { > void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - ClassWithThreeTypeParameters.class, List.class, String.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -1004,8 +1001,7 @@ class GenericSignatureOnMethod { > void genericSignatureOnMethod() { } } - return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class, - List.class, Serializable.class, Map.class, String.class); + return testCasesFromSameGenericSignatureOnConstructorAndMethod(GenericSignatureOnConstructor.class, GenericSignatureOnMethod.class); } @Test @@ -1042,18 +1038,8 @@ public void test_imports_complex_type_with_multiple_nested_parameters_with_gener ); } - private static Object[][] testCasesFromSameGenericSignatureOnConstructorAndMethod( - Class genericSignatureOnConstructor, - Class genericSignatureOnMethod, - Class... additionalImports) { - final List> toImport = FluentIterable.from(additionalImports).append(genericSignatureOnConstructor).append(genericSignatureOnMethod).toList(); - JavaClasses classes = ArchConfigurationRule.resetConfigurationAround(new Callable() { - @Override - public JavaClasses call() { - ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter().importClasses(toImport); - } - }); + private static Object[][] testCasesFromSameGenericSignatureOnConstructorAndMethod(Class genericSignatureOnConstructor, Class genericSignatureOnMethod) { + JavaClasses classes = new ClassFileImporter().importClasses(genericSignatureOnConstructor, genericSignatureOnMethod); return testForEach( getOnlyElement(classes.get(genericSignatureOnConstructor).getConstructors()), diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericSuperclassTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericSuperclassTest.java index 9fd41a5bba..57fc141ea7 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericSuperclassTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericSuperclassTest.java @@ -6,15 +6,15 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Callable; -import com.tngtech.archunit.core.domain.JavaClasses; +import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.core.domain.JavaType; -import com.tngtech.archunit.testutil.ArchConfigurationRule; import com.tngtech.java.junit.dataprovider.DataProviderRunner; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThatType; import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteClass.concreteClass; import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteGenericArray.genericArray; @@ -27,9 +27,6 @@ @RunWith(DataProviderRunner.class) public class ClassFileImporterGenericSuperclassTest { - @Rule - public final ArchConfigurationRule configurationRule = new ArchConfigurationRule().resolveAdditionalDependenciesFromClassPath(false); - @Test public void imports_non_generic_superclass() { class BaseClass { @@ -50,7 +47,7 @@ class BaseClass { class Child extends BaseClass { } - JavaType genericSuperclass = new ClassFileImporter().importClasses(Child.class, String.class).get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass") .hasErasure(BaseClass.class) @@ -66,8 +63,7 @@ class BaseClass { class Child extends BaseClass { } - JavaType rawGenericSuperclass = new ClassFileImporter().importClasses(Child.class, BaseClass.class) - .get(Child.class).getSuperclass().get(); + JavaType rawGenericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(rawGenericSuperclass).as("raw generic superclass").matches(BaseClass.class); } @@ -80,8 +76,7 @@ class BaseClass { class Child extends BaseClass { } - JavaType genericSuperClass = new ClassFileImporter().importClasses(Child.class, String.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperClass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperClass).as("generic superclass") .hasErasure(BaseClass.class) @@ -96,8 +91,7 @@ class BaseClass { class Child extends BaseClass { } - JavaType genericSuperClass = new ClassFileImporter().importClasses(Child.class, int.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperClass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperClass).as("generic superclass") .hasErasure(BaseClass.class) @@ -113,8 +107,7 @@ class BaseClass { class Child extends BaseClass { } - JavaType genericSuperclass = new ClassFileImporter().importClasses(Child.class, String.class, Serializable.class, File.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass") .hasErasure(BaseClass.class) @@ -129,8 +122,7 @@ class BaseClass { class Child extends BaseClass> { } - JavaType genericSuperclass = new ClassFileImporter().importClasses(Child.class, ClassParameterWithSingleTypeParameter.class, String.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -149,11 +141,7 @@ class Child extends BaseClass< InterfaceParameterWithSingleTypeParameter> { } - JavaType genericSuperclass = new ClassFileImporter() - .importClasses( - Child.class, ClassParameterWithSingleTypeParameter.class, InterfaceParameterWithSingleTypeParameter.class, - File.class, Serializable.class, String.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -173,8 +161,7 @@ class BaseClass { class Child extends BaseClass> { } - JavaType genericSuperclass = new ClassFileImporter().importClasses(Child.class, ClassParameterWithSingleTypeParameter.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -192,8 +179,7 @@ class Child extends BaseClass< ClassParameterWithSingleTypeParameter> { } - JavaType genericSuperclass = new ClassFileImporter().importClasses(Child.class, ClassParameterWithSingleTypeParameter.class, String.class, File.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -213,11 +199,7 @@ class Child extends BaseClass< ClassParameterWithSingleTypeParameter>> { } - JavaType genericSuperclass = new ClassFileImporter() - .importClasses( - Child.class, ClassParameterWithSingleTypeParameter.class, - Map.class, Serializable.class, File.class, Reference.class, String.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -239,8 +221,7 @@ class BaseClass { class Child extends BaseClass { } - JavaType genericSuperclass = new ClassFileImporter().importClasses(Child.class, BaseClass.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments(typeVariable("SUB")); } @@ -253,8 +234,7 @@ class BaseClass { class Child extends BaseClass> { } - JavaType genericSuperclass = new ClassFileImporter().importClasses(Child.class, ClassParameterWithSingleTypeParameter.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -270,8 +250,7 @@ class BaseClass { class Child extends BaseClass> { } - JavaType genericSuperclass = new ClassFileImporter().importClasses(Child.class, ClassParameterWithSingleTypeParameter.class, String.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -293,13 +272,7 @@ class Child extends BaseClass { } } - JavaType genericSuperclass = new ClassFileImporter() - .importClasses( - OuterWithTypeParameter.class, - OuterWithTypeParameter.SomeInner.class, - OuterWithTypeParameter.SomeInner.Child.class, - String.class) - .get(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( typeVariable("OUTER").withUpperBounds(String.class) @@ -320,9 +293,14 @@ class Child extends BaseClass { } } - JavaType genericSuperclass = new ClassFileImporter() - .importClasses(OuterWithTypeParameter.SomeInner.Child.class, String.class) - .get(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); + JavaType genericSuperclass = resetConfigurationAround(new Callable() { + @Override + public JavaType call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); + } + }); + ; assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( typeVariable("OUTER").withoutUpperBounds() @@ -342,9 +320,7 @@ class Level3 extends BaseClass { } Class innermostClass = Class.forName(Level1.class.getName() + "$1Level3"); - JavaType genericSuperclass = new ClassFileImporter() - .importClasses(innermostClass, Level1.class, String.class) - .get(innermostClass).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(innermostClass).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass") .hasActualTypeArguments( @@ -364,9 +340,7 @@ class Child extends BaseClass ClassParameterWithSingleTypeParameter> { } - JavaType genericSuperclass = new ClassFileImporter() - .importClasses(Child.class, ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -394,13 +368,7 @@ class Child extends BaseClass< } } - JavaType genericSuperclass = new ClassFileImporter() - .importClasses( - OuterWithTypeParameter.class, - OuterWithTypeParameter.SomeInner.class, - OuterWithTypeParameter.SomeInner.Child.class, - ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) - .get(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -428,11 +396,15 @@ class Child extends BaseClass< } } - JavaType genericSuperclass = new ClassFileImporter() - .importClasses( - OuterWithTypeParameter.SomeInner.Child.class, - ClassParameterWithSingleTypeParameter.class, String.class, Serializable.class) - .get(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); + JavaType genericSuperclass = resetConfigurationAround(new Callable() { + @Override + public JavaType call() { + ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); + return new ClassFileImporter() + .importClasses(OuterWithTypeParameter.SomeInner.Child.class, ClassParameterWithSingleTypeParameter.class) + .get(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); + } + }); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -462,10 +434,7 @@ class Child>> { } - JavaType genericSuperclass = new ClassFileImporter() - .importClasses(Child.class, String.class, Serializable.class, Cloneable.class, - List.class, Map.class, Map.Entry.class, Set.class, Iterable.class, Comparable.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); // @formatter:off assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( @@ -516,10 +485,7 @@ class Child extends BaseClass< > { } - JavaClasses classes = new ClassFileImporter().importClasses(Child.class, - List.class, Serializable.class, Map.class, String.class); - - JavaType genericSuperclass = classes.get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments(Serializable[].class), @@ -542,8 +508,7 @@ class BaseClass { class Child extends BaseClass[], List[][], List[][][]> { } - JavaType genericSuperclass = new ClassFileImporter().importClasses(Child.class, List.class, String.class) - .get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).hasActualTypeArguments( genericArray(parameterizedTypeArrayName(List.class, String.class, 1)).withComponentType( @@ -571,10 +536,7 @@ class Child extends BaseClass< > { } - JavaClasses classes = new ClassFileImporter().importClasses(Child.class, - List.class, Serializable.class, Map.class, String.class); - - JavaType genericSuperclass = classes.get(Child.class).getSuperclass().get(); + JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments( From f2c0e018cd82cff5ed62366d6e15296631eb9da7 Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sat, 5 Feb 2022 15:22:21 +0700 Subject: [PATCH 21/22] make import dependency resolution behavior configurable We can now easily allow users to tweak the automatic import dependency resolution behavior to their needs. This way, if some classes should be missing from the import users can decide to increase the resolution behavior for the respective type at the cost of performance. Or on the other hand, if certain information is irrelevant users can boost performance by decreasing the automatic resolution. We can now also adjust the resolution behavior to explicitly check only one type of resolution (e.g. member types) to ensure we are not by accident resolving the type by a different mechanism. However, for the generic type import tests we now have to explicitly import enclosing classes again. Signed-off-by: Peter Gafert --- ...ssFileImporterAutomaticResolutionTest.java | 117 ++++++++++++------ .../importer/DependencyResolutionProcess.java | 64 +++++++++- .../ClassFileImporterGenericClassesTest.java | 73 ++++++----- ...rterGenericCodeUnitParameterTypesTest.java | 28 +++-- ...lassFileImporterGenericFieldTypesTest.java | 73 ++++++----- ...lassFileImporterGenericInterfacesTest.java | 71 ++++++----- ...eImporterGenericMethodReturnTypesTest.java | 74 ++++++----- ...leImporterGenericMethodSignaturesTest.java | 34 +++-- ...lassFileImporterGenericSuperclassTest.java | 61 +++++---- .../DependencyResolutionProcessTestUtils.java | 92 ++++++++++++++ .../userguide/010_Advanced_Configuration.adoc | 43 +++++++ 11 files changed, 521 insertions(+), 209 deletions(-) create mode 100644 archunit/src/test/java/com/tngtech/archunit/core/importer/DependencyResolutionProcessTestUtils.java diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java index 3744eb72b0..3512c4aebb 100644 --- a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java @@ -32,6 +32,7 @@ import com.tngtech.archunit.core.domain.ReferencedClassObject; import com.tngtech.archunit.core.domain.ThrowsDeclaration; import com.tngtech.archunit.core.domain.properties.HasAnnotations; +import com.tngtech.archunit.core.importer.DependencyResolutionProcessTestUtils.ImporterWithAdjustedResolutionRuns; import com.tngtech.archunit.core.importer.testexamples.SomeAnnotation; import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassWithUnimportedAnnotation; import com.tngtech.archunit.core.importer.testexamples.annotatedparameters.ClassWithMethodWithAnnotatedParameters; @@ -50,6 +51,12 @@ import org.junit.runner.RunWith; import static com.google.common.collect.Iterables.getOnlyElement; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_ENCLOSING_TYPES_PROPERTY_NAME; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_MEMBER_TYPES_PROPERTY_NAME; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_SUPERTYPES_PROPERTY_NAME; import static com.tngtech.archunit.core.importer.testexamples.SomeEnum.OTHER_VALUE; import static com.tngtech.archunit.core.importer.testexamples.SomeEnum.SOME_VALUE; import static com.tngtech.archunit.core.importer.testexamples.annotatedparameters.ClassWithMethodWithAnnotatedParameters.methodWithOneAnnotatedParameterWithTwoAnnotations; @@ -73,7 +80,8 @@ class FieldTypeWithoutAnyFurtherReference { String field; } - JavaClass javaClass = new ClassFileImporter().importClass(FieldTypeWithoutAnyFurtherReference.class); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_MEMBER_TYPES_PROPERTY_NAME) + .importClass(FieldTypeWithoutAnyFurtherReference.class); assertThat(javaClass.getField("field").getRawType()).as("field type").isFullyImported(true); } @@ -86,7 +94,8 @@ class ConstructorParameterTypesWithoutAnyFurtherReference { } } - JavaClass javaClass = new ClassFileImporter().importClass(ConstructorParameterTypesWithoutAnyFurtherReference.class); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_MEMBER_TYPES_PROPERTY_NAME) + .importClass(ConstructorParameterTypesWithoutAnyFurtherReference.class); JavaConstructor constructor = javaClass.getConstructor(getClass(), FileSystem.class, Buffer.class); assertThat(constructor.getRawParameterTypes().get(0)).as("constructor parameter type").isFullyImported(true); @@ -102,7 +111,8 @@ File returnType() { } } - JavaClass javaClass = new ClassFileImporter().importClass(MemberTypesWithoutAnyFurtherReference.class); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_MEMBER_TYPES_PROPERTY_NAME) + .importClass(MemberTypesWithoutAnyFurtherReference.class); assertThat(javaClass.getMethod("returnType").getRawReturnType()).as("method return type").isFullyImported(true); } @@ -115,7 +125,8 @@ void methodParameters(Path methodParam1, PrintStream methodParam2) { } } - JavaClass javaClass = new ClassFileImporter().importClass(MemberTypesWithoutAnyFurtherReference.class); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_MEMBER_TYPES_PROPERTY_NAME) + .importClass(MemberTypesWithoutAnyFurtherReference.class); JavaMethod method = javaClass.getMethod("methodParameters", Path.class, PrintStream.class); assertThat(method.getRawParameterTypes().get(0)).as("method parameter type").isFullyImported(true); @@ -124,7 +135,8 @@ void methodParameters(Path methodParam1, PrintStream methodParam2) { @Test public void automatically_resolves_class_hierarchy() { - JavaClass child = new ClassFileImporter().importClass(Child.class); + JavaClass child = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_SUPERTYPES_PROPERTY_NAME) + .importClass(Child.class); JavaClass parent = child.getRawSuperclass().get(); assertThat(parent).isFullyImported(true); @@ -144,7 +156,8 @@ public void automatically_resolves_class_hierarchy() { @Test public void automatically_resolves_class_annotations() { - JavaClass clazz = new ClassFileImporter().importClass(ClassWithUnimportedAnnotation.class); + JavaClass clazz = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME) + .importClass(ClassWithUnimportedAnnotation.class); JavaAnnotation annotation = clazz.getAnnotationOfType(SomeAnnotation.class.getName()); @@ -164,7 +177,8 @@ public void automatically_resolves_class_annotations() { @Test public void automatically_resolves_field_annotations() { - JavaClass clazz = new ClassFileImporter().importClass(ClassWithAnnotatedFields.class); + JavaClass clazz = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME) + .importClass(ClassWithAnnotatedFields.class); JavaAnnotation annotation = clazz.getField("fieldAnnotatedWithAnnotationFromParentPackage") .getAnnotationOfType(SomeAnnotation.class.getName()); @@ -181,7 +195,8 @@ public void automatically_resolves_field_annotations() { @Test public void automatically_resolves_method_annotations() { - JavaClass clazz = new ClassFileImporter().importClass(ClassWithAnnotatedMethods.class); + JavaClass clazz = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME) + .importClass(ClassWithAnnotatedMethods.class); JavaAnnotation annotation = clazz.getMethod(methodAnnotatedWithAnnotationFromParentPackage) .getAnnotationOfType(SomeAnnotation.class.getName()); @@ -198,7 +213,8 @@ public void automatically_resolves_method_annotations() { @Test public void automatically_resolves_constructor_annotations() { - JavaClass clazz = new ClassFileImporter().importClass(ClassWithAnnotatedMethods.class); + JavaClass clazz = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME) + .importClass(ClassWithAnnotatedMethods.class); JavaAnnotation annotation = clazz.getConstructor() .getAnnotationOfType(MethodAnnotationWithEnumAndArrayValue.class.getName()); @@ -213,7 +229,8 @@ public void automatically_resolves_constructor_annotations() { @Test public void automatically_resolves_parameter_annotations() { - JavaClass clazz = new ClassFileImporter().importClass(ClassWithMethodWithAnnotatedParameters.class); + JavaClass clazz = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME) + .importClass(ClassWithMethodWithAnnotatedParameters.class); JavaAnnotation annotation = clazz.getMethod(methodWithOneAnnotatedParameterWithTwoAnnotations, String.class) .getParameters().get(0) @@ -231,7 +248,8 @@ public void automatically_resolves_parameter_annotations() { @Test public void automatically_resolves_meta_annotation_types() { - JavaClass javaClass = new ClassFileImporter().importClass(MetaAnnotatedClass.class); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME) + .importClass(MetaAnnotatedClass.class); JavaAnnotation someAnnotation = javaClass.getAnnotationOfType(MetaAnnotatedAnnotation.class.getName()); JavaAnnotation someMetaAnnotation = someAnnotation.getRawType() .getAnnotationOfType(SomeMetaAnnotation.class.getName()); @@ -245,13 +263,14 @@ public void automatically_resolves_meta_annotation_types() { @DataProvider public static Object[][] elementsAnnotatedWithSomeAnnotation() { + ImporterWithAdjustedResolutionRuns importer = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME); return testForEach( - new ClassFileImporter().importClass(MetaAnnotatedClass.class), - new ClassFileImporter().importClass(ClassWithMetaAnnotatedField.class).getField("metaAnnotatedField"), - new ClassFileImporter().importClass(ClassWithMetaAnnotatedMethod.class).getMethod("metaAnnotatedMethod"), - new ClassFileImporter().importClass(ClassWithMetaAnnotatedConstructor.class).getConstructor(), - getOnlyElement(new ClassFileImporter().importClass(ClassWithMetaAnnotatedConstructorParameter.class).getConstructor(String.class).getParameters()), - getOnlyElement(new ClassFileImporter().importClass(ClassWithMetaAnnotatedMethodParameter.class).getMethod("method", String.class).getParameters()) + importer.importClass(MetaAnnotatedClass.class), + importer.importClass(ClassWithMetaAnnotatedField.class).getField("metaAnnotatedField"), + importer.importClass(ClassWithMetaAnnotatedMethod.class).getMethod("metaAnnotatedMethod"), + importer.importClass(ClassWithMetaAnnotatedConstructor.class).getConstructor(), + getOnlyElement(importer.importClass(ClassWithMetaAnnotatedConstructorParameter.class).getConstructor(String.class).getParameters()), + getOnlyElement(importer.importClass(ClassWithMetaAnnotatedMethodParameter.class).getMethod("method", String.class).getParameters()) ); } @@ -307,7 +326,9 @@ Object resolvesFieldAccessOwner() { } } - JavaFieldAccess access = getOnlyElement(new ClassFileImporter().importClass(Origin.class).getMethod("resolvesFieldAccessOwner").getFieldAccesses()); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME) + .importClass(Origin.class); + JavaFieldAccess access = getOnlyElement(javaClass.getMethod("resolvesFieldAccessOwner").getFieldAccesses()); assertThat(access.getTargetOwner()).isFullyImported(true); } @@ -326,7 +347,9 @@ void resolvesMethodCallTargetOwner() { } } - JavaMethodCall call = getOnlyElement(new ClassFileImporter().importClass(Origin.class).getMethodCallsFromSelf()); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME) + .importClass(Origin.class); + JavaMethodCall call = getOnlyElement(javaClass.getMethodCallsFromSelf()); assertThat(call.getTargetOwner()).isFullyImported(true); } @@ -345,7 +368,9 @@ Runnable resolvesMethodCallTargetOwner() { } } - JavaMethodReference reference = getOnlyElement(new ClassFileImporter().importClass(Origin.class).getMethodReferencesFromSelf()); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME) + .importClass(Origin.class); + JavaMethodReference reference = getOnlyElement(javaClass.getMethodReferencesFromSelf()); assertThat(reference.getTargetOwner()).isFullyImported(true); } @@ -361,7 +386,8 @@ void resolvesConstructorCallTargetOwner() { } } - JavaClass javaClass = new ClassFileImporter().importClass(Origin.class); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME) + .importClass(Origin.class); JavaConstructorCall call = getOnlyElement(javaClass.getMethod("resolvesConstructorCallTargetOwner").getConstructorCallsFromSelf()); assertThat(call.getTargetOwner()).isFullyImported(true); @@ -381,7 +407,8 @@ Supplier resolvesConstructorReferenceTargetOwner() { } } - JavaClass javaClass = new ClassFileImporter().importClass(Origin.class); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME) + .importClass(Origin.class); JavaConstructorReference reference = getOnlyElement(javaClass.getMethod("resolvesConstructorReferenceTargetOwner").getConstructorReferencesFromSelf()); assertThat(reference.getTargetOwner()).isFullyImported(true); @@ -445,12 +472,13 @@ class OnParameter { } } + ImporterWithAdjustedResolutionRuns importer = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME); return FluentIterable.concat( - nestedAnnotationValueTestCases(new ClassFileImporter().importClass(OnClass.class)), - nestedAnnotationValueTestCases(new ClassFileImporter().importClass(OnField.class).getField("field")), - nestedAnnotationValueTestCases(new ClassFileImporter().importClass(OnMethod.class).getMethod("method")), - nestedAnnotationValueTestCases(getOnlyElement(new ClassFileImporter().importClass(OnConstructor.class).getConstructors())), - nestedAnnotationValueTestCases(getOnlyElement(new ClassFileImporter().importClass(OnParameter.class).getConstructors()).getParameters().get(0)) + nestedAnnotationValueTestCases(importer.importClass(OnClass.class)), + nestedAnnotationValueTestCases(importer.importClass(OnField.class).getField("field")), + nestedAnnotationValueTestCases(importer.importClass(OnMethod.class).getMethod("method")), + nestedAnnotationValueTestCases(getOnlyElement(importer.importClass(OnConstructor.class).getConstructors())), + nestedAnnotationValueTestCases(getOnlyElement(importer.importClass(OnParameter.class).getConstructors()).getParameters().get(0)) ).toArray(Object[].class); } @@ -491,7 +519,9 @@ Class call() { } } - ReferencedClassObject classObject = getOnlyElement(new ClassFileImporter().importClass(Origin.class).getReferencedClassObjects()); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME) + .importClass(Origin.class); + ReferencedClassObject classObject = getOnlyElement(javaClass.getReferencedClassObjects()); assertThat(classObject.getRawType()).isFullyImported(true); assertThatType(classObject.getRawType()).matches(String.class); @@ -506,7 +536,9 @@ boolean call(Object obj) { } } - InstanceofCheck instanceofCheck = getOnlyElement(new ClassFileImporter().importClass(Origin.class).getInstanceofChecks()); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME) + .importClass(Origin.class); + InstanceofCheck instanceofCheck = getOnlyElement(javaClass.getInstanceofChecks()); assertThat(instanceofCheck.getRawType()).isFullyImported(true); assertThatType(instanceofCheck.getRawType()).matches(String.class); @@ -520,7 +552,9 @@ void call() throws InterruptedException { } } - ThrowsDeclaration throwsDeclaration = getOnlyElement(new ClassFileImporter().importClass(Origin.class).getThrowsDeclarations()); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_MEMBER_TYPES_PROPERTY_NAME) + .importClass(Origin.class); + ThrowsDeclaration throwsDeclaration = getOnlyElement(javaClass.getThrowsDeclarations()); assertThat(throwsDeclaration.getRawType()).isFullyImported(true); assertThatType(throwsDeclaration.getRawType()).matches(InterruptedException.class); @@ -563,7 +597,8 @@ class Innermost { } Class lessInnerClass = Class.forName(Outermost.LessOuter.LeastOuter.class.getName() + "$1LessInner"); - JavaClass innermost = new ClassFileImporter().importClass(Class.forName(lessInnerClass.getName() + "$Innermost")); + JavaClass innermost = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ENCLOSING_TYPES_PROPERTY_NAME) + .importClass(Class.forName(lessInnerClass.getName() + "$Innermost")); JavaClass lessInner = innermost.getEnclosingClass().get(); assertThat(lessInner).isFullyImported(true); @@ -732,24 +767,34 @@ public void test_automatically_resolves_parameterized_generic_type_bounds( } private static JavaType importFirstTypeParameterClassBound(Class clazz) { - return getOnlyElement(getOnlyElement(new ClassFileImporter().importClass(clazz).getTypeParameters()).getBounds()); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME) + .importClass(clazz); + return getOnlyElement(getOnlyElement(javaClass.getTypeParameters()).getBounds()); } private static JavaType importFirstTypeParameterMethodBound(Class clazz) { - return getOnlyElement(getOnlyElement(new ClassFileImporter().importClass(clazz).getMethod("method").getTypeParameters()).getBounds()); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME) + .importClass(clazz); + return getOnlyElement(getOnlyElement(javaClass.getMethod("method").getTypeParameters()).getBounds()); } private static JavaType importFirstTypeArgumentFieldBound(Class clazz) { - return getFirstTypeArgumentUpperBound(new ClassFileImporter().importClass(clazz).getField("field").getType()); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME) + .importClass(clazz); + return getFirstTypeArgumentUpperBound(javaClass.getField("field").getType()); } private static JavaType importFirstTypeArgumentMethodParameterBound(Class clazz) { - JavaMethod method = getOnlyElement(new ClassFileImporter().importClass(clazz).getMethods()); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME) + .importClass(clazz); + JavaMethod method = getOnlyElement(javaClass.getMethods()); return getFirstTypeArgumentUpperBound(method.getParameterTypes().get(0)); } private static JavaType importFirstTypeArgumentConstructorParameterBound(Class clazz) { - JavaConstructor constructor = getOnlyElement(new ClassFileImporter().importClass(clazz).getConstructors()); + JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME) + .importClass(clazz); + JavaConstructor constructor = getOnlyElement(javaClass.getConstructors()); return getFirstTypeArgumentUpperBound(constructor.getParameterTypes().get(0)); } diff --git a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java index 3943310f5b..d032c3c42c 100644 --- a/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java +++ b/archunit/src/main/java/com/tngtech/archunit/core/importer/DependencyResolutionProcess.java @@ -17,19 +17,52 @@ import java.util.Collection; import java.util.HashSet; +import java.util.Properties; import java.util.Set; +import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.core.importer.ImportedClasses.ImportedClassState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import static com.tngtech.archunit.core.importer.ImportedClasses.ImportedClassState.HAD_TO_BE_IMPORTED; +import static java.lang.System.lineSeparator; class DependencyResolutionProcess { - private static final int maxRunsForMemberTypes = 1; - private static final int maxRunsForAccessesToTypes = 1; - private static final int maxRunsForSupertypes = -1; - private static final int maxRunsForEnclosingTypes = -1; - private static final int maxRunsForAnnotationTypes = -1; - private static final int maxRunsForGenericSignatureTypes = -1; + private static final Logger log = LoggerFactory.getLogger(DependencyResolutionProcess.class); + + static final String DEPENDENCY_RESOLUTION_PROCESS_PROPERTY_PREFIX = "import.dependencyResolutionProcess"; + private final Properties resolutionProcessProperties = ArchConfiguration.get().getSubProperties(DEPENDENCY_RESOLUTION_PROCESS_PROPERTY_PREFIX); + + static final String MAX_ITERATIONS_FOR_MEMBER_TYPES_PROPERTY_NAME = "maxIterationsForMemberTypes"; + static final int MAX_ITERATIONS_FOR_MEMBER_TYPES_DEFAULT_VALUE = 1; + private final int maxRunsForMemberTypes = getConfiguredIterations( + MAX_ITERATIONS_FOR_MEMBER_TYPES_PROPERTY_NAME, MAX_ITERATIONS_FOR_MEMBER_TYPES_DEFAULT_VALUE); + + static final String MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME = "maxIterationsForAccessesToTypes"; + static final int MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_DEFAULT_VALUE = 1; + private final int maxRunsForAccessesToTypes = getConfiguredIterations( + MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME, MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_DEFAULT_VALUE); + + static final String MAX_ITERATIONS_FOR_SUPERTYPES_PROPERTY_NAME = "maxIterationsForSupertypes"; + static final int MAX_ITERATIONS_FOR_SUPERTYPES_DEFAULT_VALUE = -1; + private final int maxRunsForSupertypes = getConfiguredIterations( + MAX_ITERATIONS_FOR_SUPERTYPES_PROPERTY_NAME, MAX_ITERATIONS_FOR_SUPERTYPES_DEFAULT_VALUE); + + static final String MAX_ITERATIONS_FOR_ENCLOSING_TYPES_PROPERTY_NAME = "maxIterationsForEnclosingTypes"; + static final int MAX_ITERATIONS_FOR_ENCLOSING_TYPES_DEFAULT_VALUE = -1; + private final int maxRunsForEnclosingTypes = getConfiguredIterations( + MAX_ITERATIONS_FOR_ENCLOSING_TYPES_PROPERTY_NAME, MAX_ITERATIONS_FOR_ENCLOSING_TYPES_DEFAULT_VALUE); + + static final String MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME = "maxIterationsForAnnotationTypes"; + static final int MAX_ITERATIONS_FOR_ANNOTATION_TYPES_DEFAULT_VALUE = -1; + private final int maxRunsForAnnotationTypes = getConfiguredIterations( + MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME, MAX_ITERATIONS_FOR_ANNOTATION_TYPES_DEFAULT_VALUE); + + static final String MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME = "maxIterationsForGenericSignatureTypes"; + static final int MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_DEFAULT_VALUE = -1; + private final int maxRunsForGenericSignatureTypes = getConfiguredIterations( + MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME, MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_DEFAULT_VALUE); private Set currentTypeNames = new HashSet<>(); private int runNumber = 1; @@ -84,11 +117,26 @@ void registerGenericSignatureType(String typeName) { } void resolve(ImportedClasses classes) { + logConfiguration(); do { executeRun(classes); } while (shouldContinue); } + private void logConfiguration() { + log.info("Automatically resolving transitive class dependencies with the following configuration:{}{}{}{}{}{}", + formatConfigProperty(MAX_ITERATIONS_FOR_MEMBER_TYPES_PROPERTY_NAME, maxRunsForMemberTypes), + formatConfigProperty(MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME, maxRunsForAccessesToTypes), + formatConfigProperty(MAX_ITERATIONS_FOR_SUPERTYPES_PROPERTY_NAME, maxRunsForSupertypes), + formatConfigProperty(MAX_ITERATIONS_FOR_ENCLOSING_TYPES_PROPERTY_NAME, maxRunsForEnclosingTypes), + formatConfigProperty(MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME, maxRunsForAnnotationTypes), + formatConfigProperty(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME, maxRunsForGenericSignatureTypes)); + } + + private String formatConfigProperty(String propertyName, int number) { + return lineSeparator() + DEPENDENCY_RESOLUTION_PROCESS_PROPERTY_PREFIX + "." + propertyName + " = " + number; + } + private void executeRun(ImportedClasses classes) { runNumber++; Set typeNamesToResolve = this.currentTypeNames; @@ -103,4 +151,8 @@ private void executeRun(ImportedClasses classes) { private boolean runNumberHasNotExceeded(int maxRuns) { return maxRuns < 0 || runNumber <= maxRuns; } + + private int getConfiguredIterations(String propertyName, int defaultValue) { + return Integer.parseInt(resolutionProcessProperties.getProperty(propertyName, String.valueOf(defaultValue))); + } } diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericClassesTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericClassesTest.java index 14cfb17da8..f0505a8fd3 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericClassesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericClassesTest.java @@ -18,6 +18,8 @@ import org.junit.Test; import org.junit.runner.RunWith; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcessTestUtils.importClassWithOnlyGenericTypeResolution; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcessTestUtils.importClassesWithOnlyGenericTypeResolution; import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThat; import static com.tngtech.archunit.testutil.Assertions.assertThatType; @@ -36,7 +38,7 @@ public class ClassFileImporterGenericClassesTest { @Test public void imports_empty_list_of_type_parameters_for_non_generic_class() { - JavaClass javaClass = new ClassFileImporter().importClass(getClass()); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(getClass()); assertThat(javaClass.getTypeParameters()).as("type parameters of non generic class").isEmpty(); } @@ -47,7 +49,7 @@ public void imports_single_generic_type_parameter_of_class() { class ClassWithSingleTypeParameterWithoutBound { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithoutBound.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithSingleTypeParameterWithoutBound.class); assertThatType(javaClass).hasOnlyTypeParameter("T").withBoundsMatching(Object.class); } @@ -58,7 +60,7 @@ public void imports_multiple_generic_type_parameters_of_class() { class ClassWithThreeTypeParametersWithoutBounds { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithThreeTypeParametersWithoutBounds.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithThreeTypeParametersWithoutBounds.class); assertThatType(javaClass).hasTypeParameters("A", "B", "C"); } @@ -69,7 +71,7 @@ public void imports_simple_class_bound_of_type_variable() { class ClassWithSingleTypeParameterWithSimpleClassBound { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithSimpleClassBound.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithSingleTypeParameterWithSimpleClassBound.class); assertThatType(javaClass).hasOnlyTypeParameter("T").withBoundsMatching(String.class); } @@ -80,7 +82,7 @@ public void imports_single_simple_class_bounds_of_multiple_type_variables() { class ClassWithThreeTypeParametersWithSimpleClassBounds { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithThreeTypeParametersWithSimpleClassBounds.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithThreeTypeParametersWithSimpleClassBounds.class); assertThatType(javaClass) .hasTypeParameters("A", "B", "C") @@ -95,7 +97,7 @@ public void imports_simple_interface_bound_of_single_type_variable() { class ClassWithSingleTypeParameterWithSimpleInterfaceBound { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithSimpleInterfaceBound.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithSingleTypeParameterWithSimpleInterfaceBound.class); assertThatType(javaClass).hasOnlyTypeParameter("T").withBoundsMatching(Serializable.class); } @@ -106,7 +108,7 @@ public void imports_multiple_simple_bounds_of_single_type_variable() { class ClassWithSingleTypeParameterWithMultipleSimpleClassAndInterfaceBounds { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithMultipleSimpleClassAndInterfaceBounds.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithSingleTypeParameterWithMultipleSimpleClassAndInterfaceBounds.class); assertThatType(javaClass).hasOnlyTypeParameter("T").withBoundsMatching(String.class, Serializable.class, Runnable.class); } @@ -118,7 +120,7 @@ class ClassWithThreeTypeParametersWithMultipleSimpleClassAndInterfaceBounds< A extends String & Serializable, B extends System & Runnable, C extends File & Serializable & Closeable> { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithThreeTypeParametersWithMultipleSimpleClassAndInterfaceBounds.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithThreeTypeParametersWithMultipleSimpleClassAndInterfaceBounds.class); assertThatType(javaClass) .hasTypeParameters("A", "B", "C") @@ -133,7 +135,7 @@ public void imports_single_class_bound_with_single_type_parameter_assigned_to_co class ClassWithSingleTypeParameterWithGenericClassBoundAssignedToConcreteClass> { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToConcreteClass.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToConcreteClass.class); assertThatType(javaClass).hasOnlyTypeParameter("T") .withBoundsMatching(parameterizedType(ClassParameterWithSingleTypeParameter.class).withTypeArguments(String.class)); @@ -145,7 +147,7 @@ public void imports_single_class_bound_with_single_type_parameter_assigned_to_ar class ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType> { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType.class); assertThatType(javaClass).hasOnlyTypeParameter("T") .withBoundsMatching(parameterizedType(ClassParameterWithSingleTypeParameter.class).withTypeArguments(String[].class)); @@ -157,7 +159,7 @@ public void imports_single_class_bound_with_single_type_parameter_assigned_to_pr class ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType> { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithSingleTypeParameterWithGenericClassBoundAssignedToArrayType.class); assertThatType(javaClass).hasOnlyTypeParameter("T") .withBoundsMatching(parameterizedType(ClassParameterWithSingleTypeParameter.class).withTypeArguments(int[].class)); @@ -172,7 +174,7 @@ class ClassWithMultipleTypeParametersWithGenericClassOrInterfaceBoundsAssignedTo C extends InterfaceParameterWithSingleTypeParameter> { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithMultipleTypeParametersWithGenericClassOrInterfaceBoundsAssignedToConcreteTypes.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithMultipleTypeParametersWithGenericClassOrInterfaceBoundsAssignedToConcreteTypes.class); assertThatType(javaClass).hasTypeParameters("A", "B", "C") .hasTypeParameter("A").withBoundsMatching(parameterizedType(ClassParameterWithSingleTypeParameter.class).withTypeArguments(File.class)) @@ -188,7 +190,7 @@ class ClassWithTwoTypeParametersWithMultipleGenericClassAndInterfaceBoundsAssign B extends Map & Iterable & Function> { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithTwoTypeParametersWithMultipleGenericClassAndInterfaceBoundsAssignedToConcreteTypes.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithTwoTypeParametersWithMultipleGenericClassAndInterfaceBoundsAssignedToConcreteTypes.class); assertThatType(javaClass).hasTypeParameters("A", "B") .hasTypeParameter("A") @@ -208,7 +210,7 @@ public void imports_single_type_bound_with_unbound_wildcard() { class ClassWithSingleTypeParameterBoundByTypeWithUnboundWildcard> { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithSingleTypeParameterBoundByTypeWithUnboundWildcard.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithSingleTypeParameterBoundByTypeWithUnboundWildcard.class); assertThatType(javaClass) .hasTypeParameter("T").withBoundsMatching(parameterizedType(List.class).withWildcardTypeParameter()); @@ -232,7 +234,7 @@ class ClassWithSingleTypeParameterBoundByTypeWithWildcardWithUpperInterfaceBound @Test @UseDataProvider public void test_imports_single_type_bound_with_upper_bound_wildcard(Class classWithWildcard, Class expectedUpperBound) { - JavaClass javaClass = new ClassFileImporter().importClass(classWithWildcard); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(classWithWildcard); assertThatType(javaClass) .hasTypeParameter("T").withBoundsMatching(parameterizedType(List.class).withWildcardTypeParameterWithUpperBound(expectedUpperBound)); @@ -256,7 +258,7 @@ class ClassWithSingleTypeParameterBoundByTypeWithWildcardWithLowerInterfaceBound @Test @UseDataProvider public void test_imports_single_type_bound_with_lower_bound_wildcard(Class classWithWildcard, Class expectedLowerBound) { - JavaClass javaClass = new ClassFileImporter().importClass(classWithWildcard); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(classWithWildcard); assertThatType(javaClass) .hasTypeParameter("T").withBoundsMatching(parameterizedType(List.class).withWildcardTypeParameterWithLowerBound(expectedLowerBound)); @@ -268,7 +270,7 @@ public void imports_multiple_type_bounds_with_multiple_wildcards_with_various_bo class ClassWithMultipleTypeParametersBoundByTypesWithDifferentBounds, B extends Reference & Map> { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithMultipleTypeParametersBoundByTypesWithDifferentBounds.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithMultipleTypeParametersBoundByTypesWithDifferentBounds.class); assertThatType(javaClass).hasTypeParameters("A", "B") .hasTypeParameter("A") @@ -297,7 +299,7 @@ public void references_type_variable_bound() { class ClassWithTypeParameterWithTypeVariableBound { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithTypeParameterWithTypeVariableBound.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithTypeParameterWithTypeVariableBound.class); assertThatType(javaClass).hasTypeParameters("U", "T", "V") .hasTypeParameter("U").withBoundsMatching(typeVariable("T").withUpperBounds(String.class)) @@ -317,7 +319,12 @@ class AndEvenMoreInner { } } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.EvenMoreInnerDeclaringOwn.AndEvenMoreInner.class); + JavaClass javaClass = importClassesWithOnlyGenericTypeResolution( + ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.EvenMoreInnerDeclaringOwn.AndEvenMoreInner.class, + ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.EvenMoreInnerDeclaringOwn.class, + ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.class, + ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.class + ).get(ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.EvenMoreInnerDeclaringOwn.AndEvenMoreInner.class); assertThatType(javaClass).hasTypeParameters("MOST_INNER1", "MOST_INNER2") .hasTypeParameter("MOST_INNER1") @@ -341,7 +348,7 @@ class EvenMoreInner { } } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithTypeParameterBoundByInnerClass.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithTypeParameterBoundByInnerClass.class); assertThatType(javaClass).hasTypeParameters("T", "U") .hasTypeParameter("T") @@ -368,7 +375,7 @@ class AndEvenMoreInner { @Override public JavaClass call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter().importClass( + return importClassWithOnlyGenericTypeResolution( ClassWithTypeParameterWithInnerClassesWithTypeVariableBound.SomeInner.EvenMoreInnerDeclaringOwn.AndEvenMoreInner.class); } }); @@ -394,9 +401,10 @@ class Level5 { } } - Class innermostClass = Class.forName(Level1.class.getName() + "$1Level3$1Level5"); + Class middleClass = Class.forName(Level1.class.getName() + "$1Level3"); + Class innermostClass = Class.forName(middleClass.getName() + "$1Level5"); - JavaClass javaClass = new ClassFileImporter().importClass(innermostClass); + JavaClass javaClass = importClassesWithOnlyGenericTypeResolution(innermostClass, middleClass, Level1.class).get(innermostClass); assertThatType(javaClass).hasTypeParameters("T51", "T52") .hasTypeParameter("T51") @@ -415,7 +423,7 @@ public void imports_wild_cards_bound_by_type_variables() { class ClassWithWildcardWithTypeVariableBounds, V extends List> { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithWildcardWithTypeVariableBounds.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithWildcardWithTypeVariableBounds.class); assertThatType(javaClass).hasTypeParameters("T", "U", "V") .hasTypeParameter("U") @@ -438,7 +446,11 @@ class MoreInner, MOST_INNER2 extends List< } } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithWildcardWithTypeVariableBounds.Inner.MoreInner.class); + JavaClass javaClass = importClassesWithOnlyGenericTypeResolution( + ClassWithWildcardWithTypeVariableBounds.Inner.MoreInner.class, + ClassWithWildcardWithTypeVariableBounds.Inner.class, + ClassWithWildcardWithTypeVariableBounds.class + ).get(ClassWithWildcardWithTypeVariableBounds.Inner.MoreInner.class); assertThatType(javaClass).hasTypeParameters("MOST_INNER1", "MOST_INNER2") .hasTypeParameter("MOST_INNER1") @@ -467,8 +479,7 @@ class MoreInner, MOST_INNER2 extends List< @Override public JavaClass call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter() - .importClasses(ClassWithWildcardWithTypeVariableBounds.Inner.MoreInner.class, List.class) + return importClassesWithOnlyGenericTypeResolution(ClassWithWildcardWithTypeVariableBounds.Inner.MoreInner.class, List.class) .get(ClassWithWildcardWithTypeVariableBounds.Inner.MoreInner.class); } }); @@ -499,7 +510,7 @@ class ClassWithComplexTypeParameters< RAW extends List> { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithComplexTypeParameters.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithComplexTypeParameters.class); assertThatType(javaClass) .hasTypeParameter("A") @@ -553,7 +564,7 @@ class ClassWithComplexTypeParametersWithConcreteArrayBounds< > { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithComplexTypeParametersWithConcreteArrayBounds.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithComplexTypeParametersWithConcreteArrayBounds.class); assertThatType(javaClass) .hasTypeParameter("A") @@ -585,7 +596,7 @@ class ClassWithTypeParameterWithParameterizedArrayBounds< > { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithTypeParameterWithParameterizedArrayBounds.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithTypeParameterWithParameterizedArrayBounds.class); assertThatType(javaClass) .hasTypeParameter("T") @@ -614,7 +625,7 @@ class ClassWithComplexTypeParametersWithGenericArrayBounds< > { } - JavaClass javaClass = new ClassFileImporter().importClass(ClassWithComplexTypeParametersWithGenericArrayBounds.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(ClassWithComplexTypeParametersWithGenericArrayBounds.class); assertThatType(javaClass) .hasTypeParameter("A") diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericCodeUnitParameterTypesTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericCodeUnitParameterTypesTest.java index 20c02b1925..8786c91f05 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericCodeUnitParameterTypesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericCodeUnitParameterTypesTest.java @@ -8,6 +8,7 @@ import java.util.Set; import java.util.concurrent.Callable; +import com.google.common.collect.FluentIterable; import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.domain.JavaCodeUnit; @@ -21,6 +22,7 @@ import org.junit.runner.RunWith; import static com.google.common.collect.Iterables.getOnlyElement; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcessTestUtils.importClassesWithOnlyGenericTypeResolution; import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThat; import static com.tngtech.archunit.testutil.Assertions.assertThatType; @@ -596,7 +598,9 @@ void method(ClassParameterWithSingleTypeParameter param) { return testCasesFromSameGenericSignatureOnConstructorAndMethod( OuterWithTypeParameter.SomeInner.GenericSignatureOnConstructor.class, - OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class + OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class, + OuterWithTypeParameter.SomeInner.class, + OuterWithTypeParameter.class ); } @@ -631,7 +635,7 @@ void method(ClassParameterWithSingleTypeParameter param) { @Override public JavaClasses call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter().importClasses( + return importClassesWithOnlyGenericTypeResolution( OuterWithTypeParameter.SomeInner.GenericSignatureOnConstructor.class, OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class); } @@ -671,7 +675,8 @@ void method(T4 param) { return testCasesFromSameGenericSignatureOnConstructorAndMethod( Class.forName(Level1.class.getName() + "$1GenericSignatureOnConstructor"), - Class.forName(Level1.class.getName() + "$1GenericSignatureOnMethod") + Class.forName(Level1.class.getName() + "$1GenericSignatureOnMethod"), + Level1.class ); } @@ -749,7 +754,9 @@ void method(ClassParameterWithTwoTypeParameters< return testCasesFromSameGenericSignatureOnConstructorAndMethod( OuterWithTypeParameter.SomeInner.GenericSignatureOnConstructor.class, - OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class + OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class, + OuterWithTypeParameter.SomeInner.class, + OuterWithTypeParameter.class ); } @@ -793,7 +800,7 @@ void method(ClassParameterWithTwoTypeParameters< @Override public JavaClasses call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter().importClasses( + return importClassesWithOnlyGenericTypeResolution( OuterWithTypeParameter.SomeInner.GenericSignatureOnConstructor.class, OuterWithTypeParameter.SomeInner.GenericSignatureOnMethod.class, ClassParameterWithSingleTypeParameter.class); @@ -1185,8 +1192,15 @@ public void test_imports_multiple_generic_code_unit_parameter_types(JavaCodeUnit // @formatter:on } - private static Object[][] testCasesFromSameGenericSignatureOnConstructorAndMethod(Class genericSignatureOnConstructor, Class genericSignatureOnMethod) { - JavaClasses classes = new ClassFileImporter().importClasses(genericSignatureOnConstructor, genericSignatureOnMethod); + @SuppressWarnings("rawtypes") + private static Object[][] testCasesFromSameGenericSignatureOnConstructorAndMethod( + Class genericSignatureOnConstructor, + Class genericSignatureOnMethod, + Class... additionalClasses + ) { + JavaClasses classes = importClassesWithOnlyGenericTypeResolution( + FluentIterable.from(additionalClasses).append(genericSignatureOnConstructor, genericSignatureOnMethod).toArray(Class.class) + ); return testForEach( getOnlyElement(classes.get(genericSignatureOnConstructor).getConstructors()), diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericFieldTypesTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericFieldTypesTest.java index 0411c75e32..60096a120c 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericFieldTypesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericFieldTypesTest.java @@ -16,6 +16,8 @@ import org.junit.Test; import org.junit.runner.RunWith; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcessTestUtils.importClassWithOnlyGenericTypeResolution; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcessTestUtils.importClassesWithOnlyGenericTypeResolution; import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThatType; import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteClass.concreteClass; @@ -38,7 +40,7 @@ class SomeClass { NonGenericFieldType field; } - JavaType fieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType fieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(fieldType).as("generic field type").matches(NonGenericFieldType.class); } @@ -53,7 +55,7 @@ class SomeClass { GenericFieldType field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type") .hasErasure(GenericFieldType.class) @@ -70,7 +72,7 @@ class SomeClass { GenericFieldType field; } - JavaType rawGenericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType rawGenericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(rawGenericFieldType).as("raw generic field type").matches(GenericFieldType.class); } @@ -85,7 +87,7 @@ class SomeClass { GenericFieldType field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type") .hasErasure(GenericFieldType.class) @@ -102,7 +104,7 @@ class SomeClass { GenericFieldType field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type") .hasErasure(GenericFieldType.class) @@ -119,7 +121,7 @@ class SomeClass { GenericFieldType field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type") .hasErasure(GenericFieldType.class) @@ -136,7 +138,7 @@ class SomeClass { GenericFieldType> field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -157,7 +159,7 @@ class SomeClass { InterfaceParameterWithSingleTypeParameter> field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -179,7 +181,7 @@ class SomeClass { GenericFieldType field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments(wildcardType()); } @@ -194,7 +196,7 @@ class SomeClass { GenericFieldType> field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -214,7 +216,7 @@ class SomeClass { ClassParameterWithSingleTypeParameter> field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -236,7 +238,7 @@ class SomeClass { ClassParameterWithSingleTypeParameter>> field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -257,7 +259,7 @@ class SomeClass { T field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type") .isInstanceOf(JavaTypeVariable.class) @@ -274,7 +276,7 @@ class SomeClass { GenericFieldType field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments(typeVariable("OF_CLASS")); } @@ -289,7 +291,7 @@ class SomeClass { GenericFieldType> field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -307,7 +309,7 @@ class SomeClass { GenericFieldType> field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -330,8 +332,11 @@ class SomeClass { } } - JavaType genericFieldType = new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.SomeClass.class) - .getField("field").getType(); + JavaType genericFieldType = importClassesWithOnlyGenericTypeResolution( + OuterWithTypeParameter.SomeInner.SomeClass.class, + OuterWithTypeParameter.SomeInner.class, + OuterWithTypeParameter.class + ).get(OuterWithTypeParameter.SomeInner.SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( typeVariable("OUTER").withUpperBounds(String.class) @@ -356,7 +361,7 @@ class SomeClass { @Override public JavaType call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.SomeClass.class) + return importClassWithOnlyGenericTypeResolution(OuterWithTypeParameter.SomeInner.SomeClass.class) .getField("field").getType(); } }); @@ -378,8 +383,9 @@ class Level3 { } Class innermostClass = Class.forName(Level1.class.getName() + "$1Level3"); - JavaType genericFieldType = new ClassFileImporter() - .importClass(innermostClass).getField("field").getType(); + JavaType genericFieldType = importClassesWithOnlyGenericTypeResolution( + innermostClass, Level1.class + ).get(innermostClass).getField("field").getType(); assertThatType(genericFieldType).as("generic field type") .matches( @@ -400,8 +406,7 @@ class SomeClass { ClassParameterWithSingleTypeParameter> field; } - JavaType genericFieldType = new ClassFileImporter() - .importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -429,8 +434,11 @@ class SomeClass { } } - JavaType genericFieldType = new ClassFileImporter() - .importClass(OuterWithTypeParameter.SomeInner.SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassesWithOnlyGenericTypeResolution( + OuterWithTypeParameter.SomeInner.SomeClass.class, + OuterWithTypeParameter.SomeInner.class, + OuterWithTypeParameter.class + ).get(OuterWithTypeParameter.SomeInner.SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -463,8 +471,7 @@ class SomeClass { @Override public JavaType call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter() - .importClasses(OuterWithTypeParameter.SomeInner.SomeClass.class, ClassParameterWithSingleTypeParameter.class) + return importClassesWithOnlyGenericTypeResolution(OuterWithTypeParameter.SomeInner.SomeClass.class, ClassParameterWithSingleTypeParameter.class) .get(OuterWithTypeParameter.SomeInner.SomeClass.class).getField("field").getType(); } }); @@ -498,7 +505,7 @@ class SomeClass>> field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); // @formatter:off assertThatType(genericFieldType).as("generic field type").hasActualTypeArguments( @@ -545,7 +552,7 @@ class SomeClass { GenericFieldType, Serializable>>>[] field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).matches( genericArray( @@ -576,7 +583,7 @@ class SomeClass { Map, Serializable[][]>>> field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments(Serializable[].class), @@ -601,7 +608,7 @@ class SomeClass { GenericFieldType[], List[][], List[][][]> field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).hasActualTypeArguments( genericArray(parameterizedTypeArrayName(List.class, String.class, 1)).withComponentType( @@ -623,7 +630,7 @@ class SomeClass { T[][] field2Dim; } - JavaClass javaClass = new ClassFileImporter().importClass(SomeClass.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(SomeClass.class); assertThatType(javaClass.getField("field").getType()).as("generic field type") .hasErasure(String[].class) @@ -650,7 +657,7 @@ class SomeClass { Map, X[][]>>> field; } - JavaType genericFieldType = new ClassFileImporter().importClass(SomeClass.class).getField("field").getType(); + JavaType genericFieldType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getField("field").getType(); assertThatType(genericFieldType).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments( diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericInterfacesTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericInterfacesTest.java index df793083a6..36f9b043bb 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericInterfacesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericInterfacesTest.java @@ -21,6 +21,8 @@ import org.junit.runner.RunWith; import static com.google.common.collect.Iterables.getOnlyElement; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcessTestUtils.importClassWithOnlyGenericTypeResolution; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcessTestUtils.importClassesWithOnlyGenericTypeResolution; import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThatType; import static com.tngtech.archunit.testutil.Assertions.assertThatTypes; @@ -41,7 +43,7 @@ public void imports_non_generic_interface() { class Child implements SomeInterface { } - Set genericInterfaces = new ClassFileImporter().importClass(Child.class).getInterfaces(); + Set genericInterfaces = importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces(); assertThatTypes(genericInterfaces).as("generic interfaces").matchExactly(SomeInterface.class); } @@ -51,7 +53,7 @@ public void imports_generic_interface_with_one_type_argument() { class Child implements InterfaceWithOneTypeParameter { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface") .hasErasure(InterfaceWithOneTypeParameter.class) @@ -64,7 +66,7 @@ public void imports_raw_generic_superclass_as_JavaClass_instead_of_JavaParameter class Child implements InterfaceWithOneTypeParameter { } - JavaType rawGenericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType rawGenericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(rawGenericInterface).as("raw generic interface").matches(InterfaceWithOneTypeParameter.class); } @@ -75,7 +77,7 @@ class Child implements InterfaceWithOneTypeParameter { } JavaType genericInterface = getOnlyElement( - new ClassFileImporter().importClass(Child.class).getInterfaces()); + importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface") .hasErasure(InterfaceWithOneTypeParameter.class) @@ -87,7 +89,7 @@ public void imports_generic_interface_with_primitive_array_type_argument() { class Child implements InterfaceWithOneTypeParameter { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface") .hasErasure(InterfaceWithOneTypeParameter.class) @@ -100,7 +102,7 @@ public void imports_generic_interface_with_multiple_type_arguments() { class Child implements InterfaceWithThreeTypeParameters { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface") .hasErasure(InterfaceWithThreeTypeParameters.class) @@ -112,7 +114,7 @@ public void imports_generic_interface_with_single_actual_type_argument_parameter class Child implements InterfaceWithOneTypeParameter> { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -128,7 +130,7 @@ class Child implements InterfaceWithThreeTypeParameters< InterfaceWithOneTypeParameter> { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -145,7 +147,7 @@ public void imports_generic_interface_with_single_actual_type_argument_parameter class Child implements InterfaceWithOneTypeParameter> { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -160,7 +162,7 @@ class Child implements InterfaceWithTwoTypeParameters< ClassParameterWithSingleTypeParameter> { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -177,7 +179,7 @@ class Child implements InterfaceWithTwoTypeParameters< ClassParameterWithSingleTypeParameter>> { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -196,7 +198,7 @@ public void imports_generic_interface_parameterized_with_type_variable() { class Child implements InterfaceWithOneTypeParameter { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments(typeVariable("SUB")); } @@ -206,7 +208,7 @@ public void imports_generic_interface_with_actual_type_argument_parameterized_wi class Child implements InterfaceWithOneTypeParameter> { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -219,7 +221,7 @@ public void references_type_variable_assigned_to_actual_type_argument_of_generic class Child implements InterfaceWithOneTypeParameter> { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -237,7 +239,12 @@ class Child implements InterfaceWithOneTypeParameter { } } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement( + importClassesWithOnlyGenericTypeResolution( + OuterWithTypeParameter.SomeInner.Child.class, + OuterWithTypeParameter.SomeInner.class, + OuterWithTypeParameter.class + ).get(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( typeVariable("OUTER").withUpperBounds(String.class) @@ -258,7 +265,7 @@ class Child implements InterfaceWithOneTypeParameter { @Override public JavaType call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return getOnlyElement(new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); + return getOnlyElement(importClassWithOnlyGenericTypeResolution(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); } }); @@ -278,7 +285,8 @@ class Level3 implements InterfaceWithOneTypeParameter { } Class innermostClass = Class.forName(Level1.class.getName() + "$1Level3"); - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(innermostClass).getInterfaces()); + JavaType genericInterface = getOnlyElement( + importClassesWithOnlyGenericTypeResolution(innermostClass, Level1.class).get(innermostClass).getInterfaces()); assertThatType(genericInterface).as("generic interface") .hasActualTypeArguments( @@ -294,7 +302,7 @@ class Child implements Interf ClassParameterWithSingleTypeParameter> { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -318,7 +326,12 @@ class Child implements InterfaceWithTwoTypeParameters< } } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement( + importClassesWithOnlyGenericTypeResolution( + OuterWithTypeParameter.SomeInner.Child.class, + OuterWithTypeParameter.SomeInner.class, + OuterWithTypeParameter.class + ).get(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -346,9 +359,9 @@ class Child implements InterfaceWithTwoTypeParameters< @Override public JavaType call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return getOnlyElement(new ClassFileImporter() - .importClasses(OuterWithTypeParameter.SomeInner.Child.class, ClassParameterWithSingleTypeParameter.class) - .get(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); + return getOnlyElement( + importClassesWithOnlyGenericTypeResolution(OuterWithTypeParameter.SomeInner.Child.class, ClassParameterWithSingleTypeParameter.class) + .get(OuterWithTypeParameter.SomeInner.Child.class).getInterfaces()); } }); @@ -399,7 +412,7 @@ public static Object[][] data_imports_complex_type_with_multiple_nested_actual_t @Test @UseDataProvider public void test_imports_complex_type_with_multiple_nested_actual_type_arguments_of_generic_interface_with_self_referencing_type_definitions(Class testInput) { - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(testInput).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(testInput).getInterfaces()); // @formatter:off assertThatType(genericInterface).as("generic interface").hasActualTypeArguments( @@ -461,7 +474,7 @@ public static Object[][] data_imports_complex_type_with_multiple_nested_actual_t @Test @UseDataProvider public void test_imports_complex_type_with_multiple_nested_actual_type_arguments_of_generic_interface_with_concrete_array_bounds(Class testInput) { - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(testInput).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(testInput).getInterfaces()); assertThatType(genericInterface).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments(Serializable[].class), @@ -494,7 +507,7 @@ public static Object[][] data_imports_type_of_generic_interface_with_parameteriz @Test @UseDataProvider public void test_imports_type_of_generic_interface_with_parameterized_array_bounds(Class testInput) { - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(testInput).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(testInput).getInterfaces()); assertThatType(genericInterface).hasActualTypeArguments( genericArray(parameterizedTypeArrayName(List.class, String.class, 1)).withComponentType( @@ -533,7 +546,7 @@ public static Object[][] data_imports_complex_type_with_multiple_nested_actual_t @Test @UseDataProvider public void test_imports_complex_type_with_multiple_nested_actual_type_arguments_of_generic_interface_with_generic_array_bounds(Class testInput) { - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(testInput).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(testInput).getInterfaces()); assertThatType(genericInterface).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments( @@ -586,7 +599,7 @@ public static Object[][] data_imports_multiple_generic_interfaces() { @Test @UseDataProvider public void test_imports_multiple_generic_interfaces(Class testInput) { - JavaClass child = new ClassFileImporter().importClass(testInput); + JavaClass child = importClassWithOnlyGenericTypeResolution(testInput); assertThatType(getGenericInterface(child, InterfaceWithOneTypeParameter.class)).as("generic interface") .hasActualTypeArguments(Path.class); @@ -613,7 +626,7 @@ class Child extends BaseClass implements InterfaceWithOneTypeParameter, InterfaceWithTwoTypeParameters { } - JavaClass child = new ClassFileImporter().importClass(Child.class); + JavaClass child = importClassWithOnlyGenericTypeResolution(Child.class); assertThatType(child.getSuperclass().get()) .hasErasure(BaseClass.class) @@ -633,7 +646,7 @@ class Child implements SomeDeeplyNestedInterface> { } - JavaType genericInterface = getOnlyElement(new ClassFileImporter().importClass(Child.class).getInterfaces()); + JavaType genericInterface = getOnlyElement(importClassWithOnlyGenericTypeResolution(Child.class).getInterfaces()); assertThatType(genericInterface).as("generic interface") .hasErasure(SomeDeeplyNestedInterface.class) diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodReturnTypesTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodReturnTypesTest.java index a4a710d75b..99f51556fe 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodReturnTypesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodReturnTypesTest.java @@ -16,6 +16,8 @@ import org.junit.Test; import org.junit.runner.RunWith; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcessTestUtils.importClassWithOnlyGenericTypeResolution; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcessTestUtils.importClassesWithOnlyGenericTypeResolution; import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThatType; import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteClass.concreteClass; @@ -40,7 +42,7 @@ NonGenericReturnType method() { } } - JavaType returnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType returnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(returnType).as("return type").matches(NonGenericReturnType.class); } @@ -58,7 +60,7 @@ int primitive(T irrelevant) { } } - JavaClass javaClass = new ClassFileImporter().importClass(SomeClass.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(SomeClass.class); JavaType returnType = javaClass.getMethod("method", Object.class).getReturnType(); assertThatType(returnType).as("return type").matches(Object.class); @@ -79,7 +81,7 @@ GenericReturnType method() { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type") .hasErasure(GenericReturnType.class) @@ -98,7 +100,7 @@ GenericReturnType method() { } } - JavaType rawGenericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType rawGenericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(rawGenericReturnType).as("raw generic method return type").matches(GenericReturnType.class); } @@ -115,7 +117,7 @@ GenericReturnType method() { } } - JavaType genericMethodReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericMethodReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericMethodReturnType).as("generic method return type") .hasErasure(GenericReturnType.class) @@ -134,7 +136,7 @@ GenericReturnType method() { } } - JavaType genericMethodReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericMethodReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericMethodReturnType).as("generic method return type") .hasErasure(GenericReturnType.class) @@ -153,7 +155,7 @@ GenericReturnType method() { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type") .hasErasure(GenericReturnType.class) @@ -172,7 +174,7 @@ GenericReturnType> method() { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -195,7 +197,7 @@ InterfaceParameterWithSingleTypeParameter> method() { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -219,7 +221,7 @@ GenericReturnType method() { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments(wildcardType()); } @@ -236,7 +238,7 @@ GenericReturnType> method() { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -258,7 +260,7 @@ ClassParameterWithSingleTypeParameter> method() { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -282,7 +284,7 @@ ClassParameterWithSingleTypeParameter>> method() { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -305,7 +307,7 @@ T method() { } } - JavaType genericMethodReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericMethodReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericMethodReturnType).as("generic method return type") .isInstanceOf(JavaTypeVariable.class) @@ -324,7 +326,7 @@ GenericReturnType method() { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments(typeVariable("OF_CLASS")); } @@ -341,7 +343,7 @@ GenericReturnType> method() { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -361,7 +363,7 @@ GenericReturnType> method() { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -386,8 +388,11 @@ GenericReturnType method() { } } - JavaType genericReturnType = new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.SomeClass.class) - .getMethod("method").getReturnType(); + JavaType genericReturnType = importClassesWithOnlyGenericTypeResolution( + OuterWithTypeParameter.SomeInner.SomeClass.class, + OuterWithTypeParameter.SomeInner.class, + OuterWithTypeParameter.class + ).get(OuterWithTypeParameter.SomeInner.SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( typeVariable("OUTER").withUpperBounds(String.class) @@ -414,7 +419,7 @@ GenericReturnType method() { @Override public JavaType call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.SomeClass.class) + return importClassWithOnlyGenericTypeResolution(OuterWithTypeParameter.SomeInner.SomeClass.class) .getMethod("method").getReturnType(); } }); @@ -438,8 +443,9 @@ T4 method() { } Class innermostClass = Class.forName(Level1.class.getName() + "$1Level3"); - JavaType genericReturnType = new ClassFileImporter() - .importClass(innermostClass).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassesWithOnlyGenericTypeResolution( + innermostClass, Level1.class + ).get(innermostClass).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type") .matches( @@ -463,8 +469,7 @@ ClassParameterWithSingleTypeParameter> method() { } } - JavaType genericReturnType = new ClassFileImporter() - .importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -494,8 +499,11 @@ ClassParameterWithSingleTypeParameter> method() { } } - JavaType genericReturnType = new ClassFileImporter() - .importClass(OuterWithTypeParameter.SomeInner.SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassesWithOnlyGenericTypeResolution( + OuterWithTypeParameter.SomeInner.SomeClass.class, + OuterWithTypeParameter.SomeInner.class, + OuterWithTypeParameter.class + ).get(OuterWithTypeParameter.SomeInner.SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -530,7 +538,7 @@ ClassParameterWithSingleTypeParameter> method() { @Override public JavaType call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter().importClasses(OuterWithTypeParameter.SomeInner.SomeClass.class, ClassParameterWithSingleTypeParameter.class) + return importClassesWithOnlyGenericTypeResolution(OuterWithTypeParameter.SomeInner.SomeClass.class, ClassParameterWithSingleTypeParameter.class) .get(OuterWithTypeParameter.SomeInner.SomeClass.class) .getMethod("method").getReturnType(); } @@ -567,7 +575,7 @@ Comparable>> method() { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); // @formatter:off assertThatType(genericReturnType).as("generic return type").hasActualTypeArguments( @@ -616,7 +624,7 @@ class SomeClass { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).matches( genericArray( @@ -649,7 +657,7 @@ class SomeClass { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments(Serializable[].class), @@ -676,7 +684,7 @@ GenericReturnType[], List[][], List[][][]> me } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).hasActualTypeArguments( genericArray(parameterizedTypeArrayName(List.class, String.class, 1)).withComponentType( @@ -699,7 +707,7 @@ abstract class SomeClass { abstract T[][] method2Dim(); } - JavaClass javaClass = new ClassFileImporter().importClass(SomeClass.class); + JavaClass javaClass = importClassWithOnlyGenericTypeResolution(SomeClass.class); assertThatType(javaClass.getMethod("method").getReturnType()) .hasErasure(String[].class) @@ -729,7 +737,7 @@ class SomeClass { } } - JavaType genericReturnType = new ClassFileImporter().importClass(SomeClass.class).getMethod("method").getReturnType(); + JavaType genericReturnType = importClassWithOnlyGenericTypeResolution(SomeClass.class).getMethod("method").getReturnType(); assertThatType(genericReturnType).hasActualTypeArguments( genericArray("X[]").withComponentType( diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodSignaturesTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodSignaturesTest.java index 2d3f426e67..46004beb39 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodSignaturesTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericMethodSignaturesTest.java @@ -9,6 +9,7 @@ import java.util.Set; import java.util.concurrent.Callable; +import com.google.common.collect.FluentIterable; import com.tngtech.archunit.ArchConfiguration; import com.tngtech.archunit.base.Function; import com.tngtech.archunit.core.domain.JavaClasses; @@ -20,6 +21,7 @@ import org.junit.runner.RunWith; import static com.google.common.collect.Iterables.getOnlyElement; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcessTestUtils.importClassesWithOnlyGenericTypeResolution; import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThat; import static com.tngtech.archunit.testutil.Assertions.assertThatCodeUnit; @@ -553,7 +555,10 @@ void genericSignatureOn } return testCasesFromSameGenericSignatureOnConstructorAndMethod( Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnConstructor.class, - Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnMethod.class + Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnMethod.class, + Outer.SomeInner.EvenMoreInnerDeclaringOwn.class, + Outer.SomeInner.class, + Outer.class ); } @@ -640,7 +645,7 @@ void genericSignatureOn @Override public JavaClasses call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter().importClasses( + return importClassesWithOnlyGenericTypeResolution( Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnConstructor.class, Outer.SomeInner.EvenMoreInnerDeclaringOwn.GenericSignatureOnMethod.class); } @@ -682,10 +687,12 @@ void genericSignatureOnMethod() { } } } - String level3ClassName = Level1.class.getName() + "$1Level3"; + Class level3Class = Class.forName(Level1.class.getName() + "$1Level3"); return testCasesFromSameGenericSignatureOnConstructorAndMethod( - Class.forName(level3ClassName + "$GenericSignatureOnConstructor"), - Class.forName(level3ClassName + "$GenericSignatureOnMethod")); + Class.forName(level3Class.getName() + "$GenericSignatureOnConstructor"), + Class.forName(level3Class.getName() + "$GenericSignatureOnMethod"), + level3Class, + Level1.class); } @Test @@ -753,7 +760,9 @@ , MOST_INNER2 extends List> voi } return testCasesFromSameGenericSignatureOnConstructorAndMethod( Outer.Inner.GenericSignatureOnConstructor.class, - Outer.Inner.GenericSignatureOnMethod.class + Outer.Inner.GenericSignatureOnMethod.class, + Outer.Inner.class, + Outer.class ); } @@ -796,7 +805,7 @@ , MOST_INNER2 extends List> voi @Override public JavaClasses call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter().importClasses( + return importClassesWithOnlyGenericTypeResolution( Outer.Inner.GenericSignatureOnConstructor.class, Outer.Inner.GenericSignatureOnMethod.class, List.class); @@ -1038,8 +1047,15 @@ public void test_imports_complex_type_with_multiple_nested_parameters_with_gener ); } - private static Object[][] testCasesFromSameGenericSignatureOnConstructorAndMethod(Class genericSignatureOnConstructor, Class genericSignatureOnMethod) { - JavaClasses classes = new ClassFileImporter().importClasses(genericSignatureOnConstructor, genericSignatureOnMethod); + @SuppressWarnings("rawtypes") + private static Object[][] testCasesFromSameGenericSignatureOnConstructorAndMethod( + Class genericSignatureOnConstructor, + Class genericSignatureOnMethod, + Class... additionalClasses + ) { + JavaClasses classes = importClassesWithOnlyGenericTypeResolution( + FluentIterable.from(additionalClasses).append(genericSignatureOnConstructor, genericSignatureOnMethod).toArray(Class.class) + ); return testForEach( getOnlyElement(classes.get(genericSignatureOnConstructor).getConstructors()), diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericSuperclassTest.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericSuperclassTest.java index 57fc141ea7..9d8235662e 100644 --- a/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericSuperclassTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/ClassFileImporterGenericSuperclassTest.java @@ -14,6 +14,8 @@ import org.junit.Test; import org.junit.runner.RunWith; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcessTestUtils.importClassWithOnlyGenericTypeResolution; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcessTestUtils.importClassesWithOnlyGenericTypeResolution; import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; import static com.tngtech.archunit.testutil.Assertions.assertThatType; import static com.tngtech.archunit.testutil.assertion.ExpectedConcreteType.ExpectedConcreteClass.concreteClass; @@ -34,7 +36,7 @@ class BaseClass { class Child extends BaseClass { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").matches(BaseClass.class); } @@ -47,7 +49,7 @@ class BaseClass { class Child extends BaseClass { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass") .hasErasure(BaseClass.class) @@ -63,7 +65,7 @@ class BaseClass { class Child extends BaseClass { } - JavaType rawGenericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType rawGenericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(rawGenericSuperclass).as("raw generic superclass").matches(BaseClass.class); } @@ -76,7 +78,7 @@ class BaseClass { class Child extends BaseClass { } - JavaType genericSuperClass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperClass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperClass).as("generic superclass") .hasErasure(BaseClass.class) @@ -91,7 +93,7 @@ class BaseClass { class Child extends BaseClass { } - JavaType genericSuperClass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperClass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperClass).as("generic superclass") .hasErasure(BaseClass.class) @@ -107,7 +109,7 @@ class BaseClass { class Child extends BaseClass { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass") .hasErasure(BaseClass.class) @@ -122,7 +124,7 @@ class BaseClass { class Child extends BaseClass> { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -141,7 +143,7 @@ class Child extends BaseClass< InterfaceParameterWithSingleTypeParameter> { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -161,7 +163,7 @@ class BaseClass { class Child extends BaseClass> { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -179,7 +181,7 @@ class Child extends BaseClass< ClassParameterWithSingleTypeParameter> { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -199,7 +201,7 @@ class Child extends BaseClass< ClassParameterWithSingleTypeParameter>> { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -221,7 +223,7 @@ class BaseClass { class Child extends BaseClass { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments(typeVariable("SUB")); } @@ -234,7 +236,7 @@ class BaseClass { class Child extends BaseClass> { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -250,7 +252,7 @@ class BaseClass { class Child extends BaseClass> { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -272,7 +274,11 @@ class Child extends BaseClass { } } - JavaType genericSuperclass = new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassesWithOnlyGenericTypeResolution( + OuterWithTypeParameter.SomeInner.Child.class, + OuterWithTypeParameter.SomeInner.class, + OuterWithTypeParameter.class + ).get(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( typeVariable("OUTER").withUpperBounds(String.class) @@ -297,7 +303,7 @@ class Child extends BaseClass { @Override public JavaType call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); + return importClassWithOnlyGenericTypeResolution(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); } }); ; @@ -320,7 +326,9 @@ class Level3 extends BaseClass { } Class innermostClass = Class.forName(Level1.class.getName() + "$1Level3"); - JavaType genericSuperclass = new ClassFileImporter().importClass(innermostClass).getSuperclass().get(); + JavaType genericSuperclass = importClassesWithOnlyGenericTypeResolution( + innermostClass, Level1.class + ).get(innermostClass).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass") .hasActualTypeArguments( @@ -340,7 +348,7 @@ class Child extends BaseClass ClassParameterWithSingleTypeParameter> { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -368,7 +376,11 @@ class Child extends BaseClass< } } - JavaType genericSuperclass = new ClassFileImporter().importClass(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassesWithOnlyGenericTypeResolution( + OuterWithTypeParameter.SomeInner.Child.class, + OuterWithTypeParameter.SomeInner.class, + OuterWithTypeParameter.class + ).get(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( parameterizedType(ClassParameterWithSingleTypeParameter.class) @@ -400,8 +412,7 @@ class Child extends BaseClass< @Override public JavaType call() { ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false); - return new ClassFileImporter() - .importClasses(OuterWithTypeParameter.SomeInner.Child.class, ClassParameterWithSingleTypeParameter.class) + return importClassesWithOnlyGenericTypeResolution(OuterWithTypeParameter.SomeInner.Child.class, ClassParameterWithSingleTypeParameter.class) .get(OuterWithTypeParameter.SomeInner.Child.class).getSuperclass().get(); } }); @@ -434,7 +445,7 @@ class Child>> { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); // @formatter:off assertThatType(genericSuperclass).as("generic superclass").hasActualTypeArguments( @@ -485,7 +496,7 @@ class Child extends BaseClass< > { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments(Serializable[].class), @@ -508,7 +519,7 @@ class BaseClass { class Child extends BaseClass[], List[][], List[][][]> { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).hasActualTypeArguments( genericArray(parameterizedTypeArrayName(List.class, String.class, 1)).withComponentType( @@ -536,7 +547,7 @@ class Child extends BaseClass< > { } - JavaType genericSuperclass = new ClassFileImporter().importClass(Child.class).getSuperclass().get(); + JavaType genericSuperclass = importClassWithOnlyGenericTypeResolution(Child.class).getSuperclass().get(); assertThatType(genericSuperclass).hasActualTypeArguments( parameterizedType(List.class).withTypeArguments( diff --git a/archunit/src/test/java/com/tngtech/archunit/core/importer/DependencyResolutionProcessTestUtils.java b/archunit/src/test/java/com/tngtech/archunit/core/importer/DependencyResolutionProcessTestUtils.java new file mode 100644 index 0000000000..bac49ad713 --- /dev/null +++ b/archunit/src/test/java/com/tngtech/archunit/core/importer/DependencyResolutionProcessTestUtils.java @@ -0,0 +1,92 @@ +package com.tngtech.archunit.core.importer; + +import java.util.Set; +import java.util.concurrent.Callable; + +import com.google.common.collect.ImmutableSet; +import com.tngtech.archunit.ArchConfiguration; +import com.tngtech.archunit.base.Optional; +import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.domain.JavaClasses; + +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.collect.Sets.newHashSet; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.DEPENDENCY_RESOLUTION_PROCESS_PROPERTY_PREFIX; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_ENCLOSING_TYPES_PROPERTY_NAME; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_DEFAULT_VALUE; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_MEMBER_TYPES_PROPERTY_NAME; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_SUPERTYPES_PROPERTY_NAME; +import static com.tngtech.archunit.testutil.ArchConfigurationRule.resetConfigurationAround; +import static java.util.Collections.singleton; + +public class DependencyResolutionProcessTestUtils { + + static JavaClasses importClassesWithOnlyGenericTypeResolution(final Class... classes) { + return ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept( + MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME, MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_DEFAULT_VALUE + ).importClasses(classes); + } + + static JavaClass importClassWithOnlyGenericTypeResolution(final Class clazz) { + return importClassesWithOnlyGenericTypeResolution(clazz).get(clazz); + } + + static class ImporterWithAdjustedResolutionRuns { + private final Set propertyNames; + private final Optional number; + + private ImporterWithAdjustedResolutionRuns(Set propertyNames, Optional number) { + this.propertyNames = propertyNames; + this.number = number; + } + + static ImporterWithAdjustedResolutionRuns disableAllIterationsExcept(String... propertyNames) { + return new ImporterWithAdjustedResolutionRuns(ImmutableSet.copyOf(propertyNames), Optional.empty()); + } + + static ImporterWithAdjustedResolutionRuns disableAllIterationsExcept(String propertyName, int number) { + return new ImporterWithAdjustedResolutionRuns(singleton(propertyName), Optional.of(number)); + } + + JavaClass importClass(final Class clazz) { + return importClasses(clazz).get(clazz); + } + + public JavaClasses importClasses(final Class... classes) { + return resetConfigurationAround(new Callable() { + @Override + public JavaClasses call() { + ImporterWithAdjustedResolutionRuns.this.setAllIterationsToZeroExcept(propertyNames); + return new ClassFileImporter().importClasses(classes); + } + }); + } + + private void setAllIterationsToZeroExcept(Set propertyNames) { + Set allPropertyNames = newHashSet( + MAX_ITERATIONS_FOR_MEMBER_TYPES_PROPERTY_NAME, + MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME, + MAX_ITERATIONS_FOR_SUPERTYPES_PROPERTY_NAME, + MAX_ITERATIONS_FOR_ENCLOSING_TYPES_PROPERTY_NAME, + MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME, + MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME + ); + allPropertyNames.removeAll(propertyNames); + + for (String propertyNameToDisable : allPropertyNames) { + setResolutionProperty(propertyNameToDisable, 0); + } + + if (number.isPresent()) { + setResolutionProperty(getOnlyElement(propertyNames), number.get()); + } + } + + private void setResolutionProperty(String propertyName, int number) { + ArchConfiguration.get().setProperty(DEPENDENCY_RESOLUTION_PROCESS_PROPERTY_PREFIX + "." + propertyName, String.valueOf(number)); + } + } +} \ No newline at end of file diff --git a/docs/userguide/010_Advanced_Configuration.adoc b/docs/userguide/010_Advanced_Configuration.adoc index 928d7154da..fdeb839fed 100644 --- a/docs/userguide/010_Advanced_Configuration.adoc +++ b/docs/userguide/010_Advanced_Configuration.adoc @@ -79,6 +79,49 @@ classResolver.args=myArgOne,myArgTwo For further details, compare the sources of `SelectedClassResolverFromClasspath`. +==== Configuring the Number of Resolution Iterations + +It is also possible to apply a more fine-grained configuration to the import dependency resolution behavior. + +In particular, the ArchUnit importer distinguishes 6 types of import dependencies: + +- member types (e.g. the type of a field of a class, type of a method parameter, etc.) +- accesses to types (e.g. a method calls another method of a different class) +- supertypes (i.e. superclasses that are extended and interfaces that are implemented) +- enclosing types (i.e. outer classes of nested classes) +- annotation types, including parameters of annotations +- generic signature types (i.e. types that comprise a parameterized type, like `List` and `String` for `List`) + +For each of these dependency types it is possible to configure the maximum number of iterations to traverse the dependencies. +E.g. let us assume we configure the maximum iterations as `2` for member types and we have a class `A` that declares a field of type `B` which in turn holds a field of type `C`. +On the first run we would automatically resolve the type `B` as a dependency of `A`. +On the second run we would then also resolve `C` as a member type dependency of `B`. + +The configuration parameters with their defaults are the following: + +[source,options="nowrap"] +.archunit.properties +---- +import.dependencyResolutionProcess.maxIterationsForMemberTypes = 1 +import.dependencyResolutionProcess.maxIterationsForAccessesToTypes = 1 +import.dependencyResolutionProcess.maxIterationsForSupertypes = -1 +import.dependencyResolutionProcess.maxIterationsForEnclosingTypes = -1 +import.dependencyResolutionProcess.maxIterationsForAnnotationTypes = -1 +import.dependencyResolutionProcess.maxIterationsForGenericSignatureTypes = -1 +---- + +Note that setting a property to a negative value (e.g. `-1`) will not stop the resolution until all types for the given sort of dependency have been resolved. +E.g. for generic type signatures a value of `-1` will by default lead to all types comprising the signature `Map>` (that is `Map`, `List` and `String`) to be automatically resolved. + +Setting a property to `0` will disable the automatic resolution, +which can lead to types being only partially imported or stubbed. +For these types the information is likely not complete or even wrong (e.g. an interface might be reported as class if the bytecode was never analyzed, or annotations might be missing). + +On the other hand, bigger (or negative) values can have a massive performance impact. +The defaults should provide a reasonable behavior. +They include the class graph for all types that are used by members or accesses directly and cut the resolution at that point. +However, relevant information for these types is fully imported, no matter how many iterations it takes (e.g. supertypes or generic signatures). + === MD5 Sums of Classes Sometimes it can be valuable to record the MD5 sums of classes being imported to track From efdc6030472ebf6d649ae5859992ea86157e617f Mon Sep 17 00:00:00 2001 From: Peter Gafert Date: Sun, 6 Feb 2022 10:48:40 +0700 Subject: [PATCH 22/22] disable enclosing and generic type resolution by default in tests Unfortunately, this costs a lot of performance (the build time increased by 50%). This is not due to the resolution being terribly slow (in fact, when I compared it on big imports the performance even went up a little due to the new resolution mechanism), but because many tiny tests import `java.util.List`, etc., and each local class used in tests now leads to the test class being imported as enclosing class, including all of its dependencies. Thus, for these tests the number of imported classes simply increases quite a bit. Since this automatic resolution is irrelevant for most tests I have thus disabled it by default. Tests can still explicitly import the types of generic signatures or enclosing classes, or explicitly reconfigure the resolution behavior. Signed-off-by: Peter Gafert --- .../src/test/resources/archunit.properties | 2 ++ .../src/test/resources/archunit.properties | 2 ++ .../src/test/resources/archunit.properties | 2 ++ ...ssFileImporterAutomaticResolutionTest.java | 20 +++++++++++++------ .../elements/ClassesShouldEvaluator.java | 11 +--------- .../src/test/resources/archunit.properties | 2 ++ 6 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 archunit-junit/junit4/src/test/resources/archunit.properties create mode 100644 archunit-junit/junit5/engine/src/test/resources/archunit.properties diff --git a/archunit-integration-test/src/test/resources/archunit.properties b/archunit-integration-test/src/test/resources/archunit.properties index f3ea70a114..8446436a78 100644 --- a/archunit-integration-test/src/test/resources/archunit.properties +++ b/archunit-integration-test/src/test/resources/archunit.properties @@ -1,3 +1,5 @@ +import.dependencyResolutionProcess.maxIterationsForEnclosingTypes=0 +import.dependencyResolutionProcess.maxIterationsForGenericSignatureTypes=0 extension.archunit-example-extension.enabled=false extension.archunit-example-extension.example-prop=exampleValue freeze.store.default.path=../archunit-example/example-plain/src/test/resources/frozen diff --git a/archunit-junit/junit4/src/test/resources/archunit.properties b/archunit-junit/junit4/src/test/resources/archunit.properties new file mode 100644 index 0000000000..d24b5e9b45 --- /dev/null +++ b/archunit-junit/junit4/src/test/resources/archunit.properties @@ -0,0 +1,2 @@ +import.dependencyResolutionProcess.maxIterationsForEnclosingTypes=0 +import.dependencyResolutionProcess.maxIterationsForGenericSignatureTypes=0 \ No newline at end of file diff --git a/archunit-junit/junit5/engine/src/test/resources/archunit.properties b/archunit-junit/junit5/engine/src/test/resources/archunit.properties new file mode 100644 index 0000000000..d24b5e9b45 --- /dev/null +++ b/archunit-junit/junit5/engine/src/test/resources/archunit.properties @@ -0,0 +1,2 @@ +import.dependencyResolutionProcess.maxIterationsForEnclosingTypes=0 +import.dependencyResolutionProcess.maxIterationsForGenericSignatureTypes=0 \ No newline at end of file diff --git a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java index 3512c4aebb..85c00bf291 100644 --- a/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java +++ b/archunit/src/jdk9test/java/com/tngtech/archunit/core/importer/ClassFileImporterAutomaticResolutionTest.java @@ -53,7 +53,9 @@ import static com.google.common.collect.Iterables.getOnlyElement; import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_ACCESSES_TO_TYPES_PROPERTY_NAME; import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_ANNOTATION_TYPES_PROPERTY_NAME; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_ENCLOSING_TYPES_DEFAULT_VALUE; import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_ENCLOSING_TYPES_PROPERTY_NAME; +import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_DEFAULT_VALUE; import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME; import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_MEMBER_TYPES_PROPERTY_NAME; import static com.tngtech.archunit.core.importer.DependencyResolutionProcess.MAX_ITERATIONS_FOR_SUPERTYPES_PROPERTY_NAME; @@ -597,7 +599,8 @@ class Innermost { } Class lessInnerClass = Class.forName(Outermost.LessOuter.LeastOuter.class.getName() + "$1LessInner"); - JavaClass innermost = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_ENCLOSING_TYPES_PROPERTY_NAME) + JavaClass innermost = ImporterWithAdjustedResolutionRuns + .disableAllIterationsExcept(MAX_ITERATIONS_FOR_ENCLOSING_TYPES_PROPERTY_NAME, MAX_ITERATIONS_FOR_ENCLOSING_TYPES_DEFAULT_VALUE) .importClass(Class.forName(lessInnerClass.getName() + "$Innermost")); JavaClass lessInner = innermost.getEnclosingClass().get(); @@ -767,32 +770,37 @@ public void test_automatically_resolves_parameterized_generic_type_bounds( } private static JavaType importFirstTypeParameterClassBound(Class clazz) { - JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME) + JavaClass javaClass = ImporterWithAdjustedResolutionRuns + .disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME, MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_DEFAULT_VALUE) .importClass(clazz); return getOnlyElement(getOnlyElement(javaClass.getTypeParameters()).getBounds()); } private static JavaType importFirstTypeParameterMethodBound(Class clazz) { - JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME) + JavaClass javaClass = ImporterWithAdjustedResolutionRuns + .disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME, MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_DEFAULT_VALUE) .importClass(clazz); return getOnlyElement(getOnlyElement(javaClass.getMethod("method").getTypeParameters()).getBounds()); } private static JavaType importFirstTypeArgumentFieldBound(Class clazz) { - JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME) + JavaClass javaClass = ImporterWithAdjustedResolutionRuns + .disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME, MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_DEFAULT_VALUE) .importClass(clazz); return getFirstTypeArgumentUpperBound(javaClass.getField("field").getType()); } private static JavaType importFirstTypeArgumentMethodParameterBound(Class clazz) { - JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME) + JavaClass javaClass = ImporterWithAdjustedResolutionRuns + .disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME, MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_DEFAULT_VALUE) .importClass(clazz); JavaMethod method = getOnlyElement(javaClass.getMethods()); return getFirstTypeArgumentUpperBound(method.getParameterTypes().get(0)); } private static JavaType importFirstTypeArgumentConstructorParameterBound(Class clazz) { - JavaClass javaClass = ImporterWithAdjustedResolutionRuns.disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME) + JavaClass javaClass = ImporterWithAdjustedResolutionRuns + .disableAllIterationsExcept(MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_PROPERTY_NAME, MAX_ITERATIONS_FOR_GENERIC_SIGNATURE_TYPES_DEFAULT_VALUE) .importClass(clazz); JavaConstructor constructor = getOnlyElement(javaClass.getConstructors()); return getFirstTypeArgumentUpperBound(constructor.getParameterTypes().get(0)); diff --git a/archunit/src/test/java/com/tngtech/archunit/lang/syntax/elements/ClassesShouldEvaluator.java b/archunit/src/test/java/com/tngtech/archunit/lang/syntax/elements/ClassesShouldEvaluator.java index 84c650f8fa..2479d598fd 100644 --- a/archunit/src/test/java/com/tngtech/archunit/lang/syntax/elements/ClassesShouldEvaluator.java +++ b/archunit/src/test/java/com/tngtech/archunit/lang/syntax/elements/ClassesShouldEvaluator.java @@ -64,12 +64,7 @@ private boolean anyLineMatches(List relevantFailures, JavaClass clazz) { private List getRelevantFailures(JavaClasses classes) { List relevant = new ArrayList<>(); for (String line : linesIn(rule.evaluate(classes).getFailureReport())) { - if ( - !isDefaultConstructor(line) - && !isSelfReference(line) - && !isExtendsJavaLangAnnotation(line) - && !isDirectDependencyFromTest(line) - ) { + if (!isDefaultConstructor(line) && !isSelfReference(line) && !isExtendsJavaLangAnnotation(line)) { relevant.add(line); } } @@ -88,10 +83,6 @@ private boolean isExtendsJavaLangAnnotation(String line) { return line.matches(String.format(".*extends.*<%s> in.*", Annotation.class.getName())); } - private boolean isDirectDependencyFromTest(String line) { - return line.matches("^Method <.*Test\\..*> .*"); - } - private List linesIn(FailureReport failureReport) { List result = new ArrayList<>(); for (String details : failureReport.getDetails()) { diff --git a/archunit/src/test/resources/archunit.properties b/archunit/src/test/resources/archunit.properties index 65d18130d0..19f5f4b93e 100644 --- a/archunit/src/test/resources/archunit.properties +++ b/archunit/src/test/resources/archunit.properties @@ -1,2 +1,4 @@ enableMd5InClassSources=true archRule.failOnEmptyShould=false +import.dependencyResolutionProcess.maxIterationsForEnclosingTypes=0 +import.dependencyResolutionProcess.maxIterationsForGenericSignatureTypes=0 \ No newline at end of file