/** * Matches constructor parameters to method parameters for injection and records remaining parameters as required keys. */ private String[] extractConstructorParameters(Key<?> factoryKey, TypeLiteral<?> implementation, Constructor constructor, List<Key<?>> methodParams, Errors errors, Set<Dependency> dependencyCollector) throws ErrorsException { // Get parameters with annotations. List<TypeLiteral<?>> ctorParams = implementation.getParameterTypes(constructor); Annotation[][] ctorParamAnnotations = constructor.getParameterAnnotations(); int p = 0; String[] parameterNames = new String[ctorParams.size()]; for (TypeLiteral<?> ctorParam : ctorParams) { Key<?> ctorParamKey = getKey(ctorParam, constructor, ctorParamAnnotations[p], errors); if (ctorParamKey.getAnnotationType() == Assisted.class) { int location = methodParams.indexOf(ctorParamKey); // This should never happen since the constructor was already checked // in #[inject]constructorHasMatchingParams(..). Preconditions.checkState(location != -1); parameterNames[p] = ReflectUtil.formatParameterName(location); } else { dependencyCollector.add(new Dependency(factoryKey, ctorParamKey, false, true, constructor.toString())); } p++; } return parameterNames; }
@Override public Object resolve(Injectee injectee, ServiceHandle<?> serviceHandle) { if (injectee.getRequiredType() instanceof Class) { TypeLiteral<?> typeLiteral = TypeLiteral.get(injectee.getRequiredType()); Errors errors = new Errors(injectee.getParent()); Key<?> key; try { key = Annotations.getKey(typeLiteral, (Member) injectee.getParent(), injectee.getParent().getDeclaredAnnotations(), errors); } catch (ErrorsException e) { errors.merge(e.getErrors()); throw new ConfigurationException(errors.getMessages()); } return injector.getInstance(key); } throw new IllegalStateException("Can't process injection point: " + injectee.getRequiredType()); }
FactoryBinding(Map<Key<?>, TypeLiteral<?>> collector, Key<?> factoryKey, Context context, GuiceUtil guiceUtil, MethodCallUtil methodCallUtil) { super(context, factoryKey); this.collector = Preconditions.checkNotNull(collector); this.factoryKey = factoryKey; this.factoryType = factoryKey.getTypeLiteral(); this.guiceUtil = guiceUtil; this.methodCallUtil = methodCallUtil; try { matchMethods(Preconditions.checkNotNull(factoryKey)); } catch (ErrorsException e) { e.getErrors().throwConfigurationExceptionIfErrorsExist(); } }
@Override protected void configure() { final Errors errors = new Errors(testClass); for (FrameworkField field : fields) { try { final Field f = field.getField(); final Key key = Annotations.getKey(TypeLiteral.get(f.getGenericType()), f, field.getAnnotations(), errors); bindMock(key, f.getType(), "Automock[" + field.getName() + "] " + key); } catch (ErrorsException e) { // Add it to the error list and hold them all until the end errors.merge(e.getErrors()); } } errors.throwConfigurationExceptionIfErrorsExist(); }
/** * Compute the value for a parameter annotated with * * @param errors * @param paramIndex * * @return * * @throws ErrorsException */ private Object getCollectionParamValue(final Errors errors, final int paramIndex, final TestEach annotation) throws ErrorsException { if (!(method instanceof TestEachFrameworkMethod)) throw new AssertionError("Required a parameterised FrameworkMethod but got " + method); // The index within the collection to use for this particular invocation final int collectionIndex = ((TestEachFrameworkMethod) method).getCollectionIndexForParameter(paramIndex); if (annotation.value() != null && annotation.value().length > 0) { final Class<?> desiredType = method.getMethod().getParameterTypes()[paramIndex]; final String val = annotation.value()[collectionIndex]; return convertParamType(val, desiredType); } else { return getGuiceCollectionParamValue(errors, paramIndex, collectionIndex); } }
/** * Matching logic for {@literal @}{@link Inject} constructor and method parameters. * <p/> * This returns true if all assisted parameters required by the constructor are provided by the factory method. */ private boolean injectConstructorHasMatchingParams(TypeLiteral<?> type, Constructor<?> constructor, List<Key<?>> paramList, Errors errors) throws ErrorsException { List<TypeLiteral<?>> params = type.getParameterTypes(constructor); Annotation[][] paramAnnotations = constructor.getParameterAnnotations(); int p = 0; for (TypeLiteral<?> param : params) { Key<?> paramKey = getKey(param, constructor, paramAnnotations[p++], errors); if (paramKey.getAnnotationType() == Assisted.class && !paramList.contains(paramKey)) { return false; } } return true; }
/** * Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation. * <p/> * This fails if another binding annotation is clobbered in the process. If the key already has the {@literal @}Assisted annotation, it is returned as-is to * preserve any String value. */ private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException { if (key.getAnnotationType() == null) { return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION); } else if (key.getAnnotationType() == Assisted.class) { return key; } else { errors.withSource(method).addMessage( PrettyPrinter.format("Only @Assisted is allowed for factory parameters, but found @%s", key.getAnnotationType())); throw errors.toException(); } }
/** * Matching logic for {@literal @}{@link Inject} constructor and method * parameters. * * This returns true if all assisted parameters required by the constructor * are provided by the factory method. */ private boolean injectConstructorHasMatchingParams(TypeLiteral<?> type, Constructor<?> constructor, List<Key<?>> paramList, Errors errors) throws ErrorsException { List<TypeLiteral<?>> params = type.getParameterTypes(constructor); Annotation[][] paramAnnotations = constructor.getParameterAnnotations(); int p = 0; for (TypeLiteral<?> param : params) { Key<?> paramKey = getKey(param, constructor, paramAnnotations[p++], errors); if(paramKey.getAnnotationType() == Assisted.class && !paramList.contains(paramKey)) { return false; } } return true; }
/** * Returns a key similar to {@code key}, but with an {@literal @}Assisted * binding annotation. * * This fails if another binding annotation is clobbered in the process. If * the key already has the {@literal @}Assisted annotation, it is returned * as-is to preserve any String value. */ private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException { if (key.getAnnotationType() == null) { return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION); } else if (key.getAnnotationType() == Assisted.class) { return key; } else { errors.withSource(method).addMessage(PrettyPrinter.format( "Only @Assisted is allowed for factory parameters, but found @%s", key.getAnnotationType())); throw errors.toException(); } }
public static <F> Provider<F> newFactory( TypeLiteral<F> factoryType, TypeLiteral<?> implementationType) { Map<Method, AssistedConstructor<?>> factoryMethodToConstructor = createMethodMapping(factoryType, implementationType); if (!factoryMethodToConstructor.isEmpty()) { return new FactoryProvider<F>(factoryType, implementationType, factoryMethodToConstructor); } else { BindingCollector collector = new BindingCollector(); // Preserving backwards-compatibility: Map all return types in a factory // interface to the passed implementation type. Errors errors = new Errors(); Key<?> implementationKey = Key.get(implementationType); try { for (Method method : factoryType.getRawType().getMethods()) { Key<?> returnType = getKey(factoryType.getReturnType(method), method, method.getAnnotations(), errors); if (!implementationKey.equals(returnType)) { collector.addBinding(returnType, implementationType); } } } catch (ErrorsException e) { throw new ConfigurationException(e.getErrors().getMessages()); } return new FactoryProvider2<F>(Key.get(factoryType), collector); } }
/** * Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation. This * fails if another binding annotation is clobbered in the process. If the key already has the * {@literal @}Assisted annotation, it is returned as-is to preserve any String value. */ private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException { if (key.getAnnotationType() == null) { return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION); } else if (key.getAnnotationType() == Assisted.class) { return key; } else { errors .withSource(method) .addMessage( "Only @Assisted is allowed for factory parameters, but found @%s", key.getAnnotationType()); throw errors.toException(); } }
public static <F> Provider<F> newFactory( TypeLiteral<F> factoryType, TypeLiteral<?> implementationType) { Map<Method, AssistedConstructor<?>> factoryMethodToConstructor = createMethodMapping(factoryType, implementationType); if (!factoryMethodToConstructor.isEmpty()) { return new FactoryProvider<F>(factoryType, factoryMethodToConstructor); } else { BindingCollector collector = new BindingCollector(); // Preserving backwards-compatibility: Map all return types in a factory // interface to the passed implementation type. Errors errors = new Errors(); Key<?> implementationKey = Key.get(implementationType); if (implementationType != null) { try { for (Method method : factoryType.getRawType().getMethods()) { Key<?> returnType = getKey(factoryType.getReturnType(method), method, method.getAnnotations(), errors); if (!implementationKey.equals(returnType)) { collector.addBinding(returnType, implementationType); } } } catch (ErrorsException e) { throw new ConfigurationException(e.getErrors().getMessages()); } } return new FactoryProvider2<F>(Key.get(factoryType), collector); } }
/** * Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation. * This fails if another binding annotation is clobbered in the process. If the key already has * the {@literal @}Assisted annotation, it is returned as-is to preserve any String value. */ private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException { if (key.getAnnotationType() == null) { return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION); } else if (key.getAnnotationType() == Assisted.class) { return key; } else { errors.withSource(method).addMessage( "Only @Assisted is allowed for factory parameters, but found @%s", key.getAnnotationType()); throw errors.toException(); } }
private Object getGuiceCollectionParamValue(final Errors errors, final int paramIndex, final int collectionIndex) throws ErrorsException { Collection<?> col = TestEachUtils.getGuiceCollectionForParam(method.getMethod(), paramIndex, registry, errors); // We have found the binding; use the iterator to fetch the desired index return Iterables.get(col, collectionIndex); }
private void matchMethods(Key<?> factoryKey) throws ErrorsException { Errors errors = new Errors(); dependencies.add(new Dependency(Dependency.GINJECTOR, factoryKey, getContext())); Class<?> factoryRawType = factoryType.getRawType(); // getMethods() includes inherited methods from super-interfaces. for (Method method : factoryRawType.getMethods()) { Key<?> returnType = getKey(factoryType.getReturnType(method), method, method.getAnnotations(), errors); // Get parameters with annotations. List<TypeLiteral<?>> params = factoryType.getParameterTypes(method); Annotation[][] paramAnnotations = method.getParameterAnnotations(); int p = 0; List<Key<?>> paramList = new ArrayList<Key<?>>(); for (TypeLiteral<?> param : params) { Key<?> paramKey = getKey(param, method, paramAnnotations[p++], errors); paramList.add(assistKey(method, paramKey, errors)); } // Try to match up the method to the constructor. TypeLiteral<?> implementation = collector.get(returnType); if (implementation == null) { implementation = returnType.getTypeLiteral(); } Constructor<?> constructor = findMatchingConstructor(method, implementation, paramList, errors); if (constructor == null) { continue; // Errors are collected and thrown below. } // Calculate a map from method to constructor parameters and required // keys. String[] parameterNames = extractConstructorParameters(factoryKey, implementation, constructor, paramList, errors, dependencies); TypeLiteral<?> methodDeclaringType = factoryType.getSupertype(method.getDeclaringClass()); assistData.add(new AssistData(implementation, MethodLiteral.get(constructor, implementation), MethodLiteral.get(method, methodDeclaringType), parameterNames)); implementations.add(implementation); dependencies.addAll(guiceUtil.getMemberInjectionDependencies(factoryKey, implementation)); } errors.throwConfigurationExceptionIfErrorsExist(); }
/** * Finds a constructor suitable for the method. If the implementation contained any constructors marked with {@link AssistedInject}, this requires all * {@link Assisted} parameters to exactly match the parameters (in any order) listed in the method. Otherwise, if no {@link AssistedInject} constructors * exist, this will default to looking for a {@literal @}{@link Inject} constructor. */ private Constructor findMatchingConstructor(Method method, TypeLiteral<?> implementation, List<Key<?>> paramList, Errors errors) throws ErrorsException { Constructor<?> matchingConstructor = null; boolean anyAssistedInjectConstructors = false; // Look for AssistedInject constructors... for (Constructor<?> constructor : implementation.getRawType().getDeclaredConstructors()) { if (constructor.isAnnotationPresent(AssistedInject.class)) { anyAssistedInjectConstructors = true; if (constructorHasMatchingParams(implementation, constructor, paramList, errors)) { if (matchingConstructor != null) { errors.addMessage(PrettyPrinter.format("%s has more than one constructor annotated with @AssistedInject " + "that matches the parameters in method %s.", implementation, method)); return null; } else { matchingConstructor = constructor; } } } } if (matchingConstructor != null) { return matchingConstructor; } if (anyAssistedInjectConstructors) { errors.addMessage(PrettyPrinter.format("%s has @AssistedInject constructors, but none of them match the " + "parameters in method %s.", implementation, method)); return null; } // Look for @Inject constructors... Constructor<?> injectConstructor = (Constructor) InjectionPoint.forConstructorOf(implementation).getMember(); if (injectConstructorHasMatchingParams(implementation, injectConstructor, paramList, errors)) { return injectConstructor; } // No matching constructor exists, complain. errors.addMessage(PrettyPrinter.format("%s has no constructors matching the parameters in method %s.", implementation, method)); return null; }
public static Key<?> fieldType(TypeLiteral<?> owner, Field field, Errors errors) throws ErrorsException { return Annotations.getKey(owner.getFieldType(field), field, field.getAnnotations(), errors); }
public static <T> Key<T> returnType(Invokable<?, T> method, Errors errors) throws ErrorsException { return (Key<T>) Annotations.getKey(Types.toLiteral(method.getReturnType()), method, method.getAnnotations(), errors); }
public static <T> Key<T> returnType(TypeLiteral<?> decl, Method method, Errors errors) throws ErrorsException { return (Key<T>) Annotations.getKey(decl.getReturnType(method), method, method.getAnnotations(), errors); }
/** * Matches constructor parameters to method parameters for injection and * records remaining parameters as required keys. */ private String[] extractConstructorParameters(Key<?> factoryKey, TypeLiteral<?> implementation, Constructor constructor, List<Key<?>> methodParams, Errors errors, Set<Dependency> dependencyCollector) throws ErrorsException { // Get parameters with annotations. List<TypeLiteral<?>> ctorParams = implementation.getParameterTypes(constructor); Annotation[][] ctorParamAnnotations = constructor.getParameterAnnotations(); int p = 0; String[] parameterNames = new String[ctorParams.size()]; Set<Key<?>> keySet = new LinkedHashSet<Key<?>>(); for (TypeLiteral<?> ctorParam : ctorParams) { Key<?> ctorParamKey = getKey(ctorParam, constructor, ctorParamAnnotations[p], errors); if (ctorParamKey.getAnnotationType() == Assisted.class) { if (!keySet.add(ctorParamKey)) { errors.addMessage(PrettyPrinter.format( "%s has more than one parameter of type %s annotated with @Assisted(\"%s\"). " + "Please specify a unique value with the annotation to avoid confusion.", implementation, ctorParamKey.getTypeLiteral().getType(), ((Assisted) ctorParamKey.getAnnotation()).value())); } int location = methodParams.indexOf(ctorParamKey); // This should never happen since the constructor was already checked // in #[inject]constructorHasMatchingParams(..). Preconditions.checkState(location != -1); parameterNames[p] = ReflectUtil.formatParameterName(location); } else { dependencyCollector.add(new Dependency(factoryKey, ctorParamKey, false, true, constructor.toString())); } p++; } return parameterNames; }
/** * Finds a constructor suitable for the method. If the implementation * contained any constructors marked with {@link AssistedInject}, this * requires all {@link Assisted} parameters to exactly match the parameters * (in any order) listed in the method. Otherwise, if no * {@link AssistedInject} constructors exist, this will default to looking * for a {@literal @}{@link Inject} constructor. */ private Constructor findMatchingConstructor(Method method, TypeLiteral<?> implementation, List<Key<?>> paramList, Errors errors) throws ErrorsException { Constructor<?> matchingConstructor = null; boolean anyAssistedInjectConstructors = false; // Look for AssistedInject constructors... for (Constructor<?> constructor : implementation.getRawType().getDeclaredConstructors()) { if (constructor.isAnnotationPresent(AssistedInject.class)) { anyAssistedInjectConstructors = true; if (constructorHasMatchingParams(implementation, constructor, paramList, errors)) { if (matchingConstructor != null) { errors.addMessage(PrettyPrinter.format( "%s has more than one constructor annotated with @AssistedInject " + "that matches the parameters in method %s.", implementation, method)); return null; } else { matchingConstructor = constructor; } } } } if (matchingConstructor != null) { return matchingConstructor; } if (anyAssistedInjectConstructors) { errors.addMessage(PrettyPrinter.format( "%s has @AssistedInject constructors, but none of them match the " + "parameters in method %s.", implementation, method)); return null; } // Look for @Inject constructors... Constructor<?> injectConstructor = (Constructor) InjectionPoint.forConstructorOf(implementation).getMember(); if (injectConstructorHasMatchingParams(implementation, injectConstructor, paramList, errors)) { return injectConstructor; } // No matching constructor exists, complain. errors.addMessage(PrettyPrinter.format( "%s has no constructors matching the parameters in method %s.", implementation, method)); return null; }
/** * Finds a constructor suitable for the method. If the implementation contained any constructors * marked with {@link AssistedInject}, this requires all {@link Assisted} parameters to exactly * match the parameters (in any order) listed in the method. Otherwise, if no {@link * AssistedInject} constructors exist, this will default to looking for an {@literal @}{@link * Inject} constructor. */ private <T> InjectionPoint findMatchingConstructorInjectionPoint( Method method, Key<?> returnType, TypeLiteral<T> implementation, List<Key<?>> paramList) throws ErrorsException { Errors errors = new Errors(method); if (returnType.getTypeLiteral().equals(implementation)) { errors = errors.withSource(implementation); } else { errors = errors.withSource(returnType).withSource(implementation); } Class<?> rawType = implementation.getRawType(); if (Modifier.isInterface(rawType.getModifiers())) { errors.addMessage( "%s is an interface, not a concrete class. Unable to create AssistedInject factory.", implementation); throw errors.toException(); } else if (Modifier.isAbstract(rawType.getModifiers())) { errors.addMessage( "%s is abstract, not a concrete class. Unable to create AssistedInject factory.", implementation); throw errors.toException(); } else if (Classes.isInnerClass(rawType)) { errors.cannotInjectInnerClass(rawType); throw errors.toException(); } Constructor<?> matchingConstructor = null; boolean anyAssistedInjectConstructors = false; // Look for AssistedInject constructors... for (Constructor<?> constructor : rawType.getDeclaredConstructors()) { if (constructor.isAnnotationPresent(AssistedInject.class)) { anyAssistedInjectConstructors = true; if (constructorHasMatchingParams(implementation, constructor, paramList, errors)) { if (matchingConstructor != null) { errors.addMessage( "%s has more than one constructor annotated with @AssistedInject" + " that matches the parameters in method %s. Unable to create " + "AssistedInject factory.", implementation, method); throw errors.toException(); } else { matchingConstructor = constructor; } } } } if (!anyAssistedInjectConstructors) { // If none existed, use @Inject. try { return InjectionPoint.forConstructorOf(implementation); } catch (ConfigurationException e) { errors.merge(e.getErrorMessages()); throw errors.toException(); } } else { // Otherwise, use it or fail with a good error message. if (matchingConstructor != null) { // safe because we got the constructor from this implementation. @SuppressWarnings("unchecked") InjectionPoint ip = InjectionPoint.forConstructor( (Constructor<? super T>) matchingConstructor, implementation); return ip; } else { errors.addMessage( "%s has @AssistedInject constructors, but none of them match the" + " parameters in method %s. Unable to create AssistedInject factory.", implementation, method); throw errors.toException(); } } }
/** * Finds a constructor suitable for the method. If the implementation contained any constructors * marked with {@link AssistedInject}, this requires all {@link Assisted} parameters to exactly * match the parameters (in any order) listed in the method. Otherwise, if no * {@link AssistedInject} constructors exist, this will default to looking for an * {@literal @}{@link Inject} constructor. */ private InjectionPoint findMatchingConstructorInjectionPoint( Method method, Key<?> returnType, TypeLiteral<?> implementation, List<Key<?>> paramList) throws ErrorsException { Errors errors = new Errors(method); if(returnType.getTypeLiteral().equals(implementation)) { errors = errors.withSource(implementation); } else { errors = errors.withSource(returnType).withSource(implementation); } Class<?> rawType = implementation.getRawType(); if (Modifier.isInterface(rawType.getModifiers())) { errors.addMessage( "%s is an interface, not a concrete class. Unable to create AssistedInject factory.", implementation); throw errors.toException(); } else if (Modifier.isAbstract(rawType.getModifiers())) { errors.addMessage( "%s is abstract, not a concrete class. Unable to create AssistedInject factory.", implementation); throw errors.toException(); } else if (Classes.isInnerClass(rawType)) { errors.cannotInjectInnerClass(rawType); throw errors.toException(); } Constructor<?> matchingConstructor = null; boolean anyAssistedInjectConstructors = false; // Look for AssistedInject constructors... for (Constructor<?> constructor : rawType.getDeclaredConstructors()) { if (constructor.isAnnotationPresent(AssistedInject.class)) { anyAssistedInjectConstructors = true; if (constructorHasMatchingParams(implementation, constructor, paramList, errors)) { if (matchingConstructor != null) { errors .addMessage( "%s has more than one constructor annotated with @AssistedInject" + " that matches the parameters in method %s. Unable to create AssistedInject factory.", implementation, method); throw errors.toException(); } else { matchingConstructor = constructor; } } } } if(!anyAssistedInjectConstructors) { // If none existed, use @Inject. try { return InjectionPoint.forConstructorOf(implementation); } catch(ConfigurationException e) { errors.merge(e.getErrorMessages()); throw errors.toException(); } } else { // Otherwise, use it or fail with a good error message. if(matchingConstructor != null) { // safe because we got the constructor from this implementation. @SuppressWarnings("unchecked") InjectionPoint ip = InjectionPoint.forConstructor( (Constructor)matchingConstructor, implementation); return ip; } else { errors.addMessage( "%s has @AssistedInject constructors, but none of them match the" + " parameters in method %s. Unable to create AssistedInject factory.", implementation, method); throw errors.toException(); } } }