Skip to content

Commit c077e0b

Browse files
authored
Merge pull request #244 from alexfedorenchik/componenttype
Offer a way to retrieve the component type of a `JavaClass` that is an array
2 parents 989918f + d63d927 commit c077e0b

File tree

5 files changed

+89
-3
lines changed

5 files changed

+89
-3
lines changed

archunit/src/main/java/com/tngtech/archunit/core/domain/ImportContext.java

+2
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,6 @@ public interface ImportContext {
5656
Set<JavaConstructor> getConstructorsWithParameterOfType(JavaClass javaClass);
5757

5858
Set<ThrowsDeclaration<JavaConstructor>> getConstructorThrowsDeclarationsOfType(JavaClass javaClass);
59+
60+
JavaClass resolveClass(String fullyQualifiedClassName);
5961
}

archunit/src/main/java/com/tngtech/archunit/core/domain/JavaClass.java

+35
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public class JavaClass implements HasName.AndFullName, HasAnnotations, HasModifi
8181
private final Set<JavaClass> interfaces = new HashSet<>();
8282
private final Set<JavaClass> subClasses = new HashSet<>();
8383
private Optional<JavaClass> enclosingClass = Optional.absent();
84+
private Optional<JavaClass> componentType = Optional.absent();
8485
private Supplier<Map<String, JavaAnnotation>> annotations =
8586
Suppliers.ofInstance(Collections.<String, JavaAnnotation>emptyMap());
8687
private Supplier<Set<JavaMethod>> allMethods;
@@ -186,6 +187,30 @@ public boolean isArray() {
186187
return javaType.isArray();
187188
}
188189

