dagger.hilt.processor.internal.aggregateddeps.AggregatedDepsMetadata Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hilt-compiler Show documentation
Show all versions of hilt-compiler Show documentation
A fast dependency injector for Android and Java.
/*
* Copyright (C) 2021 The Dagger Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dagger.hilt.processor.internal.aggregateddeps;
import static androidx.room.compiler.processing.compat.XConverters.getProcessingEnv;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import androidx.room.compiler.processing.XAnnotation;
import androidx.room.compiler.processing.XAnnotationValue;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XTypeElement;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.ClassName;
import dagger.hilt.processor.internal.AggregatedElements;
import dagger.hilt.processor.internal.ClassNames;
import dagger.hilt.processor.internal.root.ir.AggregatedDepsIr;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* A class that represents the values stored in an {@link
* dagger.hilt.processor.internal.aggregateddeps.AggregatedDeps} annotation.
*/
@AutoValue
public abstract class AggregatedDepsMetadata {
private static final String AGGREGATED_DEPS_PACKAGE = "hilt_aggregated_deps";
enum DependencyType {
MODULE,
ENTRY_POINT,
COMPONENT_ENTRY_POINT
}
/** Returns the aggregating element */
public abstract XTypeElement aggregatingElement();
public abstract Optional testElement();
public abstract ImmutableSet componentElements();
abstract DependencyType dependencyType();
public abstract XTypeElement dependency();
public abstract ImmutableSet replacedDependencies();
public boolean isModule() {
return dependencyType() == DependencyType.MODULE;
}
/** Returns metadata for all aggregated elements in the aggregating package. */
public static ImmutableSet from(XProcessingEnv env) {
return from(AggregatedElements.from(AGGREGATED_DEPS_PACKAGE, ClassNames.AGGREGATED_DEPS, env));
}
/** Returns metadata for each aggregated element. */
public static ImmutableSet from(
ImmutableSet aggregatedElements) {
return aggregatedElements.stream()
.map(aggregatedElement -> create(aggregatedElement, getProcessingEnv(aggregatedElement)))
.collect(toImmutableSet());
}
public static AggregatedDepsIr toIr(AggregatedDepsMetadata metadata) {
return new AggregatedDepsIr(
metadata.aggregatingElement().getClassName(),
metadata.componentElements().stream()
.map(XTypeElement::getClassName)
.map(ClassName::canonicalName)
.collect(Collectors.toList()),
metadata
.testElement()
.map(XTypeElement::getClassName)
.map(ClassName::canonicalName)
.orElse(null),
metadata.replacedDependencies().stream()
.map(XTypeElement::getClassName)
.map(ClassName::canonicalName)
.collect(Collectors.toList()),
metadata.dependencyType() == DependencyType.MODULE
? metadata.dependency().getClassName().canonicalName()
: null,
metadata.dependencyType() == DependencyType.ENTRY_POINT
? metadata.dependency().getClassName().canonicalName()
: null,
metadata.dependencyType() == DependencyType.COMPONENT_ENTRY_POINT
? metadata.dependency().getClassName().canonicalName()
: null);
}
private static AggregatedDepsMetadata create(XTypeElement element, XProcessingEnv env) {
XAnnotation annotation = element.getAnnotation(ClassNames.AGGREGATED_DEPS);
return new AutoValue_AggregatedDepsMetadata(
element,
getTestElement(annotation.getAnnotationValue("test"), env),
getComponents(annotation.getAnnotationValue("components"), env),
getDependencyType(
annotation.getAnnotationValue("modules"),
annotation.getAnnotationValue("entryPoints"),
annotation.getAnnotationValue("componentEntryPoints")),
getDependency(
annotation.getAnnotationValue("modules"),
annotation.getAnnotationValue("entryPoints"),
annotation.getAnnotationValue("componentEntryPoints"),
env),
getReplacedDependencies(annotation.getAnnotationValue("replaces"), env));
}
private static Optional getTestElement(
XAnnotationValue testValue, XProcessingEnv env) {
checkNotNull(testValue);
String test = testValue.asString();
return test.isEmpty() ? Optional.empty() : Optional.of(env.findTypeElement(test));
}
private static ImmutableSet getComponents(
XAnnotationValue componentsValue, XProcessingEnv env) {
checkNotNull(componentsValue);
ImmutableSet componentNames =
componentsValue.asStringList().stream()
.map(
// This is a temporary hack to map the old ApplicationComponent to the new
// SingletonComponent. Technically, this is only needed for backwards compatibility
// with libraries using the old processor since new processors should convert to the
// new SingletonComponent when generating the metadata class.
componentName ->
componentName.contentEquals(
"dagger.hilt.android.components.ApplicationComponent")
? ClassNames.SINGLETON_COMPONENT.canonicalName()
: componentName)
.map(env::requireTypeElement)
.collect(toImmutableSet());
checkState(!componentNames.isEmpty());
return componentNames;
}
private static DependencyType getDependencyType(
XAnnotationValue modulesValue,
XAnnotationValue entryPointsValue,
XAnnotationValue componentEntryPointsValue) {
checkNotNull(modulesValue);
checkNotNull(entryPointsValue);
checkNotNull(componentEntryPointsValue);
ImmutableSet.Builder dependencyTypes = ImmutableSet.builder();
if (!modulesValue.asAnnotationValueList().isEmpty()) {
dependencyTypes.add(DependencyType.MODULE);
}
if (!entryPointsValue.asAnnotationValueList().isEmpty()) {
dependencyTypes.add(DependencyType.ENTRY_POINT);
}
if (!componentEntryPointsValue.asAnnotationValueList().isEmpty()) {
dependencyTypes.add(DependencyType.COMPONENT_ENTRY_POINT);
}
return getOnlyElement(dependencyTypes.build());
}
private static XTypeElement getDependency(
XAnnotationValue modulesValue,
XAnnotationValue entryPointsValue,
XAnnotationValue componentEntryPointsValue,
XProcessingEnv env) {
checkNotNull(modulesValue);
checkNotNull(entryPointsValue);
checkNotNull(componentEntryPointsValue);
String dependencyName =
getOnlyElement(
ImmutableSet.builder()
.addAll(modulesValue.asAnnotationValueList())
.addAll(entryPointsValue.asAnnotationValueList())
.addAll(componentEntryPointsValue.asAnnotationValueList())
.build())
.asString();
XTypeElement dependency = env.findTypeElement(dependencyName);
checkNotNull(dependency, "Could not get element for %s", dependencyName);
return dependency;
}
private static ImmutableSet getReplacedDependencies(
XAnnotationValue replacedDependenciesValue, XProcessingEnv env) {
// Allow null values to support libraries using a Hilt version before @TestInstallIn was added
return replacedDependenciesValue == null
? ImmutableSet.of()
: replacedDependenciesValue.asStringList().stream()
.map(env::requireTypeElement)
.map(replacedDep -> getPublicDependency(replacedDep, env))
.collect(toImmutableSet());
}
/** Returns the public Hilt wrapper module, or the module itself if its already public. */
private static XTypeElement getPublicDependency(XTypeElement dependency, XProcessingEnv env) {
return PkgPrivateMetadata.of(dependency, ClassNames.MODULE)
.map(metadata -> env.requireTypeElement(metadata.generatedClassName().toString()))
.orElse(dependency);
}
}