1 /* 2 * Copyright (C) 2014 Google, Inc. 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 package dagger.internal.codegen; 17 18 import com.google.auto.common.MoreElements; 19 import com.google.auto.common.MoreTypes; 20 import com.google.auto.value.AutoValue; 21 import com.google.common.base.Optional; 22 import com.google.common.collect.ImmutableSet; 23 import com.google.common.collect.Sets; 24 import dagger.Provides; 25 import javax.inject.Inject; 26 import javax.lang.model.element.Element; 27 import javax.lang.model.element.ElementKind; 28 import javax.lang.model.element.ExecutableElement; 29 import javax.lang.model.element.TypeElement; 30 import javax.lang.model.type.DeclaredType; 31 import javax.lang.model.type.ExecutableType; 32 import javax.lang.model.type.TypeKind; 33 import javax.lang.model.type.TypeMirror; 34 import javax.lang.model.util.Elements; 35 import javax.lang.model.util.Types; 36 37 import static com.google.auto.common.MoreElements.isAnnotationPresent; 38 import static com.google.auto.common.MoreTypes.asDeclared; 39 import static com.google.common.base.Preconditions.checkArgument; 40 import static com.google.common.base.Preconditions.checkNotNull; 41 import static com.google.common.base.Preconditions.checkState; 42 import static dagger.internal.codegen.InjectionAnnotations.getQualifier; 43 import static dagger.internal.codegen.Scope.scopeOf; 44 import static javax.lang.model.element.ElementKind.CONSTRUCTOR; 45 import static javax.lang.model.element.ElementKind.FIELD; 46 import static javax.lang.model.element.ElementKind.METHOD; 47 48 /** 49 * A value object representing the mechanism by which a {@link Key} can be provided. New instances 50 * should be created using an instance of the {@link Factory}. 51 * 52 * @author Gregory Kick 53 * @since 2.0 54 */ 55 @AutoValue 56 abstract class ProvisionBinding extends ContributionBinding { 57 58 @Override 59 Binding.Type bindingType() { 60 return Binding.Type.PROVISION; 61 } 62 63 @Override 64 abstract Scope scope(); 65 66 static final class Factory { 67 private final Elements elements; 68 private final Types types; 69 private final Key.Factory keyFactory; 70 private final DependencyRequest.Factory dependencyRequestFactory; 71 72 Factory(Elements elements, Types types, Key.Factory keyFactory, 73 DependencyRequest.Factory dependencyRequestFactory) { 74 this.elements = elements; 75 this.types = types; 76 this.keyFactory = keyFactory; 77 this.dependencyRequestFactory = dependencyRequestFactory; 78 } 79 80 /** Returns an unresolved version of this binding. */ 81 ProvisionBinding unresolve(ProvisionBinding binding) { 82 checkState(binding.hasNonDefaultTypeParameters()); 83 return forInjectConstructor((ExecutableElement) binding.bindingElement(), 84 Optional.<TypeMirror>absent()); 85 } 86 87 /** 88 * Returns a ProvisionBinding for the given element. If {@code resolvedType} is present, this 89 * will return a resolved binding, with the key & type resolved to the given type (using 90 * {@link Types#asMemberOf(DeclaredType, Element)}). 91 */ 92 ProvisionBinding forInjectConstructor(ExecutableElement constructorElement, 93 Optional<TypeMirror> resolvedType) { 94 checkNotNull(constructorElement); 95 checkArgument(constructorElement.getKind().equals(CONSTRUCTOR)); 96 checkArgument(isAnnotationPresent(constructorElement, Inject.class)); 97 checkArgument(!getQualifier(constructorElement).isPresent()); 98 99 ExecutableType cxtorType = MoreTypes.asExecutable(constructorElement.asType()); 100 DeclaredType enclosingCxtorType = 101 MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType()); 102 // If the class this is constructing has some type arguments, resolve everything. 103 if (!enclosingCxtorType.getTypeArguments().isEmpty() && resolvedType.isPresent()) { 104 DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get()); 105 // Validate that we're resolving from the correct type. 106 checkState(types.isSameType(types.erasure(resolved), types.erasure(enclosingCxtorType)), 107 "erased expected type: %s, erased actual type: %s", 108 types.erasure(resolved), types.erasure(enclosingCxtorType)); 109 cxtorType = MoreTypes.asExecutable(types.asMemberOf(resolved, constructorElement)); 110 enclosingCxtorType = resolved; 111 } 112 113 Key key = keyFactory.forInjectConstructorWithResolvedType(enclosingCxtorType); 114 checkArgument(!key.qualifier().isPresent()); 115 ImmutableSet<DependencyRequest> dependencies = 116 dependencyRequestFactory.forRequiredResolvedVariables(enclosingCxtorType, 117 constructorElement.getParameters(), 118 cxtorType.getParameterTypes()); 119 Optional<DependencyRequest> membersInjectionRequest = 120 membersInjectionRequest(enclosingCxtorType); 121 Scope scope = Scope.scopeOf(constructorElement.getEnclosingElement()); 122 123 TypeElement bindingTypeElement = 124 MoreElements.asType(constructorElement.getEnclosingElement()); 125 126 return new AutoValue_ProvisionBinding( 127 key, 128 constructorElement, 129 dependencies, 130 findBindingPackage(key), 131 hasNonDefaultTypeParameters(bindingTypeElement, key.type(), types), 132 Optional.<DeclaredType>absent(), 133 Optional.<TypeElement>absent(), 134 membersInjectionRequest, 135 Kind.INJECTION, 136 Provides.Type.UNIQUE, 137 scope); 138 } 139 140 private static final ImmutableSet<ElementKind> MEMBER_KINDS = 141 Sets.immutableEnumSet(METHOD, FIELD); 142 143 private Optional<DependencyRequest> membersInjectionRequest(DeclaredType type) { 144 TypeElement typeElement = MoreElements.asType(type.asElement()); 145 if (!types.isSameType(elements.getTypeElement(Object.class.getCanonicalName()).asType(), 146 typeElement.getSuperclass())) { 147 return Optional.of(dependencyRequestFactory.forMembersInjectedType(type)); 148 } 149 for (Element enclosedElement : typeElement.getEnclosedElements()) { 150 if (MEMBER_KINDS.contains(enclosedElement.getKind()) 151 && (isAnnotationPresent(enclosedElement, Inject.class))) { 152 return Optional.of(dependencyRequestFactory.forMembersInjectedType(type)); 153 } 154 } 155 return Optional.absent(); 156 } 157 158 ProvisionBinding forProvidesMethod(ExecutableElement providesMethod, TypeMirror contributedBy) { 159 checkNotNull(providesMethod); 160 checkArgument(providesMethod.getKind().equals(METHOD)); 161 checkArgument(contributedBy.getKind().equals(TypeKind.DECLARED)); 162 Provides providesAnnotation = providesMethod.getAnnotation(Provides.class); 163 checkArgument(providesAnnotation != null); 164 DeclaredType declaredContainer = MoreTypes.asDeclared(contributedBy); 165 ExecutableType resolvedMethod = 166 MoreTypes.asExecutable(types.asMemberOf(declaredContainer, providesMethod)); 167 Key key = keyFactory.forProvidesMethod(resolvedMethod, providesMethod); 168 ImmutableSet<DependencyRequest> dependencies = 169 dependencyRequestFactory.forRequiredResolvedVariables( 170 declaredContainer, 171 providesMethod.getParameters(), 172 resolvedMethod.getParameterTypes()); 173 Scope scope = Scope.scopeOf(providesMethod); 174 return new AutoValue_ProvisionBinding( 175 key, 176 providesMethod, 177 dependencies, 178 findBindingPackage(key), 179 false /* no non-default parameter types */, 180 ConfigurationAnnotations.getNullableType(providesMethod), 181 Optional.of(MoreTypes.asTypeElement(declaredContainer)), 182 Optional.<DependencyRequest>absent(), 183 Kind.PROVISION, 184 providesAnnotation.type(), 185 scope); 186 } 187 188 ProvisionBinding implicitMapOfProviderBinding(DependencyRequest mapOfValueRequest) { 189 checkNotNull(mapOfValueRequest); 190 Optional<Key> implicitMapOfProviderKey = 191 keyFactory.implicitMapProviderKeyFrom(mapOfValueRequest.key()); 192 checkArgument( 193 implicitMapOfProviderKey.isPresent(), 194 "%s is not a request for Map<K, V>", 195 mapOfValueRequest); 196 DependencyRequest implicitMapOfProviderRequest = 197 dependencyRequestFactory.forImplicitMapBinding( 198 mapOfValueRequest, implicitMapOfProviderKey.get()); 199 return new AutoValue_ProvisionBinding( 200 mapOfValueRequest.key(), 201 implicitMapOfProviderRequest.requestElement(), 202 ImmutableSet.of(implicitMapOfProviderRequest), 203 findBindingPackage(mapOfValueRequest.key()), 204 false /* no non-default parameter types */, 205 Optional.<DeclaredType>absent(), 206 Optional.<TypeElement>absent(), 207 Optional.<DependencyRequest>absent(), 208 Kind.SYNTHETIC, 209 Provides.Type.MAP, 210 scopeOf(implicitMapOfProviderRequest.requestElement())); 211 } 212 213 ProvisionBinding forComponent(TypeElement componentDefinitionType) { 214 checkNotNull(componentDefinitionType); 215 return new AutoValue_ProvisionBinding( 216 keyFactory.forComponent(componentDefinitionType.asType()), 217 componentDefinitionType, 218 ImmutableSet.<DependencyRequest>of(), 219 Optional.<String>absent(), 220 false /* no non-default parameter types */, 221 Optional.<DeclaredType>absent(), 222 Optional.<TypeElement>absent(), 223 Optional.<DependencyRequest>absent(), 224 Kind.COMPONENT, 225 Provides.Type.UNIQUE, 226 Scope.unscoped()); 227 } 228 229 ProvisionBinding forComponentMethod(ExecutableElement componentMethod) { 230 checkNotNull(componentMethod); 231 checkArgument(componentMethod.getKind().equals(METHOD)); 232 checkArgument(componentMethod.getParameters().isEmpty()); 233 Scope scope = Scope.scopeOf(componentMethod); 234 return new AutoValue_ProvisionBinding( 235 keyFactory.forComponentMethod(componentMethod), 236 componentMethod, 237 ImmutableSet.<DependencyRequest>of(), 238 Optional.<String>absent(), 239 false /* no non-default parameter types */, 240 ConfigurationAnnotations.getNullableType(componentMethod), 241 Optional.<TypeElement>absent(), 242 Optional.<DependencyRequest>absent(), 243 Kind.COMPONENT_PROVISION, 244 Provides.Type.UNIQUE, 245 scope); 246 } 247 248 ProvisionBinding forSubcomponentBuilderMethod( 249 ExecutableElement subcomponentBuilderMethod, TypeElement contributedBy) { 250 checkNotNull(subcomponentBuilderMethod); 251 checkArgument(subcomponentBuilderMethod.getKind().equals(METHOD)); 252 checkArgument(subcomponentBuilderMethod.getParameters().isEmpty()); 253 DeclaredType declaredContainer = asDeclared(contributedBy.asType()); 254 return new AutoValue_ProvisionBinding( 255 keyFactory.forSubcomponentBuilderMethod(subcomponentBuilderMethod, declaredContainer), 256 subcomponentBuilderMethod, 257 ImmutableSet.<DependencyRequest>of(), 258 Optional.<String>absent(), 259 false /* no non-default parameter types */, 260 Optional.<DeclaredType>absent(), 261 Optional.of(contributedBy), 262 Optional.<DependencyRequest>absent(), 263 Kind.SUBCOMPONENT_BUILDER, 264 Provides.Type.UNIQUE, 265 Scope.unscoped()); 266 } 267 } 268 } 269