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.definecomponent; 18 19 import static javax.lang.model.element.Modifier.STATIC; 20 21 import com.google.auto.common.MoreElements; 22 import com.google.auto.common.MoreTypes; 23 import com.google.auto.value.AutoValue; 24 import com.squareup.javapoet.ClassName; 25 import com.squareup.javapoet.TypeName; 26 import dagger.hilt.processor.internal.ClassNames; 27 import dagger.hilt.processor.internal.ProcessorErrors; 28 import dagger.hilt.processor.internal.Processors; 29 import dagger.hilt.processor.internal.definecomponent.DefineComponentMetadatas.DefineComponentMetadata; 30 import java.util.HashMap; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.stream.Collectors; 34 import javax.lang.model.element.Element; 35 import javax.lang.model.element.ElementKind; 36 import javax.lang.model.element.ExecutableElement; 37 import javax.lang.model.element.TypeElement; 38 import javax.lang.model.element.VariableElement; 39 import javax.lang.model.type.TypeKind; 40 import javax.lang.model.type.TypeMirror; 41 import javax.lang.model.util.ElementFilter; 42 43 /** Metadata for types annotated with {@link dagger.hilt.DefineComponent.Builder}. */ 44 final class DefineComponentBuilderMetadatas { create(DefineComponentMetadatas componentMetadatas)45 static DefineComponentBuilderMetadatas create(DefineComponentMetadatas componentMetadatas) { 46 return new DefineComponentBuilderMetadatas(componentMetadatas); 47 } 48 49 private final Map<Element, DefineComponentBuilderMetadata> builderMetadatas = new HashMap<>(); 50 private final DefineComponentMetadatas componentMetadatas; 51 DefineComponentBuilderMetadatas(DefineComponentMetadatas componentMetadatas)52 private DefineComponentBuilderMetadatas(DefineComponentMetadatas componentMetadatas) { 53 this.componentMetadatas = componentMetadatas; 54 } 55 get(Element element)56 DefineComponentBuilderMetadata get(Element element) { 57 if (!builderMetadatas.containsKey(element)) { 58 builderMetadatas.put(element, getUncached(element)); 59 } 60 return builderMetadatas.get(element); 61 } 62 getUncached(Element element)63 private DefineComponentBuilderMetadata getUncached(Element element) { 64 ProcessorErrors.checkState( 65 Processors.hasAnnotation(element, ClassNames.DEFINE_COMPONENT_BUILDER), 66 element, 67 "%s, expected to be annotated with @DefineComponent.Builder. Found: %s", 68 element, 69 element.getAnnotationMirrors()); 70 71 // TODO(bcorso): Allow abstract classes? 72 ProcessorErrors.checkState( 73 element.getKind().equals(ElementKind.INTERFACE), 74 element, 75 "@DefineComponent.Builder is only allowed on interfaces. Found: %s", 76 element); 77 TypeElement builder = MoreElements.asType(element); 78 79 // TODO(bcorso): Allow extending interfaces? 80 ProcessorErrors.checkState( 81 builder.getInterfaces().isEmpty(), 82 builder, 83 "@DefineComponent.Builder %s, cannot extend a super class or interface. Found: %s", 84 builder, 85 builder.getInterfaces()); 86 87 // TODO(bcorso): Allow type parameters? 88 ProcessorErrors.checkState( 89 builder.getTypeParameters().isEmpty(), 90 builder, 91 "@DefineComponent.Builder %s, cannot have type parameters.", 92 builder.asType()); 93 94 List<VariableElement> nonStaticFields = 95 ElementFilter.fieldsIn(builder.getEnclosedElements()).stream() 96 .filter(method -> !method.getModifiers().contains(STATIC)) 97 .collect(Collectors.toList()); 98 ProcessorErrors.checkState( 99 nonStaticFields.isEmpty(), 100 builder, 101 "@DefineComponent.Builder %s, cannot have non-static fields. Found: %s", 102 builder, 103 nonStaticFields); 104 105 List<ExecutableElement> buildMethods = 106 ElementFilter.methodsIn(builder.getEnclosedElements()).stream() 107 .filter(method -> !method.getModifiers().contains(STATIC)) 108 .filter(method -> method.getParameters().isEmpty()) 109 .collect(Collectors.toList()); 110 111 ProcessorErrors.checkState( 112 buildMethods.size() == 1, 113 builder, 114 "@DefineComponent.Builder %s, must have exactly 1 build method that takes no parameters. " 115 + "Found: %s", 116 builder, 117 buildMethods); 118 119 ExecutableElement buildMethod = buildMethods.get(0); 120 TypeMirror component = buildMethod.getReturnType(); 121 ProcessorErrors.checkState( 122 buildMethod.getReturnType().getKind().equals(TypeKind.DECLARED) 123 && Processors.hasAnnotation( 124 MoreTypes.asTypeElement(component), ClassNames.DEFINE_COMPONENT), 125 builder, 126 "@DefineComponent.Builder method, %s#%s, must return a @DefineComponent type. Found: %s", 127 builder, 128 buildMethod, 129 component); 130 131 List<ExecutableElement> nonStaticNonBuilderMethods = 132 ElementFilter.methodsIn(builder.getEnclosedElements()).stream() 133 .filter(method -> !method.getModifiers().contains(STATIC)) 134 .filter(method -> !method.equals(buildMethod)) 135 .filter(method -> !TypeName.get(method.getReturnType()).equals(ClassName.get(builder))) 136 .collect(Collectors.toList()); 137 138 ProcessorErrors.checkState( 139 nonStaticNonBuilderMethods.isEmpty(), 140 nonStaticNonBuilderMethods, 141 "@DefineComponent.Builder %s, all non-static methods must return %s or %s. Found: %s", 142 builder, 143 builder, 144 component, 145 nonStaticNonBuilderMethods); 146 147 return new AutoValue_DefineComponentBuilderMetadatas_DefineComponentBuilderMetadata( 148 builder, 149 buildMethod, 150 componentMetadatas.get(MoreTypes.asTypeElement(component))); 151 } 152 153 @AutoValue 154 abstract static class DefineComponentBuilderMetadata { builder()155 abstract TypeElement builder(); 156 buildMethod()157 abstract ExecutableElement buildMethod(); 158 componentMetadata()159 abstract DefineComponentMetadata componentMetadata(); 160 } 161 } 162