1 package com.google.inject.throwingproviders; 2 3 import com.google.common.base.Optional; 4 import com.google.common.base.Predicate; 5 import com.google.common.collect.FluentIterable; 6 import com.google.inject.internal.Errors; 7 import java.lang.reflect.Method; 8 import java.lang.reflect.ParameterizedType; 9 import java.lang.reflect.Type; 10 import java.lang.reflect.TypeVariable; 11 import java.util.Arrays; 12 import java.util.List; 13 14 /** Helper methods to verify the correctness of CheckedProvider interfaces. */ 15 final class ProviderChecker { 16 ProviderChecker()17 private ProviderChecker() {} 18 checkInterface( Class<P> interfaceType, Optional<? extends Type> valueType)19 static <P extends CheckedProvider<?>> void checkInterface( 20 Class<P> interfaceType, Optional<? extends Type> valueType) { 21 checkArgument(interfaceType.isInterface(), "%s must be an interface", interfaceType.getName()); 22 checkArgument( 23 interfaceType.getGenericInterfaces().length == 1, 24 "%s must extend CheckedProvider (and only CheckedProvider)", 25 interfaceType); 26 27 boolean tpMode = interfaceType.getInterfaces()[0] == ThrowingProvider.class; 28 if (!tpMode) { 29 checkArgument( 30 interfaceType.getInterfaces()[0] == CheckedProvider.class, 31 "%s must extend CheckedProvider (and only CheckedProvider)", 32 interfaceType); 33 } 34 35 // Ensure that T is parameterized and unconstrained. 36 ParameterizedType genericThrowingProvider = 37 (ParameterizedType) interfaceType.getGenericInterfaces()[0]; 38 if (interfaceType.getTypeParameters().length == 1) { 39 String returnTypeName = interfaceType.getTypeParameters()[0].getName(); 40 Type returnType = genericThrowingProvider.getActualTypeArguments()[0]; 41 checkArgument( 42 returnType instanceof TypeVariable, 43 "%s does not properly extend CheckedProvider, the first type parameter of CheckedProvider" 44 + " (%s) is not a generic type", 45 interfaceType, 46 returnType); 47 checkArgument( 48 returnTypeName.equals(((TypeVariable) returnType).getName()), 49 "The generic type (%s) of %s does not match the generic type of CheckedProvider (%s)", 50 returnTypeName, 51 interfaceType, 52 ((TypeVariable) returnType).getName()); 53 } else { 54 checkArgument( 55 interfaceType.getTypeParameters().length == 0, 56 "%s has more than one generic type parameter: %s", 57 interfaceType, 58 Arrays.asList(interfaceType.getTypeParameters())); 59 if (valueType.isPresent()) { 60 checkArgument( 61 genericThrowingProvider.getActualTypeArguments()[0].equals(valueType.get()), 62 "%s expects the value type to be %s, but it was %s", 63 interfaceType, 64 genericThrowingProvider.getActualTypeArguments()[0], 65 valueType.get()); 66 } 67 } 68 69 if (tpMode) { // only validate exception in ThrowingProvider mode. 70 Type exceptionType = genericThrowingProvider.getActualTypeArguments()[1]; 71 checkArgument( 72 exceptionType instanceof Class, 73 "%s has the wrong Exception generic type (%s) when extending CheckedProvider", 74 interfaceType, 75 exceptionType); 76 } 77 78 // Skip synthetic/bridge methods because java8 generates 79 // a default method on the interface w/ the superinterface type that 80 // just delegates directly to the overridden method. 81 List<Method> declaredMethods = 82 FluentIterable.from(Arrays.asList(interfaceType.getDeclaredMethods())) 83 .filter(NotSyntheticOrBridgePredicate.INSTANCE) 84 .toList(); 85 if (declaredMethods.size() == 1) { 86 Method method = declaredMethods.get(0); 87 checkArgument( 88 method.getName().equals("get"), 89 "%s may not declare any new methods, but declared %s", 90 interfaceType, 91 method); 92 checkArgument( 93 method.getParameterTypes().length == 0, 94 "%s may not declare any new methods, but declared %s", 95 interfaceType, 96 method.toGenericString()); 97 } else { 98 checkArgument( 99 declaredMethods.isEmpty(), 100 "%s may not declare any new methods, but declared %s", 101 interfaceType, 102 Arrays.asList(interfaceType.getDeclaredMethods())); 103 } 104 } 105 checkArgument(boolean condition, String messageFormat, Object... args)106 private static void checkArgument(boolean condition, String messageFormat, Object... args) { 107 if (!condition) { 108 throw new IllegalArgumentException(Errors.format(messageFormat, args)); 109 } 110 } 111 112 private static class NotSyntheticOrBridgePredicate implements Predicate<Method> { 113 static final NotSyntheticOrBridgePredicate INSTANCE = new NotSyntheticOrBridgePredicate(); 114 115 @Override apply(Method input)116 public boolean apply(Method input) { 117 return !input.isBridge() && !input.isSynthetic(); 118 } 119 } 120 } 121