• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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