Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#1813 - Allow client definitions for injectable points #1814

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions core/src/com/google/inject/InjectionPointProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.google.inject;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;

/**
* A provider for injection points other than jakarta.@Inject or children of
*/
public interface InjectionPointProvider
{
Class<? extends Annotation> injectionPoint(AnnotatedElement member);
}
178 changes: 99 additions & 79 deletions core/src/com/google/inject/spi/InjectionPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,16 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.ObjectArrays;
import com.google.inject.ConfigurationException;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.*;
import com.google.inject.internal.Annotations;
import com.google.inject.internal.DeclaredMembers;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
import com.google.inject.internal.KotlinSupport;
import com.google.inject.internal.Nullability;
import com.google.inject.internal.util.Classes;
import jakarta.annotation.Nullable;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.AnnotatedType;
Expand All @@ -42,14 +41,7 @@
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -77,13 +69,13 @@ public final class InjectionPoint {
this.declaringType = declaringType;
this.optional = optional;
this.dependencies =
forMember(
new Errors(method),
method,
declaringType,
method.getAnnotatedParameterTypes(),
method.getParameterAnnotations(),
KotlinSupport.getInstance().getIsParameterKotlinNullablePredicate(method));
forMember(
new Errors(method),
method,
declaringType,
method.getAnnotatedParameterTypes(),
method.getParameterAnnotations(),
KotlinSupport.getInstance().getIsParameterKotlinNullablePredicate(method));
}

