Reputation: 779
Background: https://dev.to/carey/java-map-keys-should-always-be-comparable-2c1b
What I want to achieve:
I a stuck at step 2
noClasses().that()
.containAnyFieldsThat(have(rawType(HashMap.class)))
.should()...
Any ideas how I can get the type of a generic class? Thanks for support.
Upvotes: 0
Views: 357
Reputation: 3142
The crucial part of your ArchRule
(which can be directly based on fields()
or codeUnits()
) can be expressed with custom ArchCondition
s:
@ArchTest
static final ArchRule fields_of_type_HashMap_should_have_Comparable_key = fields()
.that().haveRawType(HashMap.class)
.should(haveComparableFirstTypeParameter());
@ArchTest
static final ArchRule code_units_should_have_parameters_of_type_HashMap_with_Comparable_key = codeUnits()
.should(new ArchCondition<JavaCodeUnit>("have parameters of type HashMap with Comparable key") {
@Override
public void check(JavaCodeUnit javaCodeUnit, ConditionEvents events) {
javaCodeUnit.getParameters().forEach(parameter -> {
if (parameter.getRawType().isEquivalentTo(HashMap.class)) {
haveComparableFirstTypeParameter().check(parameter, events);
}
});
}
});
@ArchTest
static final ArchRule methods_with_return_type_HashMap_should_have_return_types_with_Comparable_key = methods()
.that().haveRawReturnType(HashMap.class)
.should(new ArchCondition<JavaMethod>("have return type with Comparable key") {
@Override
public void check(JavaMethod method, ConditionEvents events) {
class ReturnType implements HasType, HasDescription {
@Override
public JavaType getType() { return method.getReturnType(); }
@Override
public JavaClass getRawType() { return method.getRawReturnType(); }
@Override
public String getDescription() { return "Return type <" + getType().getName() + "> of " + method.getDescription(); }
}
haveComparableFirstTypeParameter().check(new ReturnType(), events);
}
});
private static <T extends HasType & HasDescription> ArchCondition<T> haveComparableFirstTypeParameter() {
return new ArchCondition<T>("have Comparable first type parameter") {
@Override
public void check(T typed, ConditionEvents events) {
JavaType fieldType = typed.getType();
if (fieldType instanceof JavaParameterizedType) {
JavaType keyType = ((JavaParameterizedType) fieldType).getActualTypeArguments().get(0);
boolean satisfied = keyType.toErasure().getAllRawInterfaces().stream()
.anyMatch(rawInterface -> rawInterface.isEquivalentTo(Comparable.class));
String message = String.format("%s has a first type parameter %s that %s Comparable",
typed.getDescription(), keyType.getName(), satisfied ? "is" : "is not");
events.add(new SimpleConditionEvent(typed, satisfied, message));
} else {
events.add(SimpleConditionEvent.violated(typed, typed.getDescription() + " is not parameterized"));
}
}
};
}
Upvotes: 2