Java tutorial
/* * Copyright (C) 2014 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen; import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.auto.common.MoreTypes.asExecutable; import static com.google.auto.common.MoreTypes.isType; import static com.google.auto.common.MoreTypes.isTypeOf; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.getOnlyElement; import static dagger.internal.codegen.InjectionAnnotations.getQualifier; import static dagger.internal.codegen.MapKeys.getMapKey; import static dagger.internal.codegen.MapKeys.getUnwrappedMapKeyType; import static dagger.internal.codegen.MoreAnnotationMirrors.unwrapOptionalEquivalence; import static dagger.internal.codegen.MoreAnnotationMirrors.wrapOptionalInEquivalence; import static dagger.internal.codegen.Optionals.firstPresent; import static dagger.internal.codegen.Util.*; import static javax.lang.model.element.ElementKind.METHOD; import com.google.auto.common.AnnotationMirrors; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.auto.value.AutoValue; import com.google.auto.value.extension.memoized.Memoized; import com.google.common.base.Equivalence; import com.google.common.base.Joiner; import com.google.common.collect.*; import com.google.common.util.concurrent.ListenableFuture; import com.squareup.javapoet.ClassName; import dagger.Binds; import dagger.BindsOptionalOf; import dagger.MapKey; import dagger.multibindings.*; import dagger.producers.Produced; import dagger.producers.Producer; import dagger.producers.Production; import dagger.producers.internal.ProductionImplementation; import dagger.producers.monitoring.ProductionComponentMonitor; import dagger.releasablereferences.ForReleasableReferences; import dagger.releasablereferences.ReleasableReferenceManager; import dagger.releasablereferences.TypedReleasableReferenceManager; import java.io.Serializable; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.Executor; import java.util.stream.Stream; import javax.inject.Named; import javax.inject.Provider; import javax.inject.Qualifier; import javax.lang.model.element.*; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleTypeVisitor6; import javax.lang.model.util.Types; /** * Represents a unique combination of {@linkplain TypeMirror type} and * {@linkplain Qualifier qualifier} to which binding can occur. * * @author Gregory Kick */ @AutoValue abstract class Key implements Serializable { /** An object that is associated with a {@link Key}. */ interface HasKey extends Serializable { /** The key associated with this object. */ Key key(); } /** * A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix * for the type of this key. * * Despite documentation in {@link AnnotationMirror}, equals and hashCode aren't implemented * to represent logical equality, so {@link AnnotationMirrors#equivalence()} * provides this facility. */ abstract Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedQualifier(); /** * The type represented by this key. * * As documented in {@link TypeMirror}, equals and hashCode aren't implemented to represent * logical equality, so {@link MoreTypes#equivalence()} wraps this type. */ abstract Equivalence.Wrapper<TypeMirror> wrappedType(); /** * Distinguishes keys for multibinding contributions that share a {@link #type()} and {@link * #qualifier()}. * * <p>Each multibound map and set has a {@linkplain * ProvisionBinding.Factory#syntheticMultibinding(Key, Iterable) synthetic multibinding} that * depends on the specific contributions to that map or set using keys that identify those * multibinding contributions. * * <p>Absent except for multibinding contributions. */ abstract Optional<MultibindingContributionIdentifier> multibindingContributionIdentifier(); abstract Builder toBuilder(); @Memoized @Override public abstract int hashCode(); static Builder builder(TypeMirror type) { return new dagger.internal.codegen.AutoValue_Key.Builder().type(type); } @AutoValue.Builder abstract static class Builder { abstract Builder wrappedType(Equivalence.Wrapper<TypeMirror> wrappedType); Builder type(TypeMirror type) { return wrappedType(MoreTypes.equivalence().wrap(checkNotNull(type))); } abstract Builder wrappedQualifier(Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedQualifier); abstract Builder wrappedQualifier(Equivalence.Wrapper<AnnotationMirror> wrappedQualifier); Builder qualifier(AnnotationMirror qualifier) { return wrappedQualifier(AnnotationMirrors.equivalence().wrap(checkNotNull(qualifier))); } Builder qualifier(Optional<AnnotationMirror> qualifier) { return wrappedQualifier(wrapOptionalInEquivalence(checkNotNull(qualifier))); } Builder qualifier(TypeElement annotationType) { return qualifier(SimpleAnnotationMirror.of(annotationType)); } abstract Builder multibindingContributionIdentifier( Optional<MultibindingContributionIdentifier> identifier); abstract Builder multibindingContributionIdentifier(MultibindingContributionIdentifier identifier); abstract Key build(); } /** * An object that identifies a multibinding contribution method and the module class that * contributes it to the graph. * * @see Key#multibindingContributionIdentifier() */ static final class MultibindingContributionIdentifier { private final String identifierString; private final ExecutableElement bindingMethod; private final TypeElement contributingModule; MultibindingContributionIdentifier(ExecutableElement bindingMethod, TypeElement contributingModule) { this.bindingMethod = bindingMethod; this.contributingModule = contributingModule; this.identifierString = String.format("%s#%s", contributingModule.getQualifiedName(), bindingMethod.getSimpleName()); } /** * {@inheritDoc} * * <p>The returned string is human-readable and distinguishes the keys in the same way as the * whole object. */ @Override public String toString() { return identifierString; } public ClassName getDelegateTypeName() { final TypeMirror returnType = bindingMethod.getReturnType(); // find qualifier annotations final Optional<? extends AnnotationMirror> qualifier = bindingMethod.getAnnotationMirrors().stream() .filter(this::hasQualifierAnnotation).findFirst(); // find mapkey annotations final Optional<? extends AnnotationMirror> mapKey = bindingMethod.getAnnotationMirrors().stream() .filter(this::hasMapKeyAnnotation).findFirst(); Optional<String> mapValue = Optional.empty(); Optional<String> qualifierValue = Optional.empty(); String simpleMapKeyName = ""; String simpleMapValueName = ""; String simpleQualifierName = ""; String simpleQualifierValue = ""; if (mapKey.isPresent()) { mapValue = mapKey.get().getElementValues().entrySet().stream() .filter(e -> e.getKey().getSimpleName().toString().equals("value")) .map(e -> Util.extractClassName(e.getValue().getValue().toString().replace(".class", ""))) .findFirst(); simpleMapKeyName = MoreAnnotationMirrors.simpleName(mapKey.get()).toString(); if (mapValue.isPresent()) { simpleMapValueName = mapValue.get(); } } if (qualifier.isPresent()) { qualifierValue = qualifier.get().getElementValues().entrySet().stream() .filter(e -> e.getKey().getSimpleName().toString().equals("value")) .filter(e -> e.getKey().getReturnType().toString().equals(String.class.getName())) .map(e -> e.getValue().getValue().toString()).findFirst(); simpleQualifierName = MoreAnnotationMirrors.simpleName(qualifier.get()).toString(); if (qualifierValue.isPresent()) { simpleQualifierValue = qualifierValue.get(); } } StringBuilder sb = new StringBuilder(); sb.append(capitalize(simpleMapKeyName)); sb.append(capitalize(simpleMapValueName)); if (qualifier.isPresent()) { sb.append(capitalize(simpleQualifierName)); if (!simpleQualifierValue.isEmpty()) { sb.append(capitalize(simpleQualifierValue)); } else { sb.append(capitalize(extractClassName(typeToString(returnType)))); } } else { sb.append(capitalize(extractClassName(typeToString(returnType)))); } return ClassName.bestGuess(String.format("delegates.%sDelegate", sb.toString())); } public String getDelegateFieldName() { final TypeMirror returnType = bindingMethod.getReturnType(); // find qualifier annotations final Optional<? extends AnnotationMirror> qualifier = bindingMethod.getAnnotationMirrors().stream() .filter(this::hasQualifierAnnotation).findFirst(); // find mapkey annotations final Optional<? extends AnnotationMirror> mapKey = bindingMethod.getAnnotationMirrors().stream() .filter(this::hasMapKeyAnnotation).findFirst(); Optional<String> mapValue = Optional.empty(); Optional<String> qualifierValue = Optional.empty(); String simpleMapKeyName = ""; String simpleMapValueName = ""; String simpleQualifierName = ""; String simpleQualifierValue = ""; if (mapKey.isPresent()) { mapValue = mapKey.get().getElementValues().entrySet().stream() .filter(e -> e.getKey().getSimpleName().contentEquals("value")) .map(e -> Util.extractClassName(e.getValue().getValue().toString().replace(".class", ""))) .findFirst(); simpleMapKeyName = MoreAnnotationMirrors.simpleName(mapKey.get()).toString(); if (mapValue.isPresent()) { simpleMapValueName = mapValue.get(); } } if (qualifier.isPresent()) { qualifierValue = qualifier.get().getElementValues().entrySet().stream() .filter(e -> e.getKey().getSimpleName().contentEquals("value")) .filter(e -> e.getKey().getReturnType().toString().equals(String.class.getName())) .map(e -> e.getValue().getValue().toString()).findFirst(); simpleQualifierName = MoreAnnotationMirrors.simpleName(qualifier.get()).toString(); if (qualifierValue.isPresent()) { simpleQualifierValue = qualifierValue.get(); } } StringBuilder sb = new StringBuilder(); sb.append(lowerCaseFirstLetter(simpleMapKeyName)); sb.append(transformValue(simpleMapValueName, sb)); if (qualifier.isPresent()) { sb.append(transformValue(simpleQualifierName, sb)); if (!simpleQualifierValue.isEmpty()) { sb.append(transformValue(simpleQualifierValue, sb)); } else { sb.append(transformValue(extractClassName(typeToString(returnType)), sb)); } } else { sb.append(transformValue(extractClassName(typeToString(returnType)), sb)); } return sb.toString(); } protected String transformValue(String simpleMapValueName, StringBuilder sb) { return sb.length() == 0 ? lowerCaseFirstLetter(simpleMapValueName) : capitalize(simpleMapValueName); } private boolean hasMapKeyAnnotation(AnnotationMirror o) { return MoreElements.isAnnotationPresent(o.getAnnotationType().asElement(), MapKey.class); } private boolean hasQualifierAnnotation(AnnotationMirror o) { return MoreElements.isAnnotationPresent(o.getAnnotationType().asElement(), Qualifier.class); } private static String getCapitalizedAnnotationValue(AnnotationMirror annotation) { final Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = annotation .getElementValues(); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : elementValues .entrySet()) { if (entry.getKey().getSimpleName().contentEquals("value")) { final String original = entry.getValue().getValue().toString(); if (!original.isEmpty()) { return capitalize(original); } } } throw new IllegalStateException("value not found"); } private static String capitalize(String original) { if (original == null || original.length() == 0) { return original; } return original.substring(0, 1).toUpperCase() + original.substring(1); } private static java.util.Optional<? extends AnnotationMirror> getAnnotationMirror(TypeMirror typeMirror) { final ImmutableList<String> annotations = ImmutableList.of(Named.class.getName(), StringKey.class.getName(), IntKey.class.getName(), LongKey.class.getName(), MapKey.class.getName(), ClassKey.class.getName()); return typeMirror.getAnnotationMirrors().stream() .filter(e -> Util.isAnnotationPresent(MoreTypes.asElement(typeMirror), e.getAnnotationType())) .findFirst(); } private static java.util.Optional<? extends AnnotationMirror> getAnnotationMirror( ExecutableElement method) { final ImmutableList<String> annotations = ImmutableList.of(Named.class.getName(), StringKey.class.getName(), IntKey.class.getName(), LongKey.class.getName(), MapKey.class.getName(), ClassKey.class.getName()); return method.getAnnotationMirrors().stream() .filter(e -> annotations.contains(e.getAnnotationType().toString())).findFirst(); } @Override public boolean equals(Object obj) { return obj instanceof MultibindingContributionIdentifier && ((MultibindingContributionIdentifier) obj).identifierString.equals(this.identifierString); } @Override public int hashCode() { return identifierString.hashCode(); } } /** * A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix * for the type of this key. */ Optional<AnnotationMirror> qualifier() { return unwrapOptionalEquivalence(wrappedQualifier()); } /** * The type represented by this key. */ TypeMirror type() { return wrappedType().get(); } /** * A key whose {@link #qualifier()} and {@link #type()} are equivalent to this one's, but without * a {@link #multibindingContributionIdentifier()}. */ Key withoutMultibindingContributionIdentifier() { return toBuilder().multibindingContributionIdentifier(Optional.empty()).build(); } boolean isValidMembersInjectionKey() { return !qualifier().isPresent() && type().getKind().equals(TypeKind.DECLARED); } /** * Returns {@code true} if this is valid as an implicit key (that is, if it's valid for a * just-in-time binding by discovering an {@code @Inject} constructor). */ boolean isValidImplicitProvisionKey(Types types) { return isValidImplicitProvisionKey(qualifier(), type(), types); } /** * Returns {@code true} if a key with {@code qualifier} and {@code type} is valid as an implicit * key (that is, if it's valid for a just-in-time binding by discovering an {@code @Inject} * constructor). */ static boolean isValidImplicitProvisionKey(Optional<? extends AnnotationMirror> qualifier, TypeMirror type, final Types types) { // Qualifiers disqualify implicit provisioning. if (qualifier.isPresent()) { return false; } return type.accept(new SimpleTypeVisitor6<Boolean, Void>(false) { @Override public Boolean visitDeclared(DeclaredType type, Void ignored) { // Non-classes or abstract classes aren't allowed. TypeElement element = MoreElements.asType(type.asElement()); if (!element.getKind().equals(ElementKind.CLASS) || element.getModifiers().contains(Modifier.ABSTRACT)) { return false; } // If the key has type arguments, validate that each type argument is declared. // Otherwise the type argument may be a wildcard (or other type), and we can't // resolve that to actual types. for (TypeMirror arg : type.getTypeArguments()) { if (arg.getKind() != TypeKind.DECLARED) { return false; } } // Also validate that the key is not the erasure of a generic type. // If it is, that means the user referred to Foo<T> as just 'Foo', // which we don't allow. (This is a judgement call -- we *could* // allow it and instantiate the type bounds... but we don't.) return MoreTypes.asDeclared(element.asType()).getTypeArguments().isEmpty() || !types.isSameType(types.erasure(element.asType()), type); } }, null); } /** * {@inheritDoc} * * <p>The returned string is equal to another key's if and only if this key is {@link * #equals(Object)} to it. */ @Override public String toString() { return Joiner.on(' ').skipNulls().join(qualifier().orElse(null), type(), multibindingContributionIdentifier().orElse(null)); } /** * Indexes {@code haveKeys} by {@link HasKey#key()}. */ static <T extends HasKey> ImmutableSetMultimap<Key, T> indexByKey(Iterable<T> haveKeys) { return ImmutableSetMultimap.copyOf(Multimaps.index(haveKeys, HasKey::key)); } static final class Factory { private final Types types; private final Elements elements; Factory(Types types, Elements elements) { this.types = checkNotNull(types); this.elements = checkNotNull(elements); } private TypeElement getClassElement(Class<?> cls) { return elements.getTypeElement(cls.getCanonicalName()); } private TypeMirror boxPrimitives(TypeMirror type) { return type.getKind().isPrimitive() ? types.boxedClass((PrimitiveType) type).asType() : type; } private DeclaredType setOf(TypeMirror elementType) { return types.getDeclaredType(getClassElement(Set.class), boxPrimitives(elementType)); } private DeclaredType mapOf(TypeMirror keyType, TypeMirror valueType) { return types.getDeclaredType(getClassElement(Map.class), boxPrimitives(keyType), boxPrimitives(valueType)); } /** Returns {@code Map<KeyType, FrameworkType<ValueType>>}. */ private TypeMirror mapOfFrameworkType(TypeMirror keyType, TypeElement frameworkType, TypeMirror valueType) { return mapOf(keyType, types.getDeclaredType(frameworkType, boxPrimitives(valueType))); } private DeclaredType typedReleasableReferenceManagerOf(DeclaredType metadataType) { return types.getDeclaredType(getClassElement(TypedReleasableReferenceManager.class), metadataType); } Key forComponentMethod(ExecutableElement componentMethod) { checkArgument(componentMethod.getKind().equals(METHOD)); return forMethod(componentMethod, componentMethod.getReturnType()); } Key forProductionComponentMethod(ExecutableElement componentMethod) { checkArgument(componentMethod.getKind().equals(METHOD)); TypeMirror returnType = componentMethod.getReturnType(); TypeMirror keyType = isTypeOf(ListenableFuture.class, returnType) ? getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments()) : returnType; return forMethod(componentMethod, keyType); } Key forSubcomponentBuilderMethod(ExecutableElement subcomponentBuilderMethod, DeclaredType declaredContainer) { checkArgument(subcomponentBuilderMethod.getKind().equals(METHOD)); ExecutableType resolvedMethod = asExecutable( types.asMemberOf(declaredContainer, subcomponentBuilderMethod)); return builder(resolvedMethod.getReturnType()).build(); } Key forSubcomponentBuilder(TypeMirror builderType) { return builder(builderType).build(); } Key forProvidesMethod(ExecutableElement method, TypeElement contributingModule) { return forBindingMethod(method, contributingModule, Optional.of(getClassElement(Provider.class))); } Key forProducesMethod(ExecutableElement method, TypeElement contributingModule) { return forBindingMethod(method, contributingModule, Optional.of(getClassElement(Producer.class))); } /** Returns the key bound by a {@link Binds} method. */ Key forBindsMethod(ExecutableElement method, TypeElement contributingModule) { checkArgument(isAnnotationPresent(method, Binds.class)); return forBindingMethod(method, contributingModule, Optional.empty()); } /** Returns the base key bound by a {@link BindsOptionalOf} method. */ Key forBindsOptionalOfMethod(ExecutableElement method, TypeElement contributingModule) { checkArgument(isAnnotationPresent(method, BindsOptionalOf.class)); return forBindingMethod(method, contributingModule, Optional.empty()); } private Key forBindingMethod(ExecutableElement method, TypeElement contributingModule, Optional<TypeElement> frameworkType) { checkArgument(method.getKind().equals(METHOD)); ExecutableType methodType = MoreTypes .asExecutable(types.asMemberOf(MoreTypes.asDeclared(contributingModule.asType()), method)); ContributionType contributionType = ContributionType.fromBindingMethod(method); TypeMirror returnType = methodType.getReturnType(); if (frameworkType.isPresent() && frameworkType.get().equals(getClassElement(Producer.class)) && isType(returnType)) { if (isTypeOf(ListenableFuture.class, returnType)) { returnType = getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments()); } else if (contributionType.equals(ContributionType.SET_VALUES) && SetType.isSet(returnType)) { SetType setType = SetType.from(returnType); if (setType.elementsAreTypeOf(ListenableFuture.class)) { returnType = types.getDeclaredType(getClassElement(Set.class), setType.unwrappedElementType(ListenableFuture.class)); } } } TypeMirror keyType = bindingMethodKeyType(returnType, method, contributionType, frameworkType); Key key = forMethod(method, keyType); return contributionType .equals(ContributionType.UNIQUE) ? key : key.toBuilder() .multibindingContributionIdentifier( new MultibindingContributionIdentifier(method, contributingModule)) .build(); } /** * Returns the key for a {@link Multibinds @Multibinds} method. * * <p>The key's type is either {@code Set<T>} or {@code Map<K, F<V>>}, where {@code F} is either * {@link Provider} or {@link Producer}, depending on {@code bindingType}. */ Key forMultibindsMethod(BindingType bindingType, ExecutableType executableType, ExecutableElement method) { checkArgument(method.getKind().equals(METHOD), "%s must be a method", method); TypeElement factoryType = elements.getTypeElement(bindingType.frameworkClass().getCanonicalName()); TypeMirror returnType = executableType.getReturnType(); TypeMirror keyType = MapType.isMap(returnType) ? mapOfFrameworkType(MapType.from(returnType).keyType(), factoryType, MapType.from(returnType).valueType()) : returnType; return forMethod(method, keyType); } private TypeMirror bindingMethodKeyType(TypeMirror returnType, ExecutableElement method, ContributionType contributionType, Optional<TypeElement> frameworkType) { switch (contributionType) { case UNIQUE: return returnType; case SET: return setOf(returnType); case MAP: if (frameworkType.isPresent()) { return mapOfFrameworkType(mapKeyType(method), frameworkType.get(), returnType); } else { return mapOf(mapKeyType(method), returnType); } case SET_VALUES: // TODO(gak): do we want to allow people to use "covariant return" here? checkArgument(SetType.isSet(returnType)); return returnType; default: throw new AssertionError(); } } /** * Returns the key for a binding associated with a {@link DelegateDeclaration}. * * If {@code delegateDeclaration} is {@code @IntoMap}, transforms the {@code Map<K, V>} key * from {@link DelegateDeclaration#key()} to {@code Map<K, FrameworkType<V>>}. If {@code * delegateDeclaration} is not a map contribution, its key is returned. */ Key forDelegateBinding(DelegateDeclaration delegateDeclaration, Class<?> frameworkType) { return delegateDeclaration.contributionType().equals(ContributionType.MAP) ? wrapMapValue(delegateDeclaration.key(), frameworkType) : delegateDeclaration.key(); } private TypeMirror mapKeyType(ExecutableElement method) { AnnotationMirror mapKeyAnnotation = getMapKey(method).get(); return MapKeys.unwrapValue(mapKeyAnnotation).isPresent() ? getUnwrappedMapKeyType(mapKeyAnnotation.getAnnotationType(), types) : mapKeyAnnotation.getAnnotationType(); } private Key forMethod(ExecutableElement method, TypeMirror keyType) { return forQualifiedType(getQualifier(method), keyType); } Key forInjectConstructorWithResolvedType(TypeMirror type) { return builder(type).build(); } Key forComponent(TypeMirror type) { return builder(type).build(); } Key forMembersInjectedType(TypeMirror type) { return builder(type).build(); } Key forQualifiedType(Optional<AnnotationMirror> qualifier, TypeMirror type) { return builder(boxPrimitives(type)).qualifier(qualifier).build(); } Key forProductionExecutor() { return builder(getClassElement(Executor.class).asType()).qualifier(getClassElement(Production.class)) .build(); } Key forProductionImplementationExecutor() { return builder(getClassElement(Executor.class).asType()) .qualifier(getClassElement(ProductionImplementation.class)).build(); } Key forProductionComponentMonitor() { return builder(getClassElement(ProductionComponentMonitor.class).asType()).build(); } /** * If {@code requestKey} is for a {@code Map<K, V>} or {@code Map<K, Produced<V>>}, returns keys * for {@code Map<K, Provider<V>>} and {@code Map<K, Producer<V>>} (if Dagger-Producers is on * the classpath). */ ImmutableSet<Key> implicitFrameworkMapKeys(Key requestKey) { return Stream.of(implicitMapProviderKeyFrom(requestKey), implicitMapProducerKeyFrom(requestKey)) .filter(Optional::isPresent).map(Optional::get).collect(toImmutableSet()); } /** * Optionally extract a {@link Key} for the underlying provision binding(s) if such a * valid key can be inferred from the given key. Specifically, if the key represents a * {@link Map}{@code <K, V>}, a key of {@code Map<K, Provider<V>>} will be returned. */ Optional<Key> implicitMapProviderKeyFrom(Key possibleMapKey) { return wrapMapKey(possibleMapKey, Provider.class); } /** * Optionally extract a {@link Key} for the underlying production binding(s) if such a * valid key can be inferred from the given key. Specifically, if the key represents a * {@link Map}{@code <K, V>} or {@code Map<K, Produced<V>>}, a key of * {@code Map<K, Producer<V>>} will be returned. */ Optional<Key> implicitMapProducerKeyFrom(Key possibleMapKey) { return firstPresent(rewrapMapKey(possibleMapKey, Produced.class, Producer.class), wrapMapKey(possibleMapKey, Producer.class)); } /** * Keys for map contributions from {@link dagger.Provides} and {@link dagger.producers.Produces} * are in the form {@code Map<K, Framework<V>>}, but keys for {@link Binds} methods are just * {@code Map<K, V>} since the framework type is not known until graph resolution. This * translates from the {@code @Provides}/{@code @Produces} format into the {@code @Binds} * format. If {@link Key#type() possibleMapKey.type()} is not a {@code Map<K, Framework<V>>}, * returns {@code possibleMapKey}. */ Key convertToDelegateKey(Key possibleMapKey) { if (!MapType.isMap(possibleMapKey)) { return possibleMapKey; } MapType mapType = MapType.from(possibleMapKey); TypeMirror wrappedValueType; if (mapType.valuesAreTypeOf(Provider.class)) { wrappedValueType = mapType.unwrappedValueType(Provider.class); } else if (mapType.valuesAreTypeOf(Producer.class)) { wrappedValueType = mapType.unwrappedValueType(Producer.class); } else { return possibleMapKey; } return possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build(); } /** * Converts a {@link Key} of type {@code Map<K, V>} to {@code Map<K, Provider<V>>}. */ private Key wrapMapValue(Key key, Class<?> newWrappingClass) { checkArgument( FrameworkTypes.isFrameworkType(elements.getTypeElement(newWrappingClass.getName()).asType())); return wrapMapKey(key, newWrappingClass).get(); } /** * If {@code key}'s type is {@code Map<K, CurrentWrappingClass<Bar>>}, returns a key with type * {@code Map<K, NewWrappingClass<Bar>>} with the same qualifier. Otherwise returns {@link * Optional#empty()}. * * <p>Returns {@link Optional#empty()} if {@code newWrappingClass} is not in the classpath. * * @throws IllegalArgumentException if {@code newWrappingClass} is the same as {@code * currentWrappingClass} */ Optional<Key> rewrapMapKey(Key possibleMapKey, Class<?> currentWrappingClass, Class<?> newWrappingClass) { checkArgument(!currentWrappingClass.equals(newWrappingClass)); if (MapType.isMap(possibleMapKey)) { MapType mapType = MapType.from(possibleMapKey); if (mapType.valuesAreTypeOf(currentWrappingClass)) { TypeElement wrappingElement = getClassElement(newWrappingClass); if (wrappingElement == null) { // This target might not be compiled with Producers, so wrappingClass might not have an // associated element. return Optional.empty(); } DeclaredType wrappedValueType = types.getDeclaredType(wrappingElement, mapType.unwrappedValueType(currentWrappingClass)); return Optional.of( possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build()); } } return Optional.empty(); } /** * If {@code key}'s type is {@code Map<K, Foo>} and {@code Foo} is not {@code WrappingClass * <Bar>}, returns a key with type {@code Map<K, WrappingClass<Foo>>} with the same qualifier. * Otherwise returns {@link Optional#empty()}. * * <p>Returns {@link Optional#empty()} if {@code WrappingClass} is not in the classpath. */ private Optional<Key> wrapMapKey(Key possibleMapKey, Class<?> wrappingClass) { if (MapType.isMap(possibleMapKey)) { MapType mapType = MapType.from(possibleMapKey); if (!mapType.valuesAreTypeOf(wrappingClass)) { TypeElement wrappingElement = getClassElement(wrappingClass); if (wrappingElement == null) { // This target might not be compiled with Producers, so wrappingClass might not have an // associated element. return Optional.empty(); } DeclaredType wrappedValueType = types.getDeclaredType(wrappingElement, mapType.valueType()); return Optional.of( possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build()); } } return Optional.empty(); } /** * If {@code key}'s type is {@code Set<WrappingClass<Bar>>}, returns a key with type {@code Set * <Bar>} with the same qualifier. Otherwise returns {@link Optional#empty()}. */ Optional<Key> unwrapSetKey(Key key, Class<?> wrappingClass) { if (SetType.isSet(key)) { SetType setType = SetType.from(key); if (setType.elementsAreTypeOf(wrappingClass)) { return Optional .of(key.toBuilder().type(setOf(setType.unwrappedElementType(wrappingClass))).build()); } } return Optional.empty(); } /** * If {@code key}'s type is {@code Optional<T>} for some {@code T}, returns a key with the same * qualifier whose type is {@linkplain DependencyRequest#extractKindAndType(TypeMirror) * extracted} from {@code T}. */ Optional<Key> unwrapOptional(Key key) { if (!OptionalType.isOptional(key)) { return Optional.empty(); } TypeMirror underlyingType = DependencyRequest.extractKindAndType(OptionalType.from(key).valueType()) .type(); return Optional.of(key.toBuilder().type(underlyingType).build()); } /** Returns a key for a {@code @ForReleasableReferences(scope) ReleasableReferenceManager}. */ Key forReleasableReferenceManager(Scope scope) { return forQualifiedType(Optional.of(forReleasableReferencesAnnotationMirror(scope)), getClassElement(ReleasableReferenceManager.class).asType()); } /** * Returns a key for a {@code @ForReleasableReferences(scope) * TypedReleasableReferenceManager<metadataType>} */ Key forTypedReleasableReferenceManager(Scope scope, DeclaredType metadataType) { return builder(typedReleasableReferenceManagerOf(metadataType)) .qualifier(forReleasableReferencesAnnotationMirror(scope)).build(); } /** Returns a key for a {@code Set<ReleasableReferenceManager>}. */ Key forSetOfReleasableReferenceManagers() { return builder(setOf(getClassElement(ReleasableReferenceManager.class).asType())).build(); } /** Returns a key for a {@code Set<TypedReleasableReferenceManager<metadataType>}. */ Key forSetOfTypedReleasableReferenceManagers(DeclaredType metadataType) { return forQualifiedType(Optional.empty(), setOf(typedReleasableReferenceManagerOf(metadataType))); } private AnnotationMirror forReleasableReferencesAnnotationMirror(Scope scope) { return SimpleAnnotationMirror.of(getClassElement(ForReleasableReferences.class), ImmutableMap .of("value", new SimpleTypeAnnotationValue(scope.scopeAnnotationElement().asType()))); } } }