Skip to content

Commit

Permalink
Add generic type info to JavaClass #398
Browse files Browse the repository at this point in the history
This will solve a part of #115 and add support for generic types to `JavaClass`. It will still not be possible to query superclass or interface type parameters, but it will add support for arbitrarily nested type signatures of `JavaClass` itself, like

```
class Foo<T extends Serializable, U extends List<? super Map<? extends Bar, T[][]>>, V extends T> {}
```

Issue: #115
  • Loading branch information
codecholeric committed Sep 12, 2020
2 parents ae16a10 + bbfd4d5 commit 17a0747
Show file tree
Hide file tree
Showing 65 changed files with 3,642 additions and 1,128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaType;
import com.tngtech.archunit.core.domain.JavaClassDescriptor;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.DomainBuilders;
import com.tngtech.archunit.junit.ArchTest;
Expand All @@ -21,15 +21,15 @@ public class ImporterRules {
.should().accessClassesThat(belong_to_the_import_context());

@ArchTest
public static final ArchRule ASM_type_is_only_accessed_within_JavaType_or_JavaTypeImporter =
public static final ArchRule ASM_type_is_only_accessed_within_JavaClassDescriptor_or_JavaClassDescriptorImporter =
noClasses()
.that().resideOutsideOfPackage(THIRDPARTY_PACKAGE_IDENTIFIER)
.and(not(belongToAnyOf(JavaType.class)))
.and().doNotHaveFullyQualifiedName("com.tngtech.archunit.core.importer.JavaTypeImporter")
.and(not(belongToAnyOf(JavaClassDescriptor.class)))
.and().doNotHaveFullyQualifiedName("com.tngtech.archunit.core.importer.JavaClassDescriptorImporter")
.should().dependOnClassesThat().haveNameMatching(".*\\.asm\\..*Type")
.as("org.objectweb.asm.Type should only be accessed within JavaType(Importer)")
.as("org.objectweb.asm.Type should only be accessed within JavaClassDescriptor(Importer)")
.because("org.objectweb.asm.Type handles array types inconsistently (uses the canonical name instead of the class name), "
+ "so the correct behavior is implemented only within JavaType");
+ "so the correct behavior is implemented only within JavaClassDescriptor");

private static DescribedPredicate<JavaClass> belong_to_the_import_context() {
return new DescribedPredicate<JavaClass>("belong to the import context") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import org.mockito.junit.MockitoRule;

import static com.tngtech.archunit.junit.CacheMode.PER_CLASS;
import static com.tngtech.archunit.testutil.Assertions.assertThatClasses;
import static com.tngtech.archunit.testutil.Assertions.assertThatTypes;
import static com.tngtech.java.junit.dataprovider.DataProviders.testForEach;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
Expand Down Expand Up @@ -114,13 +114,13 @@ public void get_all_classes_by_LocationProvider() {
.withPackagesRoots(ClassCacheTest.class)
.withLocationProviders(TestLocationProviderOfClass_String.class, TestLocationProviderOfClass_Rule.class));

assertThatClasses(classes).contain(String.class, Rule.class, getClass());
assertThatTypes(classes).contain(String.class, Rule.class, getClass());

classes = cache.getClassesToAnalyzeFor(TestClassWithLocationProviderUsingTestClass.class,
analyzeLocation(LocationOfClass.Provider.class));

assertThatClasses(classes).contain(String.class);
assertThatClasses(classes).doNotContain(getClass());
assertThatTypes(classes).contain(String.class);
assertThatTypes(classes).doNotContain(getClass());
}

@Test
Expand Down Expand Up @@ -275,4 +275,4 @@ public Set<Location> get(Class<?> testClass) {
return Collections.emptySet();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ public static final class FieldAccessTarget extends AccessTarget implements HasT
this.field = Suppliers.memoize(builder.getField());
}

@Override
@PublicAPI(usage = ACCESS)
public JavaType getType() {
return type;
}

