1 /* 2 * Copyright (C) 2014 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen; 18 19 import static com.google.common.collect.Iterables.getOnlyElement; 20 import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS; 21 import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.NO_SCOPING; 22 import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_CONCRETE; 23 import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.EXCEPTION; 24 25 import com.google.auto.common.MoreTypes; 26 import com.google.common.util.concurrent.ListenableFuture; 27 import dagger.internal.codegen.langmodel.DaggerElements; 28 import dagger.internal.codegen.langmodel.DaggerTypes; 29 import dagger.multibindings.ElementsIntoSet; 30 import dagger.producers.ProducerModule; 31 import dagger.producers.Produces; 32 import java.util.Optional; 33 import java.util.Set; 34 import javax.inject.Inject; 35 import javax.lang.model.element.ExecutableElement; 36 import javax.lang.model.type.DeclaredType; 37 import javax.lang.model.type.TypeMirror; 38 39 /** A validator for {@link Produces} methods. */ 40 final class ProducesMethodValidator extends BindingMethodValidator { 41 42 @Inject ProducesMethodValidator( DaggerElements elements, DaggerTypes types, DependencyRequestValidator dependencyRequestValidator)43 ProducesMethodValidator( 44 DaggerElements elements, 45 DaggerTypes types, 46 DependencyRequestValidator dependencyRequestValidator) { 47 super( 48 elements, 49 types, 50 dependencyRequestValidator, 51 Produces.class, 52 ProducerModule.class, 53 MUST_BE_CONCRETE, 54 EXCEPTION, 55 ALLOWS_MULTIBINDINGS, 56 NO_SCOPING); 57 } 58 59 @Override elementsIntoSetNotASetMessage()60 protected String elementsIntoSetNotASetMessage() { 61 return "@Produces methods of type set values must return a Set or ListenableFuture of Set"; 62 } 63 64 @Override badTypeMessage()65 protected String badTypeMessage() { 66 return "@Produces methods can return only a primitive, an array, a type variable, " 67 + "a declared type, or a ListenableFuture of one of those types"; 68 } 69 70 @Override elementValidator(ExecutableElement element)71 protected ElementValidator elementValidator(ExecutableElement element) { 72 return new Validator(element); 73 } 74 75 private class Validator extends MethodValidator { Validator(ExecutableElement element)76 Validator(ExecutableElement element) { 77 super(element); 78 } 79 80 @Override checkAdditionalMethodProperties()81 protected void checkAdditionalMethodProperties() { 82 checkNullable(); 83 } 84 85 /** Adds a warning if a {@link Produces @Produces} method is declared nullable. */ 86 // TODO(beder): Properly handle nullable with producer methods. checkNullable()87 private void checkNullable() { 88 if (ConfigurationAnnotations.getNullableType(element).isPresent()) { 89 report.addWarning("@Nullable on @Produces methods does not do anything"); 90 } 91 } 92 93 /** 94 * {@inheritDoc} 95 * 96 * <p>Allows {@code keyType} to be a {@link ListenableFuture} of an otherwise-valid key type. 97 */ 98 @Override checkKeyType(TypeMirror keyType)99 protected void checkKeyType(TypeMirror keyType) { 100 Optional<TypeMirror> typeToCheck = unwrapListenableFuture(keyType); 101 if (typeToCheck.isPresent()) { 102 super.checkKeyType(typeToCheck.get()); 103 } 104 } 105 106 /** 107 * {@inheritDoc} 108 * 109 * <p>Allows an {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} method to return 110 * a {@link ListenableFuture} of a {@link Set} as well. 111 */ 112 @Override checkSetValuesType()113 protected void checkSetValuesType() { 114 Optional<TypeMirror> typeToCheck = unwrapListenableFuture(element.getReturnType()); 115 if (typeToCheck.isPresent()) { 116 checkSetValuesType(typeToCheck.get()); 117 } 118 } 119 unwrapListenableFuture(TypeMirror type)120 private Optional<TypeMirror> unwrapListenableFuture(TypeMirror type) { 121 if (MoreTypes.isType(type) && MoreTypes.isTypeOf(ListenableFuture.class, type)) { 122 DeclaredType declaredType = MoreTypes.asDeclared(type); 123 if (declaredType.getTypeArguments().isEmpty()) { 124 report.addError("@Produces methods cannot return a raw ListenableFuture"); 125 return Optional.empty(); 126 } else { 127 return Optional.of((TypeMirror) getOnlyElement(declaredType.getTypeArguments())); 128 } 129 } 130 return Optional.of(type); 131 } 132 } 133 } 134