1 /* 2 * Copyright (C) 2019 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.hilt.processor.internal; 18 19 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 20 21 import com.google.auto.common.MoreElements; 22 import com.google.common.base.Preconditions; 23 import com.google.common.collect.ImmutableSet; 24 import com.squareup.javapoet.AnnotationSpec; 25 import com.squareup.javapoet.ClassName; 26 import dagger.hilt.processor.internal.definecomponent.DefineComponents; 27 import javax.lang.model.element.Element; 28 import javax.lang.model.element.TypeElement; 29 import javax.lang.model.util.Elements; 30 31 /** Helper methods for defining components and the component hierarchy. */ 32 public final class Components { 33 // TODO(bcorso): Remove this once all usages are replaced with #getComponents(). 34 /** 35 * Returns the {@link ComponentDescriptor}s for a given element annotated with {@link 36 * dagger.hilt.InstallIn}. 37 */ getComponentDescriptors( Elements elements, Element element)38 public static ImmutableSet<ComponentDescriptor> getComponentDescriptors( 39 Elements elements, Element element) { 40 DefineComponents defineComponents = DefineComponents.create(); 41 return getComponents(elements, element).stream() 42 .map(component -> elements.getTypeElement(component.canonicalName())) 43 // TODO(b/144939893): Memoize ComponentDescriptors so we're not recalculating. 44 .map(defineComponents::componentDescriptor) 45 .collect(toImmutableSet()); 46 } 47 48 /** Returns the {@link dagger.hilt.InstallIn} components for a given element. */ getComponents(Elements elements, Element element)49 public static ImmutableSet<ClassName> getComponents(Elements elements, Element element) { 50 ImmutableSet<ClassName> components; 51 if (Processors.hasAnnotation(element, ClassNames.INSTALL_IN) 52 || Processors.hasAnnotation(element, ClassNames.TEST_INSTALL_IN)) { 53 components = getHiltInstallInComponents(elements, element); 54 } else { 55 // Check the enclosing element in case it passed in module is a companion object. This helps 56 // in cases where the element was arrived at by checking a binding method and moving outward. 57 Element enclosing = element.getEnclosingElement(); 58 if (enclosing != null 59 && MoreElements.isType(enclosing) 60 && MoreElements.isType(element) 61 && Processors.hasAnnotation(enclosing, ClassNames.MODULE) 62 && KotlinMetadataUtils.getMetadataUtil().isCompanionObjectClass( 63 MoreElements.asType(element))) { 64 return getComponents(elements, enclosing); 65 } 66 if (Processors.hasErrorTypeAnnotation(element)) { 67 throw new BadInputException( 68 "Error annotation found on element " + element + ". Look above for compilation errors", 69 element); 70 } else { 71 throw new BadInputException( 72 String.format( 73 "An @InstallIn annotation is required for: %s." , 74 element), 75 element); 76 } 77 } 78 79 return components; 80 } 81 getInstallInAnnotationSpec(ImmutableSet<ClassName> components)82 public static AnnotationSpec getInstallInAnnotationSpec(ImmutableSet<ClassName> components) { 83 Preconditions.checkArgument(!components.isEmpty()); 84 AnnotationSpec.Builder builder = AnnotationSpec.builder(ClassNames.INSTALL_IN); 85 components.forEach(component -> builder.addMember("value", "$T.class", component)); 86 return builder.build(); 87 } 88 getHiltInstallInComponents( Elements elements, Element element)89 private static ImmutableSet<ClassName> getHiltInstallInComponents( 90 Elements elements, Element element) { 91 Preconditions.checkArgument( 92 Processors.hasAnnotation(element, ClassNames.INSTALL_IN) 93 || Processors.hasAnnotation(element, ClassNames.TEST_INSTALL_IN)); 94 95 ImmutableSet<TypeElement> components = 96 ImmutableSet.copyOf( 97 Processors.hasAnnotation(element, ClassNames.INSTALL_IN) 98 ? Processors.getAnnotationClassValues( 99 elements, 100 Processors.getAnnotationMirror(element, ClassNames.INSTALL_IN), 101 "value") 102 : Processors.getAnnotationClassValues( 103 elements, 104 Processors.getAnnotationMirror(element, ClassNames.TEST_INSTALL_IN), 105 "components")); 106 107 ImmutableSet<TypeElement> undefinedComponents = 108 components.stream() 109 .filter(component -> !Processors.hasAnnotation(component, ClassNames.DEFINE_COMPONENT)) 110 .collect(toImmutableSet()); 111 112 ProcessorErrors.checkState( 113 undefinedComponents.isEmpty(), 114 element, 115 "@InstallIn, can only be used with @DefineComponent-annotated classes, but found: %s", 116 undefinedComponents); 117 118 return components.stream().map(ClassName::get).collect(toImmutableSet()); 119 } 120 Components()121 private Components() {} 122 } 123