| /* |
| * 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.base; |
| |
| import static com.google.auto.common.MoreTypes.asDeclared; |
| 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 dagger.internal.codegen.javapoet.TypeNames.lazyOf; |
| import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf; |
| import static dagger.internal.codegen.javapoet.TypeNames.producedOf; |
| import static dagger.internal.codegen.javapoet.TypeNames.producerOf; |
| import static dagger.internal.codegen.javapoet.TypeNames.providerOf; |
| import static dagger.internal.codegen.langmodel.DaggerTypes.checkTypePresent; |
| import static dagger.model.RequestKind.LAZY; |
| import static dagger.model.RequestKind.PRODUCED; |
| import static dagger.model.RequestKind.PRODUCER; |
| import static dagger.model.RequestKind.PROVIDER; |
| import static dagger.model.RequestKind.PROVIDER_OF_LAZY; |
| import static javax.lang.model.type.TypeKind.DECLARED; |
| |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.util.concurrent.ListenableFuture; |
| import com.squareup.javapoet.TypeName; |
| import dagger.Lazy; |
| import dagger.internal.codegen.langmodel.DaggerTypes; |
| import dagger.model.RequestKind; |
| import dagger.producers.Produced; |
| import dagger.producers.Producer; |
| import javax.inject.Provider; |
| import javax.lang.model.type.TypeMirror; |
| |
| /** Utility methods for {@link RequestKind}s. */ |
| public final class RequestKinds { |
| |
| /** Returns the type of a request of this kind for a key with a given type. */ |
| public static TypeMirror requestType( |
| RequestKind requestKind, TypeMirror type, DaggerTypes types) { |
| switch (requestKind) { |
| case INSTANCE: |
| return type; |
| |
| case PROVIDER_OF_LAZY: |
| return types.wrapType(requestType(LAZY, type, types), Provider.class); |
| |
| case FUTURE: |
| return types.wrapType(type, ListenableFuture.class); |
| |
| default: |
| return types.wrapType(type, frameworkClass(requestKind)); |
| } |
| } |
| |
| /** Returns the type of a request of this kind for a key with a given type. */ |
| public static TypeName requestTypeName(RequestKind requestKind, TypeName keyType) { |
| switch (requestKind) { |
| case INSTANCE: |
| return keyType; |
| |
| case PROVIDER: |
| return providerOf(keyType); |
| |
| case LAZY: |
| return lazyOf(keyType); |
| |
| case PROVIDER_OF_LAZY: |
| return providerOf(lazyOf(keyType)); |
| |
| case PRODUCER: |
| return producerOf(keyType); |
| |
| case PRODUCED: |
| return producedOf(keyType); |
| |
| case FUTURE: |
| return listenableFutureOf(keyType); |
| |
| default: |
| throw new AssertionError(requestKind); |
| } |
| } |
| |
| private static final ImmutableMap<RequestKind, Class<?>> FRAMEWORK_CLASSES = |
| ImmutableMap.of( |
| PROVIDER, Provider.class, |
| LAZY, Lazy.class, |
| PRODUCER, Producer.class, |
| PRODUCED, Produced.class); |
| |
| /** Returns the {@link RequestKind} that matches the wrapping types (if any) of {@code type}. */ |
| public static RequestKind getRequestKind(TypeMirror type) { |
| checkTypePresent(type); |
| if (!isType(type) // TODO(b/147320669): isType check can be removed once this bug is fixed. |
| || !type.getKind().equals(DECLARED) |
| || asDeclared(type).getTypeArguments().isEmpty()) { |
| // If the type is not a declared type (i.e. class or interface) with type arguments, then we |
| // know it can't be a parameterized type of one of the framework classes, so return INSTANCE. |
| return RequestKind.INSTANCE; |
| } |
| for (RequestKind kind : FRAMEWORK_CLASSES.keySet()) { |
| if (isTypeOf(frameworkClass(kind), type)) { |
| if (kind.equals(PROVIDER) && getRequestKind(DaggerTypes.unwrapType(type)).equals(LAZY)) { |
| return PROVIDER_OF_LAZY; |
| } |
| return kind; |
| } |
| } |
| return RequestKind.INSTANCE; |
| } |
| |
| /** |
| * Unwraps the framework class(es) of {@code requestKind} from {@code type}. If {@code |
| * requestKind} is {@link RequestKind#INSTANCE}, this acts as an identity function. |
| * |
| * @throws TypeNotPresentException if {@code type} is an {@link javax.lang.model.type.ErrorType}, |
| * which may mean that the type will be generated in a later round of processing |
| * @throws IllegalArgumentException if {@code type} is not wrapped with {@code requestKind}'s |
| * framework class(es). |
| */ |
| public static TypeMirror extractKeyType(TypeMirror type) { |
| return extractKeyType(getRequestKind(type), type); |
| } |
| |
| private static TypeMirror extractKeyType(RequestKind requestKind, TypeMirror type) { |
| switch (requestKind) { |
| case INSTANCE: |
| return type; |
| case PROVIDER_OF_LAZY: |
| return extractKeyType(LAZY, extractKeyType(PROVIDER, type)); |
| default: |
| checkArgument(isType(type)); |
| return DaggerTypes.unwrapType(type); |
| } |
| } |
| |
| /** |
| * A dagger- or {@code javax.inject}-defined class for {@code requestKind} that that can wrap |
| * another type but share the same {@link dagger.model.Key}. |
| * |
| * <p>For example, {@code Provider<String>} and {@code Lazy<String>} can both be requested if a |
| * key exists for {@code String}; they all share the same key. |
| * |
| * <p>This concept is not well defined and should probably be removed and inlined into the cases |
| * that need it. For example, {@link RequestKind#PROVIDER_OF_LAZY} has <em>2</em> wrapping |
| * classes, and {@link RequestKind#FUTURE} is wrapped with a {@link ListenableFuture}, but for |
| * historical/implementation reasons has not had an associated framework class. |
| */ |
| public static Class<?> frameworkClass(RequestKind requestKind) { |
| Class<?> result = FRAMEWORK_CLASSES.get(requestKind); |
| checkArgument(result != null, "no framework class for %s", requestKind); |
| return result; |
| } |
| |
| /** |
| * Returns {@code true} if requests for {@code requestKind} can be satisfied by a production |
| * binding. |
| */ |
| public static boolean canBeSatisfiedByProductionBinding(RequestKind requestKind) { |
| switch (requestKind) { |
| case INSTANCE: |
| case PROVIDER: |
| case LAZY: |
| case PROVIDER_OF_LAZY: |
| case MEMBERS_INJECTION: |
| return false; |
| case PRODUCER: |
| case PRODUCED: |
| case FUTURE: |
| return true; |
| } |
| throw new AssertionError(); |
| } |
| |
| private RequestKinds() {} |
| } |