InjectionPoint(TypeLiteral<?> declaringType, Constructor<?> constructor) {
Expand All @@ -94,13 +86,13 @@ public final class InjectionPoint {
KotlinSupport.getInstance().checkConstructorParameterAnnotations(constructor, errors);

this.dependencies =
forMember(
errors,
constructor,
declaringType,
constructor.getAnnotatedParameterTypes(),
constructor.getParameterAnnotations(),
KotlinSupport.getInstance().getIsParameterKotlinNullablePredicate(constructor));
forMember(
errors,
constructor,
declaringType,
constructor.getAnnotatedParameterTypes(),
constructor.getParameterAnnotations(),
KotlinSupport.getInstance().getIsParameterKotlinNullablePredicate(constructor));
}

InjectionPoint(TypeLiteral<?> declaringType, Field field, boolean optional) {
Expand All @@ -122,19 +114,19 @@ public final class InjectionPoint {
errors.throwConfigurationExceptionIfErrorsExist();

boolean allowsNull =
Nullability.hasNullableAnnotation(annotations)
|| Nullability.hasNullableAnnotation(typeUseAnnotations)
|| KotlinSupport.getInstance().isNullable(field);
Nullability.hasNullableAnnotation(annotations)
|| Nullability.hasNullableAnnotation(typeUseAnnotations)
|| KotlinSupport.getInstance().isNullable(field);
this.dependencies = ImmutableList.<Dependency<?>>of(newDependency(key, allowsNull, -1));
}

private ImmutableList<Dependency<?>> forMember(
Errors errors,
Member member,
TypeLiteral<?> type,
AnnotatedType[] annotatedTypes,
Annotation[][] parameterAnnotationsPerParameter,
Predicate<Integer> isParameterKotlinNullable) {
Errors errors,
Member member,
TypeLiteral<?> type,
AnnotatedType[] annotatedTypes,
Annotation[][] parameterAnnotationsPerParameter,
Predicate<Integer> isParameterKotlinNullable) {
List<Dependency<?>> dependencies = Lists.newArrayList();
int index = 0;

Expand All @@ -144,9 +136,9 @@ private ImmutableList<Dependency<?>> forMember(
Annotation[] parameterAnnotations = parameterAnnotationsPerParameter[index];
Key<?> key = Annotations.getKey(parameterType, member, parameterAnnotations, errors);
boolean isNullable =
Nullability.hasNullableAnnotation(parameterAnnotations)
|| Nullability.hasNullableAnnotation(typeAnnotations)
|| isParameterKotlinNullable.test(index);
Nullability.hasNullableAnnotation(parameterAnnotations)
|| Nullability.hasNullableAnnotation(typeAnnotations)
|| isParameterKotlinNullable.test(index);
dependencies.add(newDependency(key, isNullable, index));
index++;
} catch (ConfigurationException e) {
Expand Down Expand Up @@ -215,8 +207,8 @@ public TypeLiteral<?> getDeclaringType() {
@Override
public boolean equals(Object o) {
return o instanceof InjectionPoint
&& member.equals(((InjectionPoint) o).member)
&& declaringType.equals(((InjectionPoint) o).declaringType);
&& member.equals(((InjectionPoint) o).member)
&& declaringType.equals(((InjectionPoint) o).declaringType);
}

@Override
Expand Down Expand Up @@ -249,11 +241,11 @@ public static <T> InjectionPoint forConstructor(Constructor<T> constructor) {
* @since 3.0
*/
public static <T> InjectionPoint forConstructor(
Constructor<T> constructor, TypeLiteral<? extends T> type) {
Constructor<T> constructor, TypeLiteral<? extends T> type) {
if (type.getRawType() != constructor.getDeclaringClass()) {
new Errors(type)
.constructorNotDefinedByType(constructor, type)
.throwConfigurationExceptionIfErrorsExist();
.constructorNotDefinedByType(constructor, type)
.throwConfigurationExceptionIfErrorsExist();
}

return new InjectionPoint(type, constructor);
Expand Down Expand Up @@ -296,15 +288,15 @@ public static InjectionPoint forConstructorOf(TypeLiteral<?> type, boolean atInj
Errors errors = new Errors(rawType);

List<Constructor<?>> atInjectConstructors =
Arrays.stream(rawType.getDeclaredConstructors())
.filter(InjectionPoint::isInjectableConstructor)
.collect(Collectors.toList());
Arrays.stream(rawType.getDeclaredConstructors())
.filter(InjectionPoint::isInjectableConstructor)
.collect(Collectors.toList());

Constructor<?> injectableConstructor = null;
atInjectConstructors.stream()
.filter(constructor -> constructor.isAnnotationPresent(Inject.class))
.filter(constructor -> constructor.getAnnotation(Inject.class).optional())
.forEach(errors::optionalConstructor);
.filter(constructor -> constructor.isAnnotationPresent(Inject.class))
.filter(constructor -> constructor.getAnnotation(Inject.class).optional())
.forEach(errors::optionalConstructor);

if (atInjectConstructors.size() > 1) {
errors.tooManyConstructors(rawType);
Expand All @@ -329,7 +321,7 @@ public static InjectionPoint forConstructorOf(TypeLiteral<?> type, boolean atInj

// Disallow private constructors on non-private classes (unless they have @Inject)
if (Modifier.isPrivate(noArgConstructor.getModifiers())
&& !Modifier.isPrivate(rawType.getModifiers())) {
&& !Modifier.isPrivate(rawType.getModifiers())) {
errors.missingConstructor(type);
throw new ConfigurationException(errors.getMessages());
}
Expand All @@ -344,7 +336,7 @@ public static InjectionPoint forConstructorOf(TypeLiteral<?> type, boolean atInj

private static boolean isInjectableConstructor(Constructor<?> constructor) {
return constructor.isAnnotationPresent(Inject.class)
|| constructor.isAnnotationPresent(jakarta.inject.Inject.class);
|| constructor.isAnnotationPresent(jakarta.inject.Inject.class);
}

/**
Expand Down Expand Up @@ -454,8 +446,8 @@ public static Set<InjectionPoint> forInstanceMethodsAndFields(Class<?> type) {
/** Returns true if the binding annotation is in the wrong place. */
private static boolean checkForMisplacedBindingAnnotations(Member member, Errors errors) {
Annotation misplacedBindingAnnotation =
Annotations.findBindingAnnotation(
errors, member, ((AnnotatedElement) member).getAnnotations());
Annotations.findBindingAnnotation(
errors, member, ((AnnotatedElement) member).getAnnotations());
if (misplacedBindingAnnotation == null) {
return false;
}
Expand Down Expand Up @@ -538,7 +530,35 @@ public boolean isFinal() {

static Annotation getAtInject(AnnotatedElement member) {
Annotation a = member.getAnnotation(jakarta.inject.Inject.class);
return a == null ? member.getAnnotation(Inject.class) : a;
a = a == null ? member.getAnnotation(Inject.class) : a;
//#GedMarc update to allow alternative injection pointers
if(a == null) {
List<Class<? extends Annotation>> annotations = new ArrayList<>();
ServiceLoader<InjectionPointProvider> load = ServiceLoader.load(InjectionPointProvider.class);
load.forEach(iPoint -> {
annotations.add(iPoint.injectionPoint(member));
});
for (Class<? extends Annotation> annotation : annotations) {
//noinspection ConstantValue
if (a == null) {
a= member.getAnnotation(annotation);
if (a != null) {
a = new Inject(){
@Override
public Class<? extends Annotation> annotationType() {
return annotation;
}
@Override
public boolean optional() {
return member.isAnnotationPresent(Nullable.class);
}
};
break;
}
}
}
}
return a;
}

/** Linked list of injectable members. */
Expand Down Expand Up @@ -613,7 +633,7 @@ static class OverrideIndex {
* InjectableMethod#overrodeGuiceInject} is set to true
*/
boolean removeIfOverriddenBy(
Method method, boolean alwaysRemove, InjectableMethod injectableMethod) {
Method method, boolean alwaysRemove, InjectableMethod injectableMethod) {
if (position == Position.TOP) {
// If we're at the top of the hierarchy, there's nothing to override.
return false;
Expand All @@ -624,8 +644,8 @@ boolean removeIfOverriddenBy(
// methods in the parent class.
bySignature = new HashMap<>();
for (InjectableMember member = injectableMembers.head;
member != null;
member = member.next) {
member != null;
member = member.next) {
if (!(member instanceof InjectableMethod)) {
continue;
}
Expand All @@ -648,7 +668,7 @@ boolean removeIfOverriddenBy(
InjectableMethod possiblyOverridden = iterator.next();
if (overrides(method, possiblyOverridden.method)) {
boolean wasGuiceInject =
!possiblyOverridden.specInject || possiblyOverridden.overrodeGuiceInject;
!possiblyOverridden.specInject || possiblyOverridden.overrodeGuiceInject;
if (injectableMethod != null) {
injectableMethod.overrodeGuiceInject = wasGuiceInject;
}
Expand Down Expand Up @@ -680,9 +700,9 @@ void add(InjectableMethod injectableMethod) {
// Try to reuse the signature we created during removal
@SuppressWarnings("ReferenceEquality")
Signature signature =
injectableMethod.method == lastMethod
? lastSignature
: new Signature(injectableMethod.method);
injectableMethod.method == lastMethod
? lastSignature
: new Signature(injectableMethod.method);
bySignature.computeIfAbsent(signature, k -> new ArrayList<>()).add(injectableMethod);
}
}
Expand All @@ -698,7 +718,7 @@ void add(InjectableMethod injectableMethod) {
* @param errors used to record errors
*/
private static Set<InjectionPoint> getInjectionPoints(
final TypeLiteral<?> type, boolean statics, Errors errors) {
final TypeLiteral<?> type, boolean statics, Errors errors) {
InjectableMembers injectableMembers = new InjectableMembers();
OverrideIndex overrideIndex = null;

Expand Down Expand Up @@ -735,19 +755,19 @@ private static Set<InjectionPoint> getInjectionPoints(
if (atInject != null) {
InjectableMethod injectableMethod = new InjectableMethod(current, method, atInject);
if (checkForMisplacedBindingAnnotations(method, errors)
|| !isValidMethod(injectableMethod, errors)) {
|| !isValidMethod(injectableMethod, errors)) {
if (overrideIndex != null) {
boolean removed =
overrideIndex.removeIfOverriddenBy(method, false, injectableMethod);
overrideIndex.removeIfOverriddenBy(method, false, injectableMethod);
if (removed) {
logger.log(
Level.WARNING,
"Method: {0} is not a valid injectable method ("
+ "because it either has misplaced binding annotations "
+ "or specifies type parameters) but is overriding a method that is "
+ "valid. Because it is not valid, the method will not be injected. "
+ "To fix this, make the method a valid injectable method.",
method);
Level.WARNING,
"Method: {0} is not a valid injectable method ("
+ "because it either has misplaced binding annotations "
+ "or specifies type parameters) but is overriding a method that is "
+ "valid. Because it is not valid, the method will not be injected. "
+ "To fix this, make the method a valid injectable method.",
method);
}
}
continue;
Expand Down Expand Up @@ -775,12 +795,12 @@ private static Set<InjectionPoint> getInjectionPoints(
boolean removed = overrideIndex.removeIfOverriddenBy(method, false, null);
if (removed) {
logger.log(
Level.WARNING,
"Method: {0} is not annotated with @Inject but "
+ "is overriding a method that is annotated with @jakarta.inject.Inject."
+ "Because it is not annotated with @Inject, the method will not be "
+ "injected. To fix this, annotate the method with @Inject.",
method);
Level.WARNING,
"Method: {0} is not annotated with @Inject but "
+ "is overriding a method that is annotated with @jakarta.inject.Inject."
+ "Because it is not annotated with @Inject, the method will not be "
+ "injected. To fix this, annotate the method with @Inject.",
method);
}
}
}
Expand Down Expand Up @@ -833,8 +853,8 @@ private static Method[] getDeclaredMethods(TypeLiteral<?> type) {
*/
private static boolean isEligibleForInjection(Method method, boolean statics) {
return Modifier.isStatic(method.getModifiers()) == statics
&& !method.isBridge()
&& !method.isSynthetic();
&& !method.isBridge()
&& !method.isSynthetic();
}

private static boolean isValidMethod(InjectableMethod injectableMethod, Errors errors) {
Expand Down