| Christian Edward Gruber | c8e1107 | 2014-03-17 08:38:59 -0700 | [diff] [blame] | 1 | /* |
| ronshapiro | 5dde42d | 2016-06-17 09:03:35 -0700 | [diff] [blame] | 2 | * Copyright (C) 2014 The Dagger Authors. |
| Christian Edward Gruber | c8e1107 | 2014-03-17 08:38:59 -0700 | [diff] [blame] | 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 | */ |
| dpb | a69ead5 | 2016-07-11 10:05:20 -0700 | [diff] [blame] | 16 | |
| Christian Edward Gruber | c8e1107 | 2014-03-17 08:38:59 -0700 | [diff] [blame] | 17 | package dagger.internal.codegen; |
| 18 | |
| gak | e55f074 | 2016-07-12 15:36:42 -0700 | [diff] [blame] | 19 | import static com.google.auto.common.MoreElements.isAnnotationPresent; |
| 20 | import static com.google.auto.common.MoreTypes.asExecutable; |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 21 | import static com.google.auto.common.MoreTypes.isType; |
| 22 | import static com.google.auto.common.MoreTypes.isTypeOf; |
| gak | e55f074 | 2016-07-12 15:36:42 -0700 | [diff] [blame] | 23 | import static com.google.common.base.Preconditions.checkArgument; |
| 24 | import static com.google.common.base.Preconditions.checkNotNull; |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 25 | import static com.google.common.collect.Iterables.getOnlyElement; |
| gak | e55f074 | 2016-07-12 15:36:42 -0700 | [diff] [blame] | 26 | import static dagger.internal.codegen.InjectionAnnotations.getQualifier; |
| 27 | import static dagger.internal.codegen.MapKeys.getMapKey; |
| 28 | import static dagger.internal.codegen.MapKeys.getUnwrappedMapKeyType; |
| 29 | import static dagger.internal.codegen.MoreAnnotationMirrors.unwrapOptionalEquivalence; |
| 30 | import static dagger.internal.codegen.MoreAnnotationMirrors.wrapOptionalInEquivalence; |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 31 | import static dagger.internal.codegen.Optionals.firstPresent; |
| 32 | import static dagger.internal.codegen.Util.toImmutableSet; |
| gak | e55f074 | 2016-07-12 15:36:42 -0700 | [diff] [blame] | 33 | import static javax.lang.model.element.ElementKind.METHOD; |
| 34 | |
| cgruber | 4fd5a46 | 2015-01-08 17:26:12 -0800 | [diff] [blame] | 35 | import com.google.auto.common.AnnotationMirrors; |
| sameb | 2ea676a | 2015-01-15 10:20:24 -0800 | [diff] [blame] | 36 | import com.google.auto.common.MoreElements; |
| Christian Edward Gruber | 02a3229 | 2014-07-18 18:19:02 -0700 | [diff] [blame] | 37 | import com.google.auto.common.MoreTypes; |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 38 | import com.google.auto.value.AutoValue; |
| David P. Baker | 1e718b8 | 2016-11-03 15:51:22 -0400 | [diff] [blame] | 39 | import com.google.auto.value.extension.memoized.Memoized; |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 40 | import com.google.common.base.Equivalence; |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 41 | import com.google.common.base.Joiner; |
| ronshapiro | d804a30 | 2016-11-08 10:21:56 -0800 | [diff] [blame] | 42 | import com.google.common.collect.ImmutableMap; |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 43 | import com.google.common.collect.ImmutableSet; |
| dpb | a262042 | 2015-08-15 10:06:54 -0700 | [diff] [blame] | 44 | import com.google.common.collect.ImmutableSetMultimap; |
| dpb | a262042 | 2015-08-15 10:06:54 -0700 | [diff] [blame] | 45 | import com.google.common.collect.Multimaps; |
| beder | 8fdbb9e | 2014-12-08 10:14:17 -0800 | [diff] [blame] | 46 | import com.google.common.util.concurrent.ListenableFuture; |
| gak | 24b0228 | 2016-04-11 15:51:14 -0700 | [diff] [blame] | 47 | import dagger.Binds; |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 48 | import dagger.BindsOptionalOf; |
| dpb | 228475f | 2016-05-24 14:46:53 -0700 | [diff] [blame] | 49 | import dagger.multibindings.Multibinds; |
| beder | 6583420 | 2015-09-30 08:49:44 -0700 | [diff] [blame] | 50 | import dagger.producers.Produced; |
| beder | ca9c82b | 2015-01-23 18:30:22 -0800 | [diff] [blame] | 51 | import dagger.producers.Producer; |
| gak | e1b68a2 | 2016-04-07 11:54:06 -0700 | [diff] [blame] | 52 | import dagger.producers.Production; |
| beder | 893f770 | 2016-02-17 05:22:50 -0800 | [diff] [blame] | 53 | import dagger.producers.internal.ProductionImplementation; |
| gak | b8dea08 | 2016-08-11 12:17:56 -0700 | [diff] [blame] | 54 | import dagger.producers.monitoring.ProductionComponentMonitor; |
| ronshapiro | d804a30 | 2016-11-08 10:21:56 -0800 | [diff] [blame] | 55 | import dagger.releasablereferences.ForReleasableReferences; |
| 56 | import dagger.releasablereferences.ReleasableReferenceManager; |
| 57 | import dagger.releasablereferences.TypedReleasableReferenceManager; |
| houcy | 3fc075f | 2014-07-29 16:51:27 -0700 | [diff] [blame] | 58 | import java.util.Map; |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 59 | import java.util.Optional; |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 60 | import java.util.Set; |
| beder | 893f770 | 2016-02-17 05:22:50 -0800 | [diff] [blame] | 61 | import java.util.concurrent.Executor; |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 62 | import java.util.stream.Stream; |
| houcy | 3fc075f | 2014-07-29 16:51:27 -0700 | [diff] [blame] | 63 | import javax.inject.Provider; |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 64 | import javax.inject.Qualifier; |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 65 | import javax.lang.model.element.AnnotationMirror; |
| sameb | 2ea676a | 2015-01-15 10:20:24 -0800 | [diff] [blame] | 66 | import javax.lang.model.element.ElementKind; |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 67 | import javax.lang.model.element.ExecutableElement; |
| sameb | 2ea676a | 2015-01-15 10:20:24 -0800 | [diff] [blame] | 68 | import javax.lang.model.element.Modifier; |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 69 | import javax.lang.model.element.TypeElement; |
| 70 | import javax.lang.model.type.DeclaredType; |
| sameb | 0d49be8 | 2015-01-22 17:29:48 -0800 | [diff] [blame] | 71 | import javax.lang.model.type.ExecutableType; |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 72 | import javax.lang.model.type.PrimitiveType; |
| 73 | import javax.lang.model.type.TypeKind; |
| 74 | import javax.lang.model.type.TypeMirror; |
| 75 | import javax.lang.model.util.Elements; |
| gak | 3978af3 | 2014-08-19 14:38:40 -0700 | [diff] [blame] | 76 | import javax.lang.model.util.SimpleTypeVisitor6; |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 77 | import javax.lang.model.util.Types; |
| gak | 3978af3 | 2014-08-19 14:38:40 -0700 | [diff] [blame] | 78 | |
| Christian Edward Gruber | c8e1107 | 2014-03-17 08:38:59 -0700 | [diff] [blame] | 79 | /** |
| 80 | * Represents a unique combination of {@linkplain TypeMirror type} and |
| 81 | * {@linkplain Qualifier qualifier} to which binding can occur. |
| 82 | * |
| 83 | * @author Gregory Kick |
| 84 | */ |
| 85 | @AutoValue |
| 86 | abstract class Key { |
| beder | 893f770 | 2016-02-17 05:22:50 -0800 | [diff] [blame] | 87 | |
| dpb | a262042 | 2015-08-15 10:06:54 -0700 | [diff] [blame] | 88 | /** An object that is associated with a {@link Key}. */ |
| 89 | interface HasKey { |
| 90 | /** The key associated with this object. */ |
| 91 | Key key(); |
| 92 | } |
| 93 | |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 94 | /** |
| 95 | * A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix |
| 96 | * for the type of this key. |
| Christian Edward Gruber | 122f212 | 2014-06-16 16:06:00 -0700 | [diff] [blame] | 97 | * |
| 98 | * Despite documentation in {@link AnnotationMirror}, equals and hashCode aren't implemented |
| cgruber | 815ccd1 | 2014-09-02 13:06:30 -0700 | [diff] [blame] | 99 | * to represent logical equality, so {@link AnnotationMirrors#equivalence()} |
| Christian Edward Gruber | 122f212 | 2014-06-16 16:06:00 -0700 | [diff] [blame] | 100 | * provides this facility. |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 101 | */ |
| Christian Edward Gruber | 122f212 | 2014-06-16 16:06:00 -0700 | [diff] [blame] | 102 | abstract Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedQualifier(); |
| Christian Edward Gruber | c8e1107 | 2014-03-17 08:38:59 -0700 | [diff] [blame] | 103 | |
| 104 | /** |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 105 | * The type represented by this key. |
| 106 | * |
| Christian Edward Gruber | c8e1107 | 2014-03-17 08:38:59 -0700 | [diff] [blame] | 107 | * As documented in {@link TypeMirror}, equals and hashCode aren't implemented to represent |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 108 | * logical equality, so {@link MoreTypes#equivalence()} wraps this type. |
| Christian Edward Gruber | c8e1107 | 2014-03-17 08:38:59 -0700 | [diff] [blame] | 109 | */ |
| 110 | abstract Equivalence.Wrapper<TypeMirror> wrappedType(); |
| beder | 893f770 | 2016-02-17 05:22:50 -0800 | [diff] [blame] | 111 | |
| dpb | db24072 | 2016-01-25 14:06:41 -0800 | [diff] [blame] | 112 | /** |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 113 | * Distinguishes keys for multibinding contributions that share a {@link #type()} and {@link |
| 114 | * #qualifier()}. |
| dpb | 7edcd4d | 2016-06-21 07:52:39 -0700 | [diff] [blame] | 115 | * |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 116 | * <p>Each multibound map and set has a {@linkplain |
| dpb | 5420346 | 2016-10-19 13:46:43 -0700 | [diff] [blame] | 117 | * ProvisionBinding.Factory#syntheticMultibinding(Key, Iterable) synthetic multibinding} that |
| 118 | * depends on the specific contributions to that map or set using keys that identify those |
| 119 | * multibinding contributions. |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 120 | * |
| 121 | * <p>Absent except for multibinding contributions. |
| dpb | db24072 | 2016-01-25 14:06:41 -0800 | [diff] [blame] | 122 | */ |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 123 | abstract Optional<MultibindingContributionIdentifier> multibindingContributionIdentifier(); |
| ronshapiro | d804a30 | 2016-11-08 10:21:56 -0800 | [diff] [blame] | 124 | |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 125 | abstract Builder toBuilder(); |
| 126 | |
| David P. Baker | 1e718b8 | 2016-11-03 15:51:22 -0400 | [diff] [blame] | 127 | @Memoized |
| 128 | @Override |
| 129 | public abstract int hashCode(); |
| 130 | |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 131 | static Builder builder(TypeMirror type) { |
| 132 | return new AutoValue_Key.Builder().type(type); |
| 133 | } |
| 134 | |
| 135 | @AutoValue.Builder |
| 136 | abstract static class Builder { |
| 137 | abstract Builder wrappedType(Equivalence.Wrapper<TypeMirror> wrappedType); |
| 138 | |
| 139 | Builder type(TypeMirror type) { |
| 140 | return wrappedType(MoreTypes.equivalence().wrap(checkNotNull(type))); |
| 141 | } |
| 142 | |
| 143 | abstract Builder wrappedQualifier( |
| 144 | Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedQualifier); |
| 145 | |
| 146 | abstract Builder wrappedQualifier(Equivalence.Wrapper<AnnotationMirror> wrappedQualifier); |
| 147 | |
| 148 | Builder qualifier(AnnotationMirror qualifier) { |
| 149 | return wrappedQualifier(AnnotationMirrors.equivalence().wrap(checkNotNull(qualifier))); |
| 150 | } |
| 151 | |
| 152 | Builder qualifier(Optional<AnnotationMirror> qualifier) { |
| 153 | return wrappedQualifier(wrapOptionalInEquivalence(checkNotNull(qualifier))); |
| 154 | } |
| 155 | |
| 156 | Builder qualifier(TypeElement annotationType) { |
| 157 | return qualifier(SimpleAnnotationMirror.of(annotationType)); |
| 158 | } |
| 159 | |
| 160 | abstract Builder multibindingContributionIdentifier( |
| 161 | Optional<MultibindingContributionIdentifier> identifier); |
| 162 | |
| 163 | abstract Builder multibindingContributionIdentifier( |
| 164 | MultibindingContributionIdentifier identifier); |
| 165 | |
| 166 | abstract Key build(); |
| 167 | } |
| ronshapiro | d804a30 | 2016-11-08 10:21:56 -0800 | [diff] [blame] | 168 | |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 169 | /** |
| 170 | * An object that identifies a multibinding contribution method and the module class that |
| 171 | * contributes it to the graph. |
| 172 | * |
| 173 | * @see Key#multibindingContributionIdentifier() |
| 174 | */ |
| 175 | static final class MultibindingContributionIdentifier { |
| 176 | private final String identifierString; |
| dpb | 4955ef9 | 2016-04-29 12:31:14 -0700 | [diff] [blame] | 177 | |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 178 | MultibindingContributionIdentifier( |
| 179 | ExecutableElement bindingMethod, TypeElement contributingModule) { |
| 180 | this.identifierString = |
| 181 | String.format( |
| 182 | "%s#%s", contributingModule.getQualifiedName(), bindingMethod.getSimpleName()); |
| dpb | 7edcd4d | 2016-06-21 07:52:39 -0700 | [diff] [blame] | 183 | } |
| dpb | 4955ef9 | 2016-04-29 12:31:14 -0700 | [diff] [blame] | 184 | |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 185 | /** |
| 186 | * {@inheritDoc} |
| 187 | * |
| 188 | * <p>The returned string is human-readable and distinguishes the keys in the same way as the |
| 189 | * whole object. |
| 190 | */ |
| 191 | @Override |
| 192 | public String toString() { |
| 193 | return identifierString; |
| 194 | } |
| 195 | |
| 196 | @Override |
| 197 | public boolean equals(Object obj) { |
| 198 | return obj instanceof MultibindingContributionIdentifier |
| 199 | && ((MultibindingContributionIdentifier) obj) |
| 200 | .identifierString.equals(this.identifierString); |
| 201 | } |
| 202 | |
| 203 | @Override |
| 204 | public int hashCode() { |
| 205 | return identifierString.hashCode(); |
| dpb | 4955ef9 | 2016-04-29 12:31:14 -0700 | [diff] [blame] | 206 | } |
| 207 | } |
| dpb | 7edcd4d | 2016-06-21 07:52:39 -0700 | [diff] [blame] | 208 | |
| dpb | db24072 | 2016-01-25 14:06:41 -0800 | [diff] [blame] | 209 | /** |
| 210 | * A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix |
| 211 | * for the type of this key. |
| 212 | */ |
| Christian Edward Gruber | 122f212 | 2014-06-16 16:06:00 -0700 | [diff] [blame] | 213 | Optional<AnnotationMirror> qualifier() { |
| cgruber | 4136f4d | 2014-10-28 10:59:26 -0700 | [diff] [blame] | 214 | return unwrapOptionalEquivalence(wrappedQualifier()); |
| Christian Edward Gruber | 122f212 | 2014-06-16 16:06:00 -0700 | [diff] [blame] | 215 | } |
| 216 | |
| dpb | db24072 | 2016-01-25 14:06:41 -0800 | [diff] [blame] | 217 | /** |
| 218 | * The type represented by this key. |
| 219 | */ |
| Christian Edward Gruber | c8e1107 | 2014-03-17 08:38:59 -0700 | [diff] [blame] | 220 | TypeMirror type() { |
| 221 | return wrappedType().get(); |
| 222 | } |
| 223 | |
| dpb | db24072 | 2016-01-25 14:06:41 -0800 | [diff] [blame] | 224 | /** |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 225 | * A key whose {@link #qualifier()} and {@link #type()} are equivalent to this one's, but without |
| 226 | * a {@link #multibindingContributionIdentifier()}. |
| dpb | db24072 | 2016-01-25 14:06:41 -0800 | [diff] [blame] | 227 | */ |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 228 | Key withoutMultibindingContributionIdentifier() { |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 229 | return toBuilder().multibindingContributionIdentifier(Optional.empty()).build(); |
| sameb | 2ea676a | 2015-01-15 10:20:24 -0800 | [diff] [blame] | 230 | } |
| 231 | |
| gak | 3978af3 | 2014-08-19 14:38:40 -0700 | [diff] [blame] | 232 | boolean isValidMembersInjectionKey() { |
| ronshapiro | 13eee29 | 2017-05-04 11:26:54 -0700 | [diff] [blame] | 233 | return !qualifier().isPresent() && type().getKind().equals(TypeKind.DECLARED); |
| sameb | 2ea676a | 2015-01-15 10:20:24 -0800 | [diff] [blame] | 234 | } |
| 235 | |
| 236 | /** |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 237 | * Returns {@code true} if this is valid as an implicit key (that is, if it's valid for a |
| 238 | * just-in-time binding by discovering an {@code @Inject} constructor). |
| sameb | 2ea676a | 2015-01-15 10:20:24 -0800 | [diff] [blame] | 239 | */ |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 240 | boolean isValidImplicitProvisionKey(Types types) { |
| 241 | return isValidImplicitProvisionKey(qualifier(), type(), types); |
| 242 | } |
| 243 | |
| 244 | /** |
| 245 | * Returns {@code true} if a key with {@code qualifier} and {@code type} is valid as an implicit |
| 246 | * key (that is, if it's valid for a just-in-time binding by discovering an {@code @Inject} |
| 247 | * constructor). |
| 248 | */ |
| 249 | static boolean isValidImplicitProvisionKey( |
| 250 | Optional<? extends AnnotationMirror> qualifier, TypeMirror type, final Types types) { |
| sameb | 2ea676a | 2015-01-15 10:20:24 -0800 | [diff] [blame] | 251 | // Qualifiers disqualify implicit provisioning. |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 252 | if (qualifier.isPresent()) { |
| sameb | 2ea676a | 2015-01-15 10:20:24 -0800 | [diff] [blame] | 253 | return false; |
| 254 | } |
| 255 | |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 256 | return type.accept( |
| 257 | new SimpleTypeVisitor6<Boolean, Void>(false) { |
| 258 | @Override |
| 259 | public Boolean visitDeclared(DeclaredType type, Void ignored) { |
| 260 | // Non-classes or abstract classes aren't allowed. |
| 261 | TypeElement element = MoreElements.asType(type.asElement()); |
| 262 | if (!element.getKind().equals(ElementKind.CLASS) |
| 263 | || element.getModifiers().contains(Modifier.ABSTRACT)) { |
| 264 | return false; |
| 265 | } |
| sameb | 2ea676a | 2015-01-15 10:20:24 -0800 | [diff] [blame] | 266 | |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 267 | // If the key has type arguments, validate that each type argument is declared. |
| 268 | // Otherwise the type argument may be a wildcard (or other type), and we can't |
| 269 | // resolve that to actual types. |
| 270 | for (TypeMirror arg : type.getTypeArguments()) { |
| 271 | if (arg.getKind() != TypeKind.DECLARED) { |
| 272 | return false; |
| 273 | } |
| 274 | } |
| sameb | 2ea676a | 2015-01-15 10:20:24 -0800 | [diff] [blame] | 275 | |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 276 | // Also validate that the key is not the erasure of a generic type. |
| 277 | // If it is, that means the user referred to Foo<T> as just 'Foo', |
| 278 | // which we don't allow. (This is a judgement call -- we *could* |
| 279 | // allow it and instantiate the type bounds... but we don't.) |
| 280 | return MoreTypes.asDeclared(element.asType()).getTypeArguments().isEmpty() |
| 281 | || !types.isSameType(types.erasure(element.asType()), type); |
| gak | 3978af3 | 2014-08-19 14:38:40 -0700 | [diff] [blame] | 282 | } |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 283 | }, |
| 284 | null); |
| gak | 3978af3 | 2014-08-19 14:38:40 -0700 | [diff] [blame] | 285 | } |
| 286 | |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 287 | /** |
| 288 | * {@inheritDoc} |
| 289 | * |
| 290 | * <p>The returned string is equal to another key's if and only if this key is {@link |
| dpb | 5420346 | 2016-10-19 13:46:43 -0700 | [diff] [blame] | 291 | * #equals(Object)} to it. |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 292 | */ |
| Christian Edward Gruber | c8e1107 | 2014-03-17 08:38:59 -0700 | [diff] [blame] | 293 | @Override |
| 294 | public String toString() { |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 295 | return Joiner.on(' ') |
| 296 | .skipNulls() |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 297 | .join(qualifier().orElse(null), type(), multibindingContributionIdentifier().orElse(null)); |
| Christian Edward Gruber | c8e1107 | 2014-03-17 08:38:59 -0700 | [diff] [blame] | 298 | } |
| 299 | |
| dpb | f8c5c7f | 2015-11-19 08:16:43 -0800 | [diff] [blame] | 300 | /** |
| dpb | a262042 | 2015-08-15 10:06:54 -0700 | [diff] [blame] | 301 | * Indexes {@code haveKeys} by {@link HasKey#key()}. |
| 302 | */ |
| 303 | static <T extends HasKey> ImmutableSetMultimap<Key, T> indexByKey(Iterable<T> haveKeys) { |
| gak | 6624110 | 2016-09-13 12:23:00 -0700 | [diff] [blame] | 304 | return ImmutableSetMultimap.copyOf(Multimaps.index(haveKeys, HasKey::key)); |
| dpb | a262042 | 2015-08-15 10:06:54 -0700 | [diff] [blame] | 305 | } |
| 306 | |
| Christian Edward Gruber | 9daba78 | 2014-05-04 18:06:26 -0700 | [diff] [blame] | 307 | static final class Factory { |
| 308 | private final Types types; |
| 309 | private final Elements elements; |
| ronshapiro | d804a30 | 2016-11-08 10:21:56 -0800 | [diff] [blame] | 310 | |
| dpb | 90263a5 | 2016-07-13 07:52:41 -0700 | [diff] [blame] | 311 | Factory(Types types, Elements elements) { |
| Christian Edward Gruber | 9daba78 | 2014-05-04 18:06:26 -0700 | [diff] [blame] | 312 | this.types = checkNotNull(types); |
| 313 | this.elements = checkNotNull(elements); |
| 314 | } |
| 315 | |
| dpb | 800c746 | 2016-09-08 09:11:47 -0700 | [diff] [blame] | 316 | private TypeElement getClassElement(Class<?> cls) { |
| 317 | return elements.getTypeElement(cls.getCanonicalName()); |
| 318 | } |
| 319 | |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 320 | private TypeMirror boxPrimitives(TypeMirror type) { |
| 321 | return type.getKind().isPrimitive() ? types.boxedClass((PrimitiveType) type).asType() : type; |
| 322 | } |
| 323 | |
| dpb | 7edcd4d | 2016-06-21 07:52:39 -0700 | [diff] [blame] | 324 | private DeclaredType setOf(TypeMirror elementType) { |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 325 | return types.getDeclaredType(getClassElement(Set.class), boxPrimitives(elementType)); |
| Christian Edward Gruber | 9daba78 | 2014-05-04 18:06:26 -0700 | [diff] [blame] | 326 | } |
| 327 | |
| dpb | 7edcd4d | 2016-06-21 07:52:39 -0700 | [diff] [blame] | 328 | private DeclaredType mapOf(TypeMirror keyType, TypeMirror valueType) { |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 329 | return types.getDeclaredType( |
| 330 | getClassElement(Map.class), boxPrimitives(keyType), boxPrimitives(valueType)); |
| 331 | } |
| 332 | |
| 333 | /** Returns {@code Map<KeyType, FrameworkType<ValueType>>}. */ |
| 334 | private TypeMirror mapOfFrameworkType( |
| 335 | TypeMirror keyType, TypeElement frameworkType, TypeMirror valueType) { |
| 336 | return mapOf(keyType, types.getDeclaredType(frameworkType, boxPrimitives(valueType))); |
| beder | ca9c82b | 2015-01-23 18:30:22 -0800 | [diff] [blame] | 337 | } |
| 338 | |
| ronshapiro | d804a30 | 2016-11-08 10:21:56 -0800 | [diff] [blame] | 339 | private DeclaredType typedReleasableReferenceManagerOf(DeclaredType metadataType) { |
| 340 | return types.getDeclaredType( |
| 341 | getClassElement(TypedReleasableReferenceManager.class), metadataType); |
| 342 | } |
| 343 | |
| Christian Edward Gruber | 14aafa6 | 2014-07-18 18:06:25 -0700 | [diff] [blame] | 344 | Key forComponentMethod(ExecutableElement componentMethod) { |
| Christian Edward Gruber | 14aafa6 | 2014-07-18 18:06:25 -0700 | [diff] [blame] | 345 | checkArgument(componentMethod.getKind().equals(METHOD)); |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 346 | return forMethod(componentMethod, componentMethod.getReturnType()); |
| Christian Edward Gruber | 14aafa6 | 2014-07-18 18:06:25 -0700 | [diff] [blame] | 347 | } |
| 348 | |
| beder | ca9c82b | 2015-01-23 18:30:22 -0800 | [diff] [blame] | 349 | Key forProductionComponentMethod(ExecutableElement componentMethod) { |
| beder | ca9c82b | 2015-01-23 18:30:22 -0800 | [diff] [blame] | 350 | checkArgument(componentMethod.getKind().equals(METHOD)); |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 351 | TypeMirror returnType = componentMethod.getReturnType(); |
| 352 | TypeMirror keyType = |
| 353 | isTypeOf(ListenableFuture.class, returnType) |
| 354 | ? getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments()) |
| 355 | : returnType; |
| dpb | 0748982 | 2015-07-06 10:34:40 -0700 | [diff] [blame] | 356 | return forMethod(componentMethod, keyType); |
| beder | ca9c82b | 2015-01-23 18:30:22 -0800 | [diff] [blame] | 357 | } |
| 358 | |
| dpb | 99d6408 | 2015-11-12 11:27:12 -0800 | [diff] [blame] | 359 | Key forSubcomponentBuilderMethod( |
| 360 | ExecutableElement subcomponentBuilderMethod, DeclaredType declaredContainer) { |
| dpb | 99d6408 | 2015-11-12 11:27:12 -0800 | [diff] [blame] | 361 | checkArgument(subcomponentBuilderMethod.getKind().equals(METHOD)); |
| 362 | ExecutableType resolvedMethod = |
| 363 | asExecutable(types.asMemberOf(declaredContainer, subcomponentBuilderMethod)); |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 364 | return builder(resolvedMethod.getReturnType()).build(); |
| dpb | 99d6408 | 2015-11-12 11:27:12 -0800 | [diff] [blame] | 365 | } |
| 366 | |
| ronshapiro | fbb6840 | 2016-08-31 06:44:45 -0700 | [diff] [blame] | 367 | Key forSubcomponentBuilder(TypeMirror builderType) { |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 368 | return builder(builderType).build(); |
| ronshapiro | fbb6840 | 2016-08-31 06:44:45 -0700 | [diff] [blame] | 369 | } |
| 370 | |
| dpb | 4955ef9 | 2016-04-29 12:31:14 -0700 | [diff] [blame] | 371 | Key forProvidesMethod(ExecutableElement method, TypeElement contributingModule) { |
| dpb | 800c746 | 2016-09-08 09:11:47 -0700 | [diff] [blame] | 372 | return forBindingMethod( |
| 373 | method, contributingModule, Optional.of(getClassElement(Provider.class))); |
| Christian Edward Gruber | 9daba78 | 2014-05-04 18:06:26 -0700 | [diff] [blame] | 374 | } |
| 375 | |
| dpb | 4955ef9 | 2016-04-29 12:31:14 -0700 | [diff] [blame] | 376 | Key forProducesMethod(ExecutableElement method, TypeElement contributingModule) { |
| dpb | 800c746 | 2016-09-08 09:11:47 -0700 | [diff] [blame] | 377 | return forBindingMethod( |
| 378 | method, contributingModule, Optional.of(getClassElement(Producer.class))); |
| ronshapiro | 665a4a5 | 2016-04-12 21:10:00 -0700 | [diff] [blame] | 379 | } |
| 380 | |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 381 | /** Returns the key bound by a {@link Binds} method. */ |
| 382 | Key forBindsMethod(ExecutableElement method, TypeElement contributingModule) { |
| 383 | checkArgument(isAnnotationPresent(method, Binds.class)); |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 384 | return forBindingMethod(method, contributingModule, Optional.empty()); |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 385 | } |
| 386 | |
| 387 | /** Returns the base key bound by a {@link BindsOptionalOf} method. */ |
| 388 | Key forBindsOptionalOfMethod(ExecutableElement method, TypeElement contributingModule) { |
| 389 | checkArgument(isAnnotationPresent(method, BindsOptionalOf.class)); |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 390 | return forBindingMethod(method, contributingModule, Optional.empty()); |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 391 | } |
| 392 | |
| 393 | private Key forBindingMethod( |
| 394 | ExecutableElement method, |
| 395 | TypeElement contributingModule, |
| 396 | Optional<TypeElement> frameworkType) { |
| dpb | 4955ef9 | 2016-04-29 12:31:14 -0700 | [diff] [blame] | 397 | checkArgument(method.getKind().equals(METHOD)); |
| dpb | db24072 | 2016-01-25 14:06:41 -0800 | [diff] [blame] | 398 | ExecutableType methodType = |
| dpb | 4955ef9 | 2016-04-29 12:31:14 -0700 | [diff] [blame] | 399 | MoreTypes.asExecutable( |
| 400 | types.asMemberOf(MoreTypes.asDeclared(contributingModule.asType()), method)); |
| ronshapiro | 665a4a5 | 2016-04-12 21:10:00 -0700 | [diff] [blame] | 401 | ContributionType contributionType = ContributionType.fromBindingMethod(method); |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 402 | TypeMirror returnType = methodType.getReturnType(); |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 403 | if (frameworkType.isPresent() |
| dpb | 800c746 | 2016-09-08 09:11:47 -0700 | [diff] [blame] | 404 | && frameworkType.get().equals(getClassElement(Producer.class)) |
| Ron Shapiro | 5f21a80 | 2016-09-21 10:23:55 -0400 | [diff] [blame] | 405 | && isType(returnType)) { |
| 406 | if (isTypeOf(ListenableFuture.class, returnType)) { |
| 407 | returnType = getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments()); |
| 408 | } else if (contributionType.equals(ContributionType.SET_VALUES) |
| 409 | && SetType.isSet(returnType)) { |
| 410 | SetType setType = SetType.from(returnType); |
| 411 | if (setType.elementsAreTypeOf(ListenableFuture.class)) { |
| 412 | returnType = |
| 413 | types.getDeclaredType( |
| 414 | getClassElement(Set.class), |
| 415 | setType.unwrappedElementType(ListenableFuture.class)); |
| 416 | } |
| 417 | } |
| beder | 8fdbb9e | 2014-12-08 10:14:17 -0800 | [diff] [blame] | 418 | } |
| dpb | 0748982 | 2015-07-06 10:34:40 -0700 | [diff] [blame] | 419 | TypeMirror keyType = |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 420 | bindingMethodKeyType(returnType, method, contributionType, frameworkType); |
| dpb | db24072 | 2016-01-25 14:06:41 -0800 | [diff] [blame] | 421 | Key key = forMethod(method, keyType); |
| ronshapiro | 665a4a5 | 2016-04-12 21:10:00 -0700 | [diff] [blame] | 422 | return contributionType.equals(ContributionType.UNIQUE) |
| dpb | db24072 | 2016-01-25 14:06:41 -0800 | [diff] [blame] | 423 | ? key |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 424 | : key.toBuilder() |
| 425 | .multibindingContributionIdentifier( |
| 426 | new MultibindingContributionIdentifier(method, contributingModule)) |
| 427 | .build(); |
| dpb | 0748982 | 2015-07-06 10:34:40 -0700 | [diff] [blame] | 428 | } |
| beder | 893f770 | 2016-02-17 05:22:50 -0800 | [diff] [blame] | 429 | |
| dpb | 51bf8b4 | 2015-12-18 09:59:56 -0800 | [diff] [blame] | 430 | /** |
| ronshapiro | 6d85388 | 2017-03-16 16:37:02 -0700 | [diff] [blame] | 431 | * Returns the key for a {@link Multibinds @Multibinds} method. |
| dpb | 51bf8b4 | 2015-12-18 09:59:56 -0800 | [diff] [blame] | 432 | * |
| ronshapiro | 23c17b8 | 2016-06-10 09:57:13 -0700 | [diff] [blame] | 433 | * <p>The key's type is either {@code Set<T>} or {@code Map<K, F<V>>}, where {@code F} is either |
| dpb | 51bf8b4 | 2015-12-18 09:59:56 -0800 | [diff] [blame] | 434 | * {@link Provider} or {@link Producer}, depending on {@code bindingType}. |
| 435 | */ |
| dpb | 228475f | 2016-05-24 14:46:53 -0700 | [diff] [blame] | 436 | Key forMultibindsMethod( |
| dpb | 51bf8b4 | 2015-12-18 09:59:56 -0800 | [diff] [blame] | 437 | BindingType bindingType, ExecutableType executableType, ExecutableElement method) { |
| 438 | checkArgument(method.getKind().equals(METHOD), "%s must be a method", method); |
| 439 | TypeElement factoryType = |
| 440 | elements.getTypeElement(bindingType.frameworkClass().getCanonicalName()); |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 441 | TypeMirror returnType = executableType.getReturnType(); |
| dpb | 51bf8b4 | 2015-12-18 09:59:56 -0800 | [diff] [blame] | 442 | TypeMirror keyType = |
| 443 | MapType.isMap(returnType) |
| 444 | ? mapOfFrameworkType( |
| 445 | MapType.from(returnType).keyType(), |
| 446 | factoryType, |
| 447 | MapType.from(returnType).valueType()) |
| 448 | : returnType; |
| 449 | return forMethod(method, keyType); |
| 450 | } |
| dpb | 0748982 | 2015-07-06 10:34:40 -0700 | [diff] [blame] | 451 | |
| ronshapiro | 846fd94 | 2016-06-01 11:50:01 -0700 | [diff] [blame] | 452 | private TypeMirror bindingMethodKeyType( |
| dpb | 0748982 | 2015-07-06 10:34:40 -0700 | [diff] [blame] | 453 | TypeMirror returnType, |
| 454 | ExecutableElement method, |
| ronshapiro | 665a4a5 | 2016-04-12 21:10:00 -0700 | [diff] [blame] | 455 | ContributionType contributionType, |
| ronshapiro | 23c17b8 | 2016-06-10 09:57:13 -0700 | [diff] [blame] | 456 | Optional<TypeElement> frameworkType) { |
| ronshapiro | 665a4a5 | 2016-04-12 21:10:00 -0700 | [diff] [blame] | 457 | switch (contributionType) { |
| beder | 8fdbb9e | 2014-12-08 10:14:17 -0800 | [diff] [blame] | 458 | case UNIQUE: |
| dpb | 0748982 | 2015-07-06 10:34:40 -0700 | [diff] [blame] | 459 | return returnType; |
| beder | 8fdbb9e | 2014-12-08 10:14:17 -0800 | [diff] [blame] | 460 | case SET: |
| dpb | 7edcd4d | 2016-06-21 07:52:39 -0700 | [diff] [blame] | 461 | return setOf(returnType); |
| beder | 8fdbb9e | 2014-12-08 10:14:17 -0800 | [diff] [blame] | 462 | case MAP: |
| ronshapiro | 23c17b8 | 2016-06-10 09:57:13 -0700 | [diff] [blame] | 463 | if (frameworkType.isPresent()) { |
| 464 | return mapOfFrameworkType(mapKeyType(method), frameworkType.get(), returnType); |
| 465 | } else { |
| dpb | 7edcd4d | 2016-06-21 07:52:39 -0700 | [diff] [blame] | 466 | return mapOf(mapKeyType(method), returnType); |
| ronshapiro | 23c17b8 | 2016-06-10 09:57:13 -0700 | [diff] [blame] | 467 | } |
| beder | 8fdbb9e | 2014-12-08 10:14:17 -0800 | [diff] [blame] | 468 | case SET_VALUES: |
| 469 | // TODO(gak): do we want to allow people to use "covariant return" here? |
| ronshapiro | 665a4a5 | 2016-04-12 21:10:00 -0700 | [diff] [blame] | 470 | checkArgument(SetType.isSet(returnType)); |
| dpb | 0748982 | 2015-07-06 10:34:40 -0700 | [diff] [blame] | 471 | return returnType; |
| beder | 8fdbb9e | 2014-12-08 10:14:17 -0800 | [diff] [blame] | 472 | default: |
| 473 | throw new AssertionError(); |
| 474 | } |
| 475 | } |
| 476 | |
| dpb | a262042 | 2015-08-15 10:06:54 -0700 | [diff] [blame] | 477 | /** |
| ronshapiro | 23c17b8 | 2016-06-10 09:57:13 -0700 | [diff] [blame] | 478 | * Returns the key for a binding associated with a {@link DelegateDeclaration}. |
| 479 | * |
| 480 | * If {@code delegateDeclaration} is {@code @IntoMap}, transforms the {@code Map<K, V>} key |
| 481 | * from {@link DelegateDeclaration#key()} to {@code Map<K, FrameworkType<V>>}. If {@code |
| 482 | * delegateDeclaration} is not a map contribution, its key is returned. |
| 483 | */ |
| 484 | Key forDelegateBinding( |
| 485 | DelegateDeclaration delegateDeclaration, Class<?> frameworkType) { |
| 486 | return delegateDeclaration.contributionType().equals(ContributionType.MAP) |
| 487 | ? wrapMapValue(delegateDeclaration.key(), frameworkType) |
| 488 | : delegateDeclaration.key(); |
| 489 | } |
| 490 | |
| dpb | 0748982 | 2015-07-06 10:34:40 -0700 | [diff] [blame] | 491 | private TypeMirror mapKeyType(ExecutableElement method) { |
| dpb | a57e387 | 2015-07-15 09:40:55 -0700 | [diff] [blame] | 492 | AnnotationMirror mapKeyAnnotation = getMapKey(method).get(); |
| dpb | 482cbd4 | 2015-08-07 08:17:31 -0700 | [diff] [blame] | 493 | return MapKeys.unwrapValue(mapKeyAnnotation).isPresent() |
| dpb | 0748982 | 2015-07-06 10:34:40 -0700 | [diff] [blame] | 494 | ? getUnwrappedMapKeyType(mapKeyAnnotation.getAnnotationType(), types) |
| 495 | : mapKeyAnnotation.getAnnotationType(); |
| 496 | } |
| 497 | |
| 498 | private Key forMethod(ExecutableElement method, TypeMirror keyType) { |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 499 | return forQualifiedType(getQualifier(method), keyType); |
| dpb | 0748982 | 2015-07-06 10:34:40 -0700 | [diff] [blame] | 500 | } |
| 501 | |
| sameb | 2ea676a | 2015-01-15 10:20:24 -0800 | [diff] [blame] | 502 | Key forInjectConstructorWithResolvedType(TypeMirror type) { |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 503 | return builder(type).build(); |
| Christian Edward Gruber | 9daba78 | 2014-05-04 18:06:26 -0700 | [diff] [blame] | 504 | } |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 505 | |
| ronshapiro | dc07ed5 | 2017-08-23 08:52:10 -0700 | [diff] [blame^] | 506 | // TODO(ronshapiro): Remove these conveniences which are simple wrappers around Key.Builder |
| 507 | Key forType(TypeMirror type) { |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 508 | return builder(type).build(); |
| gak | ad6b5e5 | 2014-09-03 09:38:09 -0700 | [diff] [blame] | 509 | } |
| 510 | |
| 511 | Key forMembersInjectedType(TypeMirror type) { |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 512 | return builder(type).build(); |
| Christian Edward Gruber | 6a66a5b | 2014-06-12 21:10:17 -0700 | [diff] [blame] | 513 | } |
| 514 | |
| 515 | Key forQualifiedType(Optional<AnnotationMirror> qualifier, TypeMirror type) { |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 516 | return builder(boxPrimitives(type)).qualifier(qualifier).build(); |
| Christian Edward Gruber | 122f212 | 2014-06-16 16:06:00 -0700 | [diff] [blame] | 517 | } |
| 518 | |
| beder | 893f770 | 2016-02-17 05:22:50 -0800 | [diff] [blame] | 519 | Key forProductionExecutor() { |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 520 | return builder(getClassElement(Executor.class).asType()) |
| 521 | .qualifier(getClassElement(Production.class)) |
| 522 | .build(); |
| beder | 893f770 | 2016-02-17 05:22:50 -0800 | [diff] [blame] | 523 | } |
| 524 | |
| 525 | Key forProductionImplementationExecutor() { |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 526 | return builder(getClassElement(Executor.class).asType()) |
| 527 | .qualifier(getClassElement(ProductionImplementation.class)) |
| 528 | .build(); |
| beder | 893f770 | 2016-02-17 05:22:50 -0800 | [diff] [blame] | 529 | } |
| 530 | |
| gak | b8dea08 | 2016-08-11 12:17:56 -0700 | [diff] [blame] | 531 | Key forProductionComponentMonitor() { |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 532 | return builder(getClassElement(ProductionComponentMonitor.class).asType()).build(); |
| gak | b8dea08 | 2016-08-11 12:17:56 -0700 | [diff] [blame] | 533 | } |
| 534 | |
| cgruber | 815ccd1 | 2014-09-02 13:06:30 -0700 | [diff] [blame] | 535 | /** |
| dpb | 1da2999 | 2016-06-24 07:54:52 -0700 | [diff] [blame] | 536 | * If {@code requestKey} is for a {@code Map<K, V>} or {@code Map<K, Produced<V>>}, returns keys |
| 537 | * for {@code Map<K, Provider<V>>} and {@code Map<K, Producer<V>>} (if Dagger-Producers is on |
| 538 | * the classpath). |
| 539 | */ |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 540 | ImmutableSet<Key> implicitFrameworkMapKeys(Key requestKey) { |
| 541 | return Stream.of( |
| 542 | implicitMapProviderKeyFrom(requestKey), implicitMapProducerKeyFrom(requestKey)) |
| 543 | .filter(Optional::isPresent) |
| 544 | .map(Optional::get) |
| 545 | .collect(toImmutableSet()); |
| dpb | 1da2999 | 2016-06-24 07:54:52 -0700 | [diff] [blame] | 546 | } |
| 547 | |
| 548 | /** |
| cgruber | 815ccd1 | 2014-09-02 13:06:30 -0700 | [diff] [blame] | 549 | * Optionally extract a {@link Key} for the underlying provision binding(s) if such a |
| gak | ad6b5e5 | 2014-09-03 09:38:09 -0700 | [diff] [blame] | 550 | * valid key can be inferred from the given key. Specifically, if the key represents a |
| cgruber | 815ccd1 | 2014-09-02 13:06:30 -0700 | [diff] [blame] | 551 | * {@link Map}{@code <K, V>}, a key of {@code Map<K, Provider<V>>} will be returned. |
| 552 | */ |
| 553 | Optional<Key> implicitMapProviderKeyFrom(Key possibleMapKey) { |
| dpb | 1da2999 | 2016-06-24 07:54:52 -0700 | [diff] [blame] | 554 | return wrapMapKey(possibleMapKey, Provider.class); |
| beder | ca9c82b | 2015-01-23 18:30:22 -0800 | [diff] [blame] | 555 | } |
| 556 | |
| 557 | /** |
| 558 | * Optionally extract a {@link Key} for the underlying production binding(s) if such a |
| 559 | * valid key can be inferred from the given key. Specifically, if the key represents a |
| beder | c2aea2b | 2016-01-19 12:13:37 -0800 | [diff] [blame] | 560 | * {@link Map}{@code <K, V>} or {@code Map<K, Produced<V>>}, a key of |
| 561 | * {@code Map<K, Producer<V>>} will be returned. |
| beder | ca9c82b | 2015-01-23 18:30:22 -0800 | [diff] [blame] | 562 | */ |
| 563 | Optional<Key> implicitMapProducerKeyFrom(Key possibleMapKey) { |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 564 | return firstPresent( |
| 565 | rewrapMapKey(possibleMapKey, Produced.class, Producer.class), |
| 566 | wrapMapKey(possibleMapKey, Producer.class)); |
| beder | c2aea2b | 2016-01-19 12:13:37 -0800 | [diff] [blame] | 567 | } |
| 568 | |
| 569 | /** |
| ronshapiro | 23c17b8 | 2016-06-10 09:57:13 -0700 | [diff] [blame] | 570 | * Keys for map contributions from {@link dagger.Provides} and {@link dagger.producers.Produces} |
| 571 | * are in the form {@code Map<K, Framework<V>>}, but keys for {@link Binds} methods are just |
| 572 | * {@code Map<K, V>} since the framework type is not known until graph resolution. This |
| 573 | * translates from the {@code @Provides}/{@code @Produces} format into the {@code @Binds} |
| 574 | * format. If {@link Key#type() possibleMapKey.type()} is not a {@code Map<K, Framework<V>>}, |
| 575 | * returns {@code possibleMapKey}. |
| 576 | */ |
| 577 | Key convertToDelegateKey(Key possibleMapKey) { |
| 578 | if (!MapType.isMap(possibleMapKey)) { |
| 579 | return possibleMapKey; |
| 580 | } |
| 581 | MapType mapType = MapType.from(possibleMapKey); |
| 582 | TypeMirror wrappedValueType; |
| 583 | if (mapType.valuesAreTypeOf(Provider.class)) { |
| 584 | wrappedValueType = mapType.unwrappedValueType(Provider.class); |
| 585 | } else if (mapType.valuesAreTypeOf(Producer.class)) { |
| 586 | wrappedValueType = mapType.unwrappedValueType(Producer.class); |
| 587 | } else { |
| 588 | return possibleMapKey; |
| 589 | } |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 590 | return possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build(); |
| ronshapiro | 23c17b8 | 2016-06-10 09:57:13 -0700 | [diff] [blame] | 591 | } |
| 592 | |
| 593 | /** |
| 594 | * Converts a {@link Key} of type {@code Map<K, V>} to {@code Map<K, Provider<V>>}. |
| 595 | */ |
| 596 | private Key wrapMapValue(Key key, Class<?> newWrappingClass) { |
| 597 | checkArgument( |
| 598 | FrameworkTypes.isFrameworkType( |
| 599 | elements.getTypeElement(newWrappingClass.getName()).asType())); |
| dpb | 1da2999 | 2016-06-24 07:54:52 -0700 | [diff] [blame] | 600 | return wrapMapKey(key, newWrappingClass).get(); |
| ronshapiro | 23c17b8 | 2016-06-10 09:57:13 -0700 | [diff] [blame] | 601 | } |
| 602 | |
| 603 | /** |
| dpb | 1da2999 | 2016-06-24 07:54:52 -0700 | [diff] [blame] | 604 | * If {@code key}'s type is {@code Map<K, CurrentWrappingClass<Bar>>}, returns a key with type |
| 605 | * {@code Map<K, NewWrappingClass<Bar>>} with the same qualifier. Otherwise returns {@link |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 606 | * Optional#empty()}. |
| dpb | 1da2999 | 2016-06-24 07:54:52 -0700 | [diff] [blame] | 607 | * |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 608 | * <p>Returns {@link Optional#empty()} if {@code newWrappingClass} is not in the classpath. |
| dpb | 1da2999 | 2016-06-24 07:54:52 -0700 | [diff] [blame] | 609 | * |
| 610 | * @throws IllegalArgumentException if {@code newWrappingClass} is the same as {@code |
| 611 | * currentWrappingClass} |
| beder | c2aea2b | 2016-01-19 12:13:37 -0800 | [diff] [blame] | 612 | */ |
| dpb | 1da2999 | 2016-06-24 07:54:52 -0700 | [diff] [blame] | 613 | Optional<Key> rewrapMapKey( |
| beder | c2aea2b | 2016-01-19 12:13:37 -0800 | [diff] [blame] | 614 | Key possibleMapKey, Class<?> currentWrappingClass, Class<?> newWrappingClass) { |
| 615 | checkArgument(!currentWrappingClass.equals(newWrappingClass)); |
| ronshapiro | 698653f | 2016-06-03 09:14:13 -0700 | [diff] [blame] | 616 | if (MapType.isMap(possibleMapKey)) { |
| 617 | MapType mapType = MapType.from(possibleMapKey); |
| beder | c2aea2b | 2016-01-19 12:13:37 -0800 | [diff] [blame] | 618 | if (mapType.valuesAreTypeOf(currentWrappingClass)) { |
| 619 | TypeElement wrappingElement = getClassElement(newWrappingClass); |
| 620 | if (wrappingElement == null) { |
| 621 | // This target might not be compiled with Producers, so wrappingClass might not have an |
| 622 | // associated element. |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 623 | return Optional.empty(); |
| beder | c2aea2b | 2016-01-19 12:13:37 -0800 | [diff] [blame] | 624 | } |
| 625 | DeclaredType wrappedValueType = |
| 626 | types.getDeclaredType( |
| 627 | wrappingElement, mapType.unwrappedValueType(currentWrappingClass)); |
| dpb | 7edcd4d | 2016-06-21 07:52:39 -0700 | [diff] [blame] | 628 | return Optional.of( |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 629 | possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build()); |
| beder | c2aea2b | 2016-01-19 12:13:37 -0800 | [diff] [blame] | 630 | } |
| 631 | } |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 632 | return Optional.empty(); |
| beder | ca9c82b | 2015-01-23 18:30:22 -0800 | [diff] [blame] | 633 | } |
| 634 | |
| 635 | /** |
| dpb | 1da2999 | 2016-06-24 07:54:52 -0700 | [diff] [blame] | 636 | * If {@code key}'s type is {@code Map<K, Foo>} and {@code Foo} is not {@code WrappingClass |
| 637 | * <Bar>}, returns a key with type {@code Map<K, WrappingClass<Foo>>} with the same qualifier. |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 638 | * Otherwise returns {@link Optional#empty()}. |
| dpb | 1da2999 | 2016-06-24 07:54:52 -0700 | [diff] [blame] | 639 | * |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 640 | * <p>Returns {@link Optional#empty()} if {@code WrappingClass} is not in the classpath. |
| beder | ca9c82b | 2015-01-23 18:30:22 -0800 | [diff] [blame] | 641 | */ |
| dpb | 1da2999 | 2016-06-24 07:54:52 -0700 | [diff] [blame] | 642 | private Optional<Key> wrapMapKey(Key possibleMapKey, Class<?> wrappingClass) { |
| ronshapiro | 698653f | 2016-06-03 09:14:13 -0700 | [diff] [blame] | 643 | if (MapType.isMap(possibleMapKey)) { |
| 644 | MapType mapType = MapType.from(possibleMapKey); |
| dpb | f8c5c7f | 2015-11-19 08:16:43 -0800 | [diff] [blame] | 645 | if (!mapType.valuesAreTypeOf(wrappingClass)) { |
| beder | 69667a2 | 2015-01-24 09:07:31 -0800 | [diff] [blame] | 646 | TypeElement wrappingElement = getClassElement(wrappingClass); |
| 647 | if (wrappingElement == null) { |
| 648 | // This target might not be compiled with Producers, so wrappingClass might not have an |
| 649 | // associated element. |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 650 | return Optional.empty(); |
| beder | 69667a2 | 2015-01-24 09:07:31 -0800 | [diff] [blame] | 651 | } |
| dpb | f8c5c7f | 2015-11-19 08:16:43 -0800 | [diff] [blame] | 652 | DeclaredType wrappedValueType = |
| 653 | types.getDeclaredType(wrappingElement, mapType.valueType()); |
| dpb | 7edcd4d | 2016-06-21 07:52:39 -0700 | [diff] [blame] | 654 | return Optional.of( |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 655 | possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build()); |
| cgruber | 815ccd1 | 2014-09-02 13:06:30 -0700 | [diff] [blame] | 656 | } |
| 657 | } |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 658 | return Optional.empty(); |
| cgruber | 815ccd1 | 2014-09-02 13:06:30 -0700 | [diff] [blame] | 659 | } |
| beder | 6583420 | 2015-09-30 08:49:44 -0700 | [diff] [blame] | 660 | |
| 661 | /** |
| dpb | 1da2999 | 2016-06-24 07:54:52 -0700 | [diff] [blame] | 662 | * If {@code key}'s type is {@code Set<WrappingClass<Bar>>}, returns a key with type {@code Set |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 663 | * <Bar>} with the same qualifier. Otherwise returns {@link Optional#empty()}. |
| beder | 6583420 | 2015-09-30 08:49:44 -0700 | [diff] [blame] | 664 | */ |
| dpb | 1da2999 | 2016-06-24 07:54:52 -0700 | [diff] [blame] | 665 | Optional<Key> unwrapSetKey(Key key, Class<?> wrappingClass) { |
| 666 | if (SetType.isSet(key)) { |
| 667 | SetType setType = SetType.from(key); |
| 668 | if (setType.elementsAreTypeOf(wrappingClass)) { |
| 669 | return Optional.of( |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 670 | key.toBuilder().type(setOf(setType.unwrappedElementType(wrappingClass))).build()); |
| beder | 6583420 | 2015-09-30 08:49:44 -0700 | [diff] [blame] | 671 | } |
| 672 | } |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 673 | return Optional.empty(); |
| beder | 6583420 | 2015-09-30 08:49:44 -0700 | [diff] [blame] | 674 | } |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 675 | |
| 676 | /** |
| 677 | * If {@code key}'s type is {@code Optional<T>} for some {@code T}, returns a key with the same |
| 678 | * qualifier whose type is {@linkplain DependencyRequest#extractKindAndType(TypeMirror) |
| 679 | * extracted} from {@code T}. |
| 680 | */ |
| 681 | Optional<Key> unwrapOptional(Key key) { |
| 682 | if (!OptionalType.isOptional(key)) { |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 683 | return Optional.empty(); |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 684 | } |
| 685 | TypeMirror underlyingType = |
| 686 | DependencyRequest.extractKindAndType(OptionalType.from(key).valueType()).type(); |
| dpb | ec434ad | 2016-09-08 13:24:00 -0700 | [diff] [blame] | 687 | return Optional.of(key.toBuilder().type(underlyingType).build()); |
| dpb | d8d950a | 2016-08-30 09:24:37 -0700 | [diff] [blame] | 688 | } |
| ronshapiro | d804a30 | 2016-11-08 10:21:56 -0800 | [diff] [blame] | 689 | |
| 690 | /** Returns a key for a {@code @ForReleasableReferences(scope) ReleasableReferenceManager}. */ |
| 691 | Key forReleasableReferenceManager(Scope scope) { |
| 692 | return forQualifiedType( |
| 693 | Optional.of(forReleasableReferencesAnnotationMirror(scope)), |
| 694 | getClassElement(ReleasableReferenceManager.class).asType()); |
| 695 | } |
| 696 | |
| 697 | /** |
| 698 | * Returns a key for a {@code @ForReleasableReferences(scope) |
| 699 | * TypedReleasableReferenceManager<metadataType>} |
| 700 | */ |
| 701 | Key forTypedReleasableReferenceManager(Scope scope, DeclaredType metadataType) { |
| 702 | return builder(typedReleasableReferenceManagerOf(metadataType)) |
| 703 | .qualifier(forReleasableReferencesAnnotationMirror(scope)) |
| 704 | .build(); |
| 705 | } |
| 706 | |
| 707 | /** Returns a key for a {@code Set<ReleasableReferenceManager>}. */ |
| 708 | Key forSetOfReleasableReferenceManagers() { |
| 709 | return builder(setOf(getClassElement(ReleasableReferenceManager.class).asType())).build(); |
| 710 | } |
| 711 | |
| 712 | /** Returns a key for a {@code Set<TypedReleasableReferenceManager<metadataType>}. */ |
| 713 | Key forSetOfTypedReleasableReferenceManagers(DeclaredType metadataType) { |
| 714 | return forQualifiedType( |
| dpb | ffd98f6 | 2016-12-20 10:05:16 -0800 | [diff] [blame] | 715 | Optional.empty(), setOf(typedReleasableReferenceManagerOf(metadataType))); |
| ronshapiro | d804a30 | 2016-11-08 10:21:56 -0800 | [diff] [blame] | 716 | } |
| 717 | |
| 718 | private AnnotationMirror forReleasableReferencesAnnotationMirror(Scope scope) { |
| 719 | return SimpleAnnotationMirror.of( |
| 720 | getClassElement(ForReleasableReferences.class), |
| 721 | ImmutableMap.of( |
| 722 | "value", new SimpleTypeAnnotationValue(scope.scopeAnnotationElement().asType()))); |
| 723 | } |
| Christian Edward Gruber | c8e1107 | 2014-03-17 08:38:59 -0700 | [diff] [blame] | 724 | } |
| Christian Edward Gruber | c8e1107 | 2014-03-17 08:38:59 -0700 | [diff] [blame] | 725 | } |