1 /* 2 * Copyright (C) 2015 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.auto.common.MoreElements.isAnnotationPresent; 20 import static com.google.common.base.Preconditions.checkArgument; 21 22 import com.google.auto.common.MoreTypes; 23 import com.google.auto.value.AutoValue; 24 import com.google.auto.value.extension.memoized.Memoized; 25 import dagger.internal.codegen.ContributionType.HasContributionType; 26 import dagger.internal.codegen.langmodel.DaggerTypes; 27 import dagger.model.Key; 28 import dagger.multibindings.Multibinds; 29 import java.util.Map; 30 import java.util.Optional; 31 import java.util.Set; 32 import javax.inject.Inject; 33 import javax.lang.model.element.Element; 34 import javax.lang.model.element.ExecutableElement; 35 import javax.lang.model.element.TypeElement; 36 import javax.lang.model.type.ExecutableType; 37 import javax.lang.model.type.TypeMirror; 38 39 /** 40 * A declaration that a multibinding with a certain key is available to be injected in a component 41 * even if the component has no multibindings for that key. Identified by a map- or set-returning 42 * method annotated with {@link Multibinds @Multibinds}. 43 */ 44 @AutoValue 45 abstract class MultibindingDeclaration extends BindingDeclaration implements HasContributionType { 46 47 /** 48 * The map or set key whose availability is declared. For maps, this will be {@code Map<K, 49 * Provider<V>>}. For sets, this will be {@code Set<T>}. 50 */ 51 @Override key()52 public abstract Key key(); 53 54 /** 55 * {@link ContributionType#SET} if the declared type is a {@link Set}, or 56 * {@link ContributionType#MAP} if it is a {@link Map}. 57 */ 58 @Override contributionType()59 public abstract ContributionType contributionType(); 60 61 @Memoized 62 @Override hashCode()63 public abstract int hashCode(); 64 65 @Override equals(Object obj)66 public abstract boolean equals(Object obj); 67 68 /** 69 * A factory for {@link MultibindingDeclaration}s. 70 */ 71 static final class Factory { 72 private final DaggerTypes types; 73 private final KeyFactory keyFactory; 74 75 @Inject Factory(DaggerTypes types, KeyFactory keyFactory)76 Factory(DaggerTypes types, KeyFactory keyFactory) { 77 this.types = types; 78 this.keyFactory = keyFactory; 79 } 80 81 /** A multibinding declaration for a {@link Multibinds @Multibinds} method. */ forMultibindsMethod( ExecutableElement moduleMethod, TypeElement moduleElement)82 MultibindingDeclaration forMultibindsMethod( 83 ExecutableElement moduleMethod, TypeElement moduleElement) { 84 checkArgument(isAnnotationPresent(moduleMethod, Multibinds.class)); 85 return forDeclaredMethod( 86 moduleMethod, 87 MoreTypes.asExecutable( 88 types.asMemberOf(MoreTypes.asDeclared(moduleElement.asType()), moduleMethod)), 89 moduleElement); 90 } 91 forDeclaredMethod( ExecutableElement method, ExecutableType methodType, TypeElement contributingType)92 private MultibindingDeclaration forDeclaredMethod( 93 ExecutableElement method, 94 ExecutableType methodType, 95 TypeElement contributingType) { 96 TypeMirror returnType = methodType.getReturnType(); 97 checkArgument( 98 SetType.isSet(returnType) || MapType.isMap(returnType), 99 "%s must return a set or map", 100 method); 101 return new AutoValue_MultibindingDeclaration( 102 Optional.<Element>of(method), 103 Optional.of(contributingType), 104 keyFactory.forMultibindsMethod(methodType, method), 105 contributionType(returnType)); 106 } 107 contributionType(TypeMirror returnType)108 private ContributionType contributionType(TypeMirror returnType) { 109 if (MapType.isMap(returnType)) { 110 return ContributionType.MAP; 111 } else if (SetType.isSet(returnType)) { 112 return ContributionType.SET; 113 } else { 114 throw new IllegalArgumentException("Must be Map or Set: " + returnType); 115 } 116 } 117 } 118 } 119