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 java.util.stream.Collectors.joining; 23 24 import androidx.room.compiler.processing.XAnnotation; 25 import androidx.room.compiler.processing.XAnnotationValue; 26 import androidx.room.compiler.processing.XElement; 27 import androidx.room.compiler.processing.XExecutableElement; 28 import androidx.room.compiler.processing.XTypeElement; 29 import com.google.auto.value.AutoValue; 30 import com.google.common.collect.ImmutableList; 31 import com.squareup.javapoet.ClassName; 32 import dagger.hilt.processor.internal.ClassNames; 33 import dagger.hilt.processor.internal.ProcessorErrors; 34 import dagger.hilt.processor.internal.Processors; 35 import dagger.internal.codegen.xprocessing.XAnnotations; 36 import dagger.internal.codegen.xprocessing.XElements; 37 import dagger.internal.codegen.xprocessing.XTypes; 38 import java.util.HashMap; 39 import java.util.LinkedHashSet; 40 import java.util.Map; 41 import java.util.Optional; 42 43 /** Metadata for types annotated with {@link dagger.hilt.DefineComponent}. */ 44 final class DefineComponentMetadatas { create()45 static DefineComponentMetadatas create() { 46 return new DefineComponentMetadatas(); 47 } 48 49 private final Map<XElement, DefineComponentMetadata> metadatas = new HashMap<>(); 50 DefineComponentMetadatas()51 private DefineComponentMetadatas() {} 52 53 /** Returns the metadata for an element annotated with {@link dagger.hilt.DefineComponent}. */ get(XElement element)54 DefineComponentMetadata get(XElement element) { 55 return get(element, new LinkedHashSet<>()); 56 } 57 get(XElement element, LinkedHashSet<XElement> childPath)58 private DefineComponentMetadata get(XElement element, LinkedHashSet<XElement> childPath) { 59 if (!metadatas.containsKey(element)) { 60 metadatas.put(element, getUncached(element, childPath)); 61 } 62 return metadatas.get(element); 63 } 64 getUncached( XElement element, LinkedHashSet<XElement> childPath)65 private DefineComponentMetadata getUncached( 66 XElement element, LinkedHashSet<XElement> childPath) { 67 ProcessorErrors.checkState( 68 childPath.add(element), 69 element, 70 "@DefineComponent cycle: %s -> %s", 71 childPath.stream().map(XElements::toStableString).collect(joining(" -> ")), 72 XElements.toStableString(element)); 73 74 ProcessorErrors.checkState( 75 element.hasAnnotation(ClassNames.DEFINE_COMPONENT), 76 element, 77 "%s, expected to be annotated with @DefineComponent. Found: %s", 78 XElements.toStableString(element), 79 element.getAllAnnotations().stream() 80 .map(XAnnotations::toStableString) 81 .collect(toImmutableList())); 82 83 // TODO(bcorso): Allow abstract classes? 84 ProcessorErrors.checkState( 85 isTypeElement(element) && asTypeElement(element).isInterface(), 86 element, 87 "@DefineComponent is only allowed on interfaces. Found: %s", 88 XElements.toStableString(element)); 89 XTypeElement component = asTypeElement(element); 90 91 // TODO(bcorso): Allow extending interfaces? 92 ProcessorErrors.checkState( 93 component.getSuperInterfaces().isEmpty(), 94 component, 95 "@DefineComponent %s, cannot extend a super class or interface. Found: %s", 96 XElements.toStableString(component), 97 component.getSuperInterfaces().stream() 98 .map(XTypes::toStableString) 99 .collect(toImmutableList())); 100 101 // TODO(bcorso): Allow type parameters? 102 ProcessorErrors.checkState( 103 component.getTypeParameters().isEmpty(), 104 component, 105 "@DefineComponent %s, cannot have type parameters.", 106 XTypes.toStableString(component.getType())); 107 108 // TODO(bcorso): Allow non-static abstract methods (aka EntryPoints)? 109 ImmutableList<XExecutableElement> nonStaticMethods = 110 component.getDeclaredMethods().stream() 111 .filter(method -> !method.isStatic()) 112 .collect(toImmutableList()); 113 114 ProcessorErrors.checkState( 115 nonStaticMethods.isEmpty(), 116 component, 117 "@DefineComponent %s, cannot have non-static methods. Found: %s", 118 XElements.toStableString(component), 119 nonStaticMethods.stream() 120 .map(XElements::toStableString) 121 .collect(toImmutableList())); 122 123 // No need to check non-static fields since interfaces can't have them. 124 125 ImmutableList<XTypeElement> scopes = 126 Processors.getScopeAnnotations(component).stream() 127 .map(XAnnotation::getTypeElement) 128 .collect(toImmutableList()); 129 130 ImmutableList<XAnnotation> aliasScopes = 131 ImmutableList.copyOf(component.getAnnotationsAnnotatedWith(ClassNames.ALIAS_OF)); 132 ProcessorErrors.checkState( 133 aliasScopes.isEmpty(), 134 component, 135 "@DefineComponent %s, references invalid scope(s) annotated with @AliasOf. " 136 + "@DefineComponent scopes cannot be aliases of other scopes: %s", 137 XElements.toStableString(component), 138 aliasScopes.stream().map(XAnnotations::toStableString).collect(toImmutableList())); 139 140 XAnnotation annotation = component.getAnnotation(ClassNames.DEFINE_COMPONENT); 141 XAnnotationValue parentValue = annotation.getAnnotationValue("parent"); 142 143 ProcessorErrors.checkState( 144 !"<error>".contentEquals(parentValue.getValue().toString()), 145 component, 146 "@DefineComponent %s, references an invalid parent type: %s", 147 XElements.toStableString(component), 148 XAnnotations.toStableString(annotation)); 149 150 XTypeElement parent = parentValue.asType().getTypeElement(); 151 152 ProcessorErrors.checkState( 153 parent.getClassName().equals(ClassNames.DEFINE_COMPONENT_NO_PARENT) 154 || parent.hasAnnotation(ClassNames.DEFINE_COMPONENT), 155 component, 156 "@DefineComponent %s, references a type not annotated with @DefineComponent: %s", 157 XElements.toStableString(component), 158 XElements.toStableString(parent)); 159 160 Optional<DefineComponentMetadata> parentComponent = 161 parent.getClassName().equals(ClassNames.DEFINE_COMPONENT_NO_PARENT) 162 ? Optional.empty() 163 : Optional.of(get(parent, childPath)); 164 165 ClassName componentClassName = component.getClassName(); 166 167 ProcessorErrors.checkState( 168 parentComponent.isPresent() 169 || componentClassName.equals(ClassNames.SINGLETON_COMPONENT), 170 component, 171 "@DefineComponent %s is missing a parent declaration.\n" 172 + "Please declare the parent, for example: @DefineComponent(parent =" 173 + " SingletonComponent.class)", 174 XElements.toStableString(component)); 175 176 ProcessorErrors.checkState( 177 componentClassName.equals(ClassNames.SINGLETON_COMPONENT) 178 || !componentClassName.simpleName().equals(ClassNames.SINGLETON_COMPONENT.simpleName()), 179 component, 180 "Cannot have a component with the same simple name as the reserved %s: %s", 181 ClassNames.SINGLETON_COMPONENT.simpleName(), 182 componentClassName.canonicalName()); 183 184 return new AutoValue_DefineComponentMetadatas_DefineComponentMetadata( 185 component, scopes, parentComponent); 186 } 187 188 @AutoValue 189 abstract static class DefineComponentMetadata { 190 191 /** Returns the component annotated with {@link dagger.hilt.DefineComponent}. */ component()192 abstract XTypeElement component(); 193 194 /** Returns the scopes of the component. */ scopes()195 abstract ImmutableList<XTypeElement> scopes(); 196 197 /** Returns the parent component, if one exists. */ parentMetadata()198 abstract Optional<DefineComponentMetadata> parentMetadata(); 199 isRoot()200 boolean isRoot() { 201 return !parentMetadata().isPresent(); 202 } 203 } 204 } 205