1 /* 2 * Copyright (C) 2021 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.aggregateddeps; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 import static com.google.common.base.Preconditions.checkState; 21 import static com.google.common.collect.Iterables.getOnlyElement; 22 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 23 24 import com.google.auto.value.AutoValue; 25 import com.google.common.collect.ImmutableMap; 26 import com.google.common.collect.ImmutableSet; 27 import dagger.hilt.processor.internal.AggregatedElements; 28 import dagger.hilt.processor.internal.AnnotationValues; 29 import dagger.hilt.processor.internal.ClassNames; 30 import dagger.hilt.processor.internal.Processors; 31 import java.util.Optional; 32 import javax.lang.model.element.AnnotationMirror; 33 import javax.lang.model.element.AnnotationValue; 34 import javax.lang.model.element.TypeElement; 35 import javax.lang.model.util.Elements; 36 37 /** 38 * A class that represents the values stored in an {@link 39 * dagger.hilt.processor.internal.aggregateddeps.AggregatedDeps} annotation. 40 */ 41 @AutoValue 42 abstract class AggregatedDepsMetadata { 43 private static final String AGGREGATED_DEPS_PACKAGE = "hilt_aggregated_deps"; 44 45 enum DependencyType { 46 MODULE, 47 ENTRY_POINT, 48 COMPONENT_ENTRY_POINT 49 } 50 testElement()51 abstract Optional<TypeElement> testElement(); 52 componentElements()53 abstract ImmutableSet<TypeElement> componentElements(); 54 dependencyType()55 abstract DependencyType dependencyType(); 56 dependency()57 abstract TypeElement dependency(); 58 replacedDependencies()59 abstract ImmutableSet<TypeElement> replacedDependencies(); 60 61 /** Returns all aggregated deps in the aggregating package. */ from(Elements elements)62 public static ImmutableSet<AggregatedDepsMetadata> from(Elements elements) { 63 return AggregatedElements.from(AGGREGATED_DEPS_PACKAGE, ClassNames.AGGREGATED_DEPS, elements) 64 .stream() 65 .map(aggregatedElement -> create(aggregatedElement, elements)) 66 .collect(toImmutableSet()); 67 } 68 create(TypeElement element, Elements elements)69 private static AggregatedDepsMetadata create(TypeElement element, Elements elements) { 70 AnnotationMirror annotationMirror = 71 Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_DEPS); 72 73 ImmutableMap<String, AnnotationValue> values = 74 Processors.getAnnotationValues(elements, annotationMirror); 75 76 return new AutoValue_AggregatedDepsMetadata( 77 getTestElement(values.get("test"), elements), 78 getComponents(values.get("components"), elements), 79 getDependencyType( 80 values.get("modules"), 81 values.get("entryPoints"), 82 values.get("componentEntryPoints")), 83 getDependency( 84 values.get("modules"), 85 values.get("entryPoints"), 86 values.get("componentEntryPoints"), 87 elements), 88 getReplacedDependencies(values.get("replaces"), elements)); 89 } 90 getTestElement( AnnotationValue testValue, Elements elements)91 private static Optional<TypeElement> getTestElement( 92 AnnotationValue testValue, Elements elements) { 93 checkNotNull(testValue); 94 String test = AnnotationValues.getString(testValue); 95 return test.isEmpty() ? Optional.empty() : Optional.of(elements.getTypeElement(test)); 96 } 97 getComponents( AnnotationValue componentsValue, Elements elements)98 private static ImmutableSet<TypeElement> getComponents( 99 AnnotationValue componentsValue, Elements elements) { 100 checkNotNull(componentsValue); 101 ImmutableSet<TypeElement> componentNames = 102 AnnotationValues.getAnnotationValues(componentsValue).stream() 103 .map(AnnotationValues::getString) 104 .map( 105 // This is a temporary hack to map the old ApplicationComponent to the new 106 // SingletonComponent. Technically, this is only needed for backwards compatibility 107 // with libraries using the old processor since new processors should convert to the 108 // new SingletonComponent when generating the metadata class. 109 componentName -> 110 componentName.contentEquals( 111 "dagger.hilt.android.components.ApplicationComponent") 112 ? ClassNames.SINGLETON_COMPONENT.canonicalName() 113 : componentName) 114 .map(elements::getTypeElement) 115 .collect(toImmutableSet()); 116 checkState(!componentNames.isEmpty()); 117 return componentNames; 118 } 119 getDependencyType( AnnotationValue modulesValue, AnnotationValue entryPointsValue, AnnotationValue componentEntryPointsValue)120 private static DependencyType getDependencyType( 121 AnnotationValue modulesValue, 122 AnnotationValue entryPointsValue, 123 AnnotationValue componentEntryPointsValue) { 124 checkNotNull(modulesValue); 125 checkNotNull(entryPointsValue); 126 checkNotNull(componentEntryPointsValue); 127 128 ImmutableSet.Builder<DependencyType> dependencyTypes = ImmutableSet.builder(); 129 if (!AnnotationValues.getAnnotationValues(modulesValue).isEmpty()) { 130 dependencyTypes.add(DependencyType.MODULE); 131 } 132 if (!AnnotationValues.getAnnotationValues(entryPointsValue).isEmpty()) { 133 dependencyTypes.add(DependencyType.ENTRY_POINT); 134 } 135 if (!AnnotationValues.getAnnotationValues(componentEntryPointsValue).isEmpty()) { 136 dependencyTypes.add(DependencyType.COMPONENT_ENTRY_POINT); 137 } 138 return getOnlyElement(dependencyTypes.build()); 139 } 140 getDependency( AnnotationValue modulesValue, AnnotationValue entryPointsValue, AnnotationValue componentEntryPointsValue, Elements elements)141 private static TypeElement getDependency( 142 AnnotationValue modulesValue, 143 AnnotationValue entryPointsValue, 144 AnnotationValue componentEntryPointsValue, 145 Elements elements) { 146 checkNotNull(modulesValue); 147 checkNotNull(entryPointsValue); 148 checkNotNull(componentEntryPointsValue); 149 150 return elements.getTypeElement( 151 AnnotationValues.getString( 152 getOnlyElement( 153 ImmutableSet.<AnnotationValue>builder() 154 .addAll(AnnotationValues.getAnnotationValues(modulesValue)) 155 .addAll(AnnotationValues.getAnnotationValues(entryPointsValue)) 156 .addAll(AnnotationValues.getAnnotationValues(componentEntryPointsValue)) 157 .build()))); 158 } 159 getReplacedDependencies( AnnotationValue replacedDependenciesValue, Elements elements)160 private static ImmutableSet<TypeElement> getReplacedDependencies( 161 AnnotationValue replacedDependenciesValue, Elements elements) { 162 // Allow null values to support libraries using a Hilt version before @TestInstallIn was added 163 return replacedDependenciesValue == null 164 ? ImmutableSet.of() 165 : AnnotationValues.getAnnotationValues(replacedDependenciesValue).stream() 166 .map(AnnotationValues::getString) 167 .map(elements::getTypeElement) 168 .map(replacedDep -> getPublicDependency(replacedDep, elements)) 169 .collect(toImmutableSet()); 170 } 171 172 /** Returns the public Hilt wrapper module, or the module itself if its already public. */ getPublicDependency(TypeElement dependency, Elements elements)173 private static TypeElement getPublicDependency(TypeElement dependency, Elements elements) { 174 return PkgPrivateMetadata.of(elements, dependency, ClassNames.MODULE) 175 .map(metadata -> elements.getTypeElement(metadata.generatedClassName().toString())) 176 .orElse(dependency); 177 } 178 } 179