190+
/**
191+
* This is a convenience method for {@link #tryGetComponentType()} in cases where
192+
* clients know that this type is certainly an array type and thus the component type present.
193+
* @throws IllegalArgumentException if this class is no array
194+
* @return The result of {@link #tryGetComponentType()}
195+
*/
196+
@PublicAPI(usage = ACCESS)
197+
public JavaClass getComponentType() {
198+
return tryGetComponentType().getOrThrow(new IllegalArgumentException(
199+
String.format("Type %s is no array", getSimpleName())));
200+
}
201+
202+
/**
203+
* Returns the component type of this class, if this class is an array, otherwise
204+
* {@link Optional#absent()}. The component type is the type of the elements of an array type.
205+
* Consider {@code String[]}, then the component type would be {@code String}.
206+
* Likewise for {@code String[][]} the component type would be {@code String[]}.
207+
* @return The component type, if this type is an array, otherwise {@link Optional#absent()}
208+
*/
209+
@PublicAPI(usage = ACCESS)
210+
Optional<JavaClass> tryGetComponentType() {
211+
return componentType;
212+
}
213+
189214
/**
190215
* @return Returns true if this class is declared within another class.
191216
* Returns false for top-level classes.
@@ -841,6 +866,7 @@ public Map<String, JavaAnnotation> get() {
841866
}
842867

843868
CompletionProcess completeFrom(ImportContext context) {
869+
completeComponentType(context);
844870
enclosingClass = context.createEnclosingClass(this);
845871
memberDependenciesOnClass = new MemberDependenciesOnClass(
846872
context.getFieldsOfType(this),
@@ -852,6 +878,15 @@ CompletionProcess completeFrom(ImportContext context) {
852878
return new CompletionProcess();
853879
}
854880

881+
private void completeComponentType(ImportContext context) {
882+
JavaClass current = this;
883+
while (current.isArray() && !current.componentType.isPresent()) {
884+
JavaClass componentType = context.resolveClass(current.javaType.tryGetComponentType().get().getName());
885+
current.componentType = Optional.of(componentType);
886+
current = componentType;
887+
}
888+
}
889+
855890
@Override
856891
public String toString() {
857892
return "JavaClass{name='" + javaType.getName() + "\'}";

archunit/src/main/java/com/tngtech/archunit/core/importer/ClassGraphCreator.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,11 @@ public Optional<JavaClass> createEnclosingClass(JavaClass owner) {
275275
Optional.<JavaClass>absent();
276276
}
277277

278+
@Override
279+
public JavaClass resolveClass(String fullyQualifiedClassName) {
280+
return classes.getOrResolve(fullyQualifiedClassName);
281+
}
282+
278283
private static class MemberDependenciesByTarget {
279284
private final SetMultimap<JavaClass, JavaField> fieldTypeDependencies = HashMultimap.create();
280285
private final SetMultimap<JavaClass, JavaMethod> methodParameterTypeDependencies = HashMultimap.create();
@@ -336,4 +341,4 @@ Set<ThrowsDeclaration<JavaConstructor>> getConstructorThrowsDeclarationsOfType(J
336341
return constructorThrowsDeclarationDependencies.get(javaClass);
337342
}
338343
}
339-
}
344+
}

archunit/src/test/java/com/tngtech/archunit/core/domain/JavaClassTest.java

+40-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
import com.tngtech.archunit.core.domain.testobjects.IsArrayTestClass;
2424
import com.tngtech.archunit.core.domain.testobjects.SuperA;
2525
import com.tngtech.archunit.core.importer.ClassFileImporter;
26+
import com.tngtech.archunit.core.importer.testexamples.arrays.ClassAccessingOneDimensionalArray;
27+
import com.tngtech.archunit.core.importer.testexamples.arrays.ClassAccessingTwoDimensionalArray;
28+
import com.tngtech.archunit.core.importer.testexamples.arrays.ClassUsedInArray;
2629
import com.tngtech.java.junit.dataprovider.DataProvider;
2730
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
2831
import com.tngtech.java.junit.dataprovider.UseDataProvider;
@@ -80,13 +83,49 @@ public void finds_array_type() {
8083
JavaMethod method = importClassWithContext(IsArrayTestClass.class).getMethod("anArray");
8184

8285
assertThat(method.getRawReturnType().isArray()).isTrue();
86+
assertThat(method.getRawReturnType().tryGetComponentType().get()).matches(Object.class);
8387
}
8488

8589
@Test
8690
public void finds_non_array_type() {
8791
JavaMethod method = importClassWithContext(IsArrayTestClass.class).getMethod("notAnArray");
8892

8993
assertThat(method.getRawReturnType().isArray()).isFalse();
94+
assertThat(method.getRawReturnType().tryGetComponentType()).isAbsent();
95+
}
96+
97+
@Test
98+
public void finds_multidimensional_array_type() {
99+
JavaClasses classes = importClassesWithContext(ClassUsedInArray.class, ClassAccessingOneDimensionalArray.class, ClassAccessingTwoDimensionalArray.class);
100+
JavaClass type = classes.get(ClassUsedInArray.class);
101+
JavaClass oneDimArray = classes.get(ClassAccessingOneDimensionalArray.class).getField("array").getRawType();
102+
JavaClass twoDimArray = classes.get(ClassAccessingTwoDimensionalArray.class).getField("array").getRawType();
103+
104+
assertThat(oneDimArray.isArray()).isTrue();
105+
assertThat(oneDimArray.tryGetComponentType().get()).isEqualTo(type);
106+
assertThat(twoDimArray.isArray()).isTrue();
107+
assertThat(twoDimArray.tryGetComponentType().get()).isEqualTo(oneDimArray);
108+
assertThat(twoDimArray.tryGetComponentType().get().tryGetComponentType().get()).isEqualTo(type);
109+
}
110+
111+
@Test
112+
public void finds_component_type_chain_of_otherwise_unreferenced_component_type() {
113+
class OnlyReferencingMultiDimArray {
114+
OnlyReferencingMultiDimArray[][][] field;
115+
}
116+
117+
JavaClass javaClass = importClassesWithContext(OnlyReferencingMultiDimArray.class)
118+
.get(OnlyReferencingMultiDimArray.class);
119+
120+
JavaClass arrayType = javaClass.getField("field").getRawType();
121+
JavaClass twoDim = arrayType.getComponentType();
122+
assertThat(twoDim.getName()).isEqualTo(OnlyReferencingMultiDimArray[][].class.getName());
123+
124+
JavaClass oneDim = twoDim.getComponentType();
125+
assertThat(oneDim.getName()).isEqualTo(OnlyReferencingMultiDimArray[].class.getName());
126+
127+
JavaClass original = oneDim.getComponentType();
128+
assertThat(original).isEqualTo(javaClass);
90129
}
91130

92131
@Test
@@ -1134,4 +1173,4 @@ private class NestedNamedInnerClass {
11341173
}
11351174
}
11361175
}
1137-
}
1176+
}

archunit/src/test/java/com/tngtech/archunit/core/importer/ImportTestUtils.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -428,5 +428,10 @@ public Set<JavaConstructor> getConstructorsWithParameterOfType(JavaClass javaCla
428428
public Set<ThrowsDeclaration<JavaConstructor>> getConstructorThrowsDeclarationsOfType(JavaClass javaClass) {
429429
return Collections.emptySet();
430430
}
431+
432+
@Override
433+
public JavaClass resolveClass(String fullyQualifiedClassName) {
434+
throw new UnsupportedOperationException("Override me where necessary");
435+
}
431436
}
432-
}
437+
}

0 commit comments

Comments
 (0)