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