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 androidx.room.compiler.processing.XElementKt.isTypeElement; 20 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; 21 import static dagger.internal.codegen.xprocessing.XElements.asTypeElement; 22 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; 23 24 import androidx.room.compiler.processing.XElement; 25 import androidx.room.compiler.processing.XFieldElement; 26 import androidx.room.compiler.processing.XMethodElement; 27 import androidx.room.compiler.processing.XType; 28 import androidx.room.compiler.processing.XTypeElement; 29 import com.google.auto.value.AutoValue; 30 import com.google.common.collect.ImmutableList; 31 import dagger.hilt.processor.internal.ClassNames; 32 import dagger.hilt.processor.internal.ProcessorErrors; 33 import dagger.hilt.processor.internal.definecomponent.DefineComponentMetadatas.DefineComponentMetadata; 34 import dagger.internal.codegen.xprocessing.XAnnotations; 35 import dagger.internal.codegen.xprocessing.XElements; 36 import dagger.internal.codegen.xprocessing.XTypes; 37 import java.util.HashMap; 38 import java.util.Map; 39 40 /** Metadata for types annotated with {@link dagger.hilt.DefineComponent.Builder}. */ 41 final class DefineComponentBuilderMetadatas { create(DefineComponentMetadatas componentMetadatas)42 static DefineComponentBuilderMetadatas create(DefineComponentMetadatas componentMetadatas) { 43 return new DefineComponentBuilderMetadatas(componentMetadatas); 44 } 45 46 private final Map<XElement, DefineComponentBuilderMetadata> builderMetadatas = new HashMap<>(); 47 private final DefineComponentMetadatas componentMetadatas; 48 DefineComponentBuilderMetadatas(DefineComponentMetadatas componentMetadatas)49 private DefineComponentBuilderMetadatas(DefineComponentMetadatas componentMetadatas) { 50 this.componentMetadatas = componentMetadatas; 51 } 52 get(XElement element)53 DefineComponentBuilderMetadata get(XElement element) { 54 if (!builderMetadatas.containsKey(element)) { 55 builderMetadatas.put(element, getUncached(element)); 56 } 57 return builderMetadatas.get(element); 58 } 59 getUncached(XElement element)60 private DefineComponentBuilderMetadata getUncached(XElement element) { 61 ProcessorErrors.checkState( 62 element.hasAnnotation(ClassNames.DEFINE_COMPONENT_BUILDER), 63 element, 64 "%s, expected to be annotated with @DefineComponent.Builder. Found: %s", 65 XElements.toStableString(element), 66 element.getAllAnnotations().stream() 67 .map(XAnnotations::toStableString) 68 .collect(toImmutableList())); 69 70 // TODO(bcorso): Allow abstract classes? 71 ProcessorErrors.checkState( 72 isTypeElement(element) && asTypeElement(element).isInterface(), 73 element, 74 "@DefineComponent.Builder is only allowed on interfaces. Found: %s", 75 XElements.toStableString(element)); 76 XTypeElement builder = asTypeElement(element); 77 78 // TODO(bcorso): Allow extending interfaces? 79 ProcessorErrors.checkState( 80 builder.getSuperInterfaces().isEmpty(), 81 builder, 82 "@DefineComponent.Builder %s, cannot extend a super class or interface. Found: %s", 83 XElements.toStableString(builder), 84 builder.getSuperInterfaces().stream() 85 .map(XTypes::toStableString) 86 .collect(toImmutableList())); 87 88 // TODO(bcorso): Allow type parameters? 89 ProcessorErrors.checkState( 90 builder.getTypeParameters().isEmpty(), 91 builder, 92 "@DefineComponent.Builder %s, cannot have type parameters.", 93 XTypes.toStableString(builder.getType())); 94 95 ImmutableList<XFieldElement> nonStaticFields = 96 builder.getDeclaredFields().stream() 97 .filter(field -> !field.isStatic()) 98 .collect(toImmutableList()); 99 100 ProcessorErrors.checkState( 101 nonStaticFields.isEmpty(), 102 builder, 103 "@DefineComponent.Builder %s, cannot have non-static fields. Found: %s", 104 XElements.toStableString(builder), 105 nonStaticFields.stream() 106 .map(XElements::toStableString) 107 .collect(toImmutableList())); 108 109 ImmutableList<XMethodElement> buildMethods = 110 builder.getDeclaredMethods().stream() 111 .filter(method -> !method.isStatic()) 112 .filter(method -> method.getParameters().isEmpty()) 113 .collect(toImmutableList()); 114 115 ProcessorErrors.checkState( 116 buildMethods.size() == 1, 117 builder, 118 "@DefineComponent.Builder %s, must have exactly 1 build method that takes no parameters. " 119 + "Found: %s", 120 XElements.toStableString(builder), 121 buildMethods.stream() 122 .map(XElements::toStableString) 123 .collect(toImmutableList())); 124 125 XMethodElement buildMethod = buildMethods.get(0); 126 XType componentType = buildMethod.getReturnType(); 127 ProcessorErrors.checkState( 128 isDeclared(componentType) 129 && componentType.getTypeElement().hasAnnotation(ClassNames.DEFINE_COMPONENT), 130 builder, 131 "@DefineComponent.Builder method, %s#%s, must return a @DefineComponent type. Found: %s", 132 XElements.toStableString(builder), 133 XElements.toStableString(buildMethod), 134 XTypes.toStableString(componentType)); 135 136 ImmutableList<XMethodElement> nonStaticNonBuilderMethods = 137 builder.getDeclaredMethods().stream() 138 .filter(method -> !method.isStatic()) 139 .filter(method -> !method.equals(buildMethod)) 140 .filter(method -> !method.getReturnType().getTypeName().equals(builder.getClassName())) 141 .collect(toImmutableList()); 142 143 ProcessorErrors.checkStateX( 144 nonStaticNonBuilderMethods.isEmpty(), 145 nonStaticNonBuilderMethods, 146 "@DefineComponent.Builder %s, all non-static methods must return %s or %s. Found: %s", 147 XElements.toStableString(builder), 148 XElements.toStableString(builder), 149 XTypes.toStableString(componentType), 150 nonStaticNonBuilderMethods.stream() 151 .map(XElements::toStableString) 152 .collect(toImmutableList())); 153 154 return new AutoValue_DefineComponentBuilderMetadatas_DefineComponentBuilderMetadata( 155 builder, 156 buildMethod, 157 componentMetadatas.get(componentType.getTypeElement())); 158 } 159 160 @AutoValue 161 abstract static class DefineComponentBuilderMetadata { builder()162 abstract XTypeElement builder(); 163 buildMethod()164 abstract XMethodElement buildMethod(); 165 componentMetadata()166 abstract DefineComponentMetadata componentMetadata(); 167 } 168 } 169