com.google.inject.throwingproviders.ProviderChecker Maven / Gradle / Ivy
package com.google.inject.throwingproviders;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.inject.internal.Errors;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.List;
/** Helper methods to verify the correctness of CheckedProvider interfaces. */
final class ProviderChecker {
private ProviderChecker() {}
static > void checkInterface(
Class
interfaceType, Optional extends Type> valueType) {
checkArgument(interfaceType.isInterface(), "%s must be an interface", interfaceType.getName());
checkArgument(
interfaceType.getGenericInterfaces().length == 1,
"%s must extend CheckedProvider (and only CheckedProvider)",
interfaceType);
boolean tpMode = interfaceType.getInterfaces()[0] == ThrowingProvider.class;
if (!tpMode) {
checkArgument(
interfaceType.getInterfaces()[0] == CheckedProvider.class,
"%s must extend CheckedProvider (and only CheckedProvider)",
interfaceType);
}
// Ensure that T is parameterized and unconstrained.
ParameterizedType genericThrowingProvider =
(ParameterizedType) interfaceType.getGenericInterfaces()[0];
if (interfaceType.getTypeParameters().length == 1) {
String returnTypeName = interfaceType.getTypeParameters()[0].getName();
Type returnType = genericThrowingProvider.getActualTypeArguments()[0];
checkArgument(
returnType instanceof TypeVariable,
"%s does not properly extend CheckedProvider, the first type parameter of CheckedProvider"
+ " (%s) is not a generic type",
interfaceType,
returnType);
checkArgument(
returnTypeName.equals(((TypeVariable) returnType).getName()),
"The generic type (%s) of %s does not match the generic type of CheckedProvider (%s)",
returnTypeName,
interfaceType,
((TypeVariable) returnType).getName());
} else {
checkArgument(
interfaceType.getTypeParameters().length == 0,
"%s has more than one generic type parameter: %s",
interfaceType,
Arrays.asList(interfaceType.getTypeParameters()));
if (valueType.isPresent()) {
checkArgument(
genericThrowingProvider.getActualTypeArguments()[0].equals(valueType.get()),
"%s expects the value type to be %s, but it was %s",
interfaceType,
genericThrowingProvider.getActualTypeArguments()[0],
valueType.get());
}
}
if (tpMode) { // only validate exception in ThrowingProvider mode.
Type exceptionType = genericThrowingProvider.getActualTypeArguments()[1];
checkArgument(
exceptionType instanceof Class,
"%s has the wrong Exception generic type (%s) when extending CheckedProvider",
interfaceType,
exceptionType);
}
// Skip synthetic/bridge methods because java8 generates
// a default method on the interface w/ the superinterface type that
// just delegates directly to the overridden method.
List declaredMethods =
FluentIterable.from(Arrays.asList(interfaceType.getDeclaredMethods()))
.filter(NotSyntheticOrBridgePredicate.INSTANCE)
.toList();
if (declaredMethods.size() == 1) {
Method method = declaredMethods.get(0);
checkArgument(
method.getName().equals("get"),
"%s may not declare any new methods, but declared %s",
interfaceType,
method);
checkArgument(
method.getParameterTypes().length == 0,
"%s may not declare any new methods, but declared %s",
interfaceType,
method.toGenericString());
} else {
checkArgument(
declaredMethods.isEmpty(),
"%s may not declare any new methods, but declared %s",
interfaceType,
Arrays.asList(interfaceType.getDeclaredMethods()));
}
}
private static void checkArgument(boolean condition, String messageFormat, Object... args) {
if (!condition) {
throw new IllegalArgumentException(Errors.format(messageFormat, args));
}
}
private static class NotSyntheticOrBridgePredicate implements Predicate {
static final NotSyntheticOrBridgePredicate INSTANCE = new NotSyntheticOrBridgePredicate();
@Override
public boolean apply(Method input) {
return !input.isBridge() && !input.isSynthetic();
}
}
}