@Override
@PublicAPI(usage = ACCESS)
public JavaClass getRawType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public String apply(String input) {

/**
* Configures that the identifier is omitted if the annotation is a
* <a href="https://docs.oracle.com/javase/specs/jls/se14/html/jls-9.html#jls-9.7.3">single-element annotation</a>
* <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-9.7.3">single-element annotation</a>
* and the identifier of the only element is "value".
*
* <ul><li>Example with this configuration: {@code @Copyright("2020 Acme Corporation")}</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ private JavaClassConversion(ClassLoader classLoader) {

@Override
public Class<?> convert(JavaClass input, Class<?> returnType) {
return JavaType.From.javaClass(input).resolveClass(classLoader);
return JavaClassDescriptor.From.javaClass(input).resolveClass(classLoader);
}

@Override
Expand Down Expand Up @@ -150,7 +150,7 @@ public boolean canHandle(Class<?> returnType) {
private static class JavaEnumConstantConversion implements Conversion<JavaEnumConstant> {
@Override
public Enum<?> convert(JavaEnumConstant input, Class<?> returnType) {
for (Object constant : JavaType.From.javaClass(input.getDeclaringClass()).resolveClass().getEnumConstants()) {
for (Object constant : JavaClassDescriptor.From.javaClass(input.getDeclaringClass()).resolveClass().getEnumConstants()) {
Enum<?> anEnum = (Enum<?>) constant;
if (anEnum.name().equals(input.name())) {
return anEnum;
Expand Down Expand Up @@ -196,7 +196,7 @@ public Annotation convert(JavaAnnotation<?> input, Class<?> returnType) {
// JavaAnnotation.getType() will return the type name of a Class<? extends Annotation>
@SuppressWarnings("unchecked")
Class<? extends Annotation> type = (Class<? extends Annotation>)
JavaType.From.javaClass(input.getRawType()).resolveClass(classLoader);
JavaClassDescriptor.From.javaClass(input.getRawType()).resolveClass(classLoader);
return AnnotationProxy.of(type, input);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@
import com.tngtech.archunit.core.importer.DomainBuilders.JavaMethodBuilder;
import com.tngtech.archunit.core.importer.DomainBuilders.JavaMethodCallBuilder;
import com.tngtech.archunit.core.importer.DomainBuilders.JavaStaticInitializerBuilder;
import com.tngtech.archunit.core.importer.DomainBuilders.JavaWildcardTypeBuilder;
import com.tngtech.archunit.core.importer.DomainBuilders.MethodCallTargetBuilder;

import static com.google.common.base.Preconditions.checkArgument;

/**
* Together with {@link DomainBuilders}, this class is the link to create domain objects from the import
* context. To make the API clear, we try to keep only those methods public, which are really meant to be used.
Expand All @@ -62,7 +65,7 @@
@Internal
public class DomainObjectCreationContext {
public static JavaClasses createJavaClasses(
Map<String, JavaClass> selectedClasses, Map<String, JavaClass> allClasses, ImportContext importContext) {
Map<String, JavaClass> selectedClasses, Collection<JavaClass> allClasses, ImportContext importContext) {

return JavaClasses.of(selectedClasses, allClasses, importContext);
}
Expand All @@ -75,6 +78,14 @@ public static void completeClassHierarchy(JavaClass javaClass, ImportContext imp
javaClass.completeClassHierarchyFrom(importContext);
}

public static void completeEnclosingClass(JavaClass javaClass, ImportContext importContext) {
javaClass.completeEnclosingClassFrom(importContext);
}

public static void completeTypeParameters(JavaClass javaClass, ImportContext importContext) {
javaClass.completeTypeParametersFrom(importContext);
}

public static void completeMembers(JavaClass javaClass, ImportContext importContext) {
javaClass.completeMembers(importContext);
}
Expand Down Expand Up @@ -143,6 +154,24 @@ public static <CODE_UNIT extends JavaCodeUnit> ThrowsClause<CODE_UNIT> createThr
return ThrowsClause.from(codeUnit, types);
}

public static JavaTypeVariable createTypeVariable(String name, JavaClass erasure) {
return new JavaTypeVariable(name, erasure);
}

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

public static JavaGenericArrayType createGenericArrayType(JavaType componentType, JavaClass erasure) {
checkArgument(componentType instanceof JavaTypeVariable || componentType instanceof JavaGenericArrayType,
"Component type of a generic array type can only be a type variable or a generic array type. This is most likely a bug.");
return new JavaGenericArrayType(componentType.getName() + "[]", componentType, erasure);
}

public static JavaWildcardType createWildcardType(JavaWildcardTypeBuilder builder) {
return new JavaWildcardType(builder);
}

static class AccessContext {
final SetMultimap<JavaClass, JavaFieldAccess> fieldAccessesByTarget = HashMultimap.create();
final SetMultimap<JavaClass, JavaMethodCall> methodCallsByTarget = HashMultimap.create();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.tngtech.archunit.core.domain;

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

Expand All @@ -27,6 +28,8 @@ public interface ImportContext {

Set<JavaClass> createInterfaces(JavaClass owner);

List<JavaTypeVariable> createTypeParameters(JavaClass owner);

Set<JavaField> createFields(JavaClass owner);

Set<JavaMethod> createMethods(JavaClass owner);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ private String startWithLowerCase(HasDescription annotatedElement) {
return annotatedElement.getDescription().substring(0, 1).toLowerCase() + annotatedElement.getDescription().substring(1);
}

@Override
public JavaType getType() {
return type;
}

@Override
@PublicAPI(usage = ACCESS)
public JavaClass getRawType() {
Expand Down
Loading

0 comments on commit 17a0747

Please sign in to comment.