diff --git a/reflect/src/main/java/dagger/reflect/ReflectiveJustInTimeLookupFactory.java b/reflect/src/main/java/dagger/reflect/ReflectiveJustInTimeLookupFactory.java index 40bba4a0..adb2e7f7 100644 --- a/reflect/src/main/java/dagger/reflect/ReflectiveJustInTimeLookupFactory.java +++ b/reflect/src/main/java/dagger/reflect/ReflectiveJustInTimeLookupFactory.java @@ -43,7 +43,12 @@ final class ReflectiveJustInTimeLookupFactory implements JustInTimeLookup.Factor } Annotation scope = findScope(cls.getAnnotations()); - Binding binding = new UnlinkedJustInTimeBinding<>(cls, target, typeArguments); + Binding binding; + if (TypeUtil.isAndroidInjectorFactory(type)) { + binding = new UnlinkedJustInTimeContributesAndroidInjectorBinding<>(cls, target, typeArguments); + } else { + binding = new UnlinkedJustInTimeBinding<>(cls, target, typeArguments); + } return new JustInTimeLookup(scope, binding); } diff --git a/reflect/src/main/java/dagger/reflect/Scope.java b/reflect/src/main/java/dagger/reflect/Scope.java index 64bb15fe..7807c1a5 100644 --- a/reflect/src/main/java/dagger/reflect/Scope.java +++ b/reflect/src/main/java/dagger/reflect/Scope.java @@ -35,6 +35,11 @@ private Scope( this.parent = parent; } + @Nullable + public Scope getParent() { + return parent; + } + @Override public String toString() { return "Scope" + annotations; diff --git a/reflect/src/main/java/dagger/reflect/TypeUtil.java b/reflect/src/main/java/dagger/reflect/TypeUtil.java index 36e98c10..ae144f11 100644 --- a/reflect/src/main/java/dagger/reflect/TypeUtil.java +++ b/reflect/src/main/java/dagger/reflect/TypeUtil.java @@ -15,6 +15,8 @@ */ package dagger.reflect; + +import dagger.android.DispatchingAndroidInjector; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -28,6 +30,15 @@ final class TypeUtil { private TypeUtil() {} + static boolean isAndroidInjectorFactory(Type type) { + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedKeyType = (ParameterizedType) type; + Type rawKeyType = parameterizedKeyType.getRawType(); + return DispatchingAndroidInjector.class.equals(rawKeyType); + } + return false; + } + /** * Returns a type that is functionally equal but not necessarily equal according to {@link * Object#equals(Object) Object.equals()}. diff --git a/reflect/src/main/java/dagger/reflect/UnlinkedJustInTimeContributesAndroidInjectorBinding.java b/reflect/src/main/java/dagger/reflect/UnlinkedJustInTimeContributesAndroidInjectorBinding.java new file mode 100644 index 00000000..1d74b7e0 --- /dev/null +++ b/reflect/src/main/java/dagger/reflect/UnlinkedJustInTimeContributesAndroidInjectorBinding.java @@ -0,0 +1,80 @@ +package dagger.reflect; + +import static dagger.reflect.Reflection.findQualifier; + +import dagger.MembersInjector; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Map; +import org.jetbrains.annotations.Nullable; + +final class UnlinkedJustInTimeContributesAndroidInjectorBinding extends Binding.UnlinkedBinding { + private final Class cls; + private final Constructor constructor; + // Type arguments might be used as types for this binding's parameterized constructor parameters. + private @Nullable Type[] concreteTypeArguments; + + UnlinkedJustInTimeContributesAndroidInjectorBinding( + Class cls, Constructor constructor, @Nullable Type[] concreteTypeArguments) { + this.cls = cls; + this.constructor = constructor; + this.concreteTypeArguments = concreteTypeArguments; + } + + @Override + public LinkedBinding link(Linker linker, Scope scope) { + Type[] parameterTypes = constructor.getGenericParameterTypes(); + Annotation[][] parameterAnnotations = constructor.getParameterAnnotations(); + + LinkedBinding[] bindings = new LinkedBinding[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + Type parameterType = parameterTypes[i]; + Key key = + Key.of(findQualifier(parameterAnnotations[i]), parameterType); + bindings[i] = linker.get(key); + addParentMappings(key, scope.getParent(), linker); + + } + + MembersInjector membersInjector = ReflectiveMembersInjector.create(cls, scope); + + return new LinkedJustInTimeBinding<>(constructor, bindings, membersInjector); + } + + private void addParentMappings(Key key,@Nullable Scope parentScope, Linker linker) { + if (parentScope == null) { + return; + } + + Binding.LinkedBinding bindingLinked = linker.get(key); + Object value = bindingLinked.get(); + + if (value instanceof Map) { + Map, Binding> bindingValue = (Map, Binding>) value; + Binding.LinkedBinding parentBinding = parentScope.getBinding(key); + if (parentBinding != null) { + Object parentBindingValue = parentBinding.get(); + if (parentBindingValue instanceof Map) { + Map, LinkedBinding> parentBindingValueMap = (Map, LinkedBinding>) parentBindingValue; + bindingValue.putAll(parentBindingValueMap); + } + } + } + + addParentMappings(key, parentScope.getParent(), linker); + } + + @Override + public String toString() { + return "@Inject[" + cls.getName() + getTypeArgumentsStringOrEmpty() + ".(…)]"; + } + + private String getTypeArgumentsStringOrEmpty() { + if (concreteTypeArguments == null) { + return ""; + } + return Arrays.toString(concreteTypeArguments); + } +}