| 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 <P extends CheckedProvider<?>> void checkInterface( |
| Class<P> 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<Method> 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<Method> { |
| static final NotSyntheticOrBridgePredicate INSTANCE = new NotSyntheticOrBridgePredicate(); |
| |
| @Override |
| public boolean apply(Method input) { |
| return !input.isBridge() && !input.isSynthetic(); |
| } |
| } |
| } |