dagger.hilt.processor.internal.root.ComponentTreeDepsGenerator Maven / Gradle / Ivy
Show all versions of hilt-compiler Show documentation
/*
* 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.root;
import static javax.lang.model.element.Modifier.PUBLIC;
import androidx.room.compiler.processing.XFiler.Mode;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XTypeElement;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import dagger.hilt.processor.internal.AggregatedElements;
import dagger.hilt.processor.internal.ClassNames;
import dagger.hilt.processor.internal.Processors;
import java.io.IOException;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
/** Generates an {@link dagger.hilt.internal.componenttreedeps.ComponentTreeDeps}. */
final class ComponentTreeDepsGenerator {
// Keeps track of already generated proxies. For correctness, this same instance of
// ComponentTreeDepsGenerator must be used for a given round.
private final Set generatedProxies = new HashSet<>();
private final XProcessingEnv env;
private final Mode mode;
ComponentTreeDepsGenerator(XProcessingEnv env, Mode mode) {
this.env = env;
this.mode = mode;
}
void generate(ComponentTreeDepsMetadata metadata) throws IOException {
ClassName name = metadata.name();
TypeSpec.Builder builder =
TypeSpec.classBuilder(name)
// No originating element since this is generated by the aggregating processor.
.addAnnotation(componentTreeDepsAnnotation(metadata));
Processors.addGeneratedAnnotation(builder, env, ClassNames.ROOT_PROCESSOR.toString());
env.getFiler().write(JavaFile.builder(name.packageName(), builder.build()).build(), mode);
}
AnnotationSpec componentTreeDepsAnnotation(ComponentTreeDepsMetadata metadata)
throws IOException {
AnnotationSpec.Builder builder = AnnotationSpec.builder(ClassNames.COMPONENT_TREE_DEPS);
addDeps(builder, metadata.aggregatedRootDeps(), "rootDeps");
addDeps(builder, metadata.defineComponentDeps(), "defineComponentDeps");
addDeps(builder, metadata.aliasOfDeps(), "aliasOfDeps");
addDeps(builder, metadata.aggregatedDeps(), "aggregatedDeps");
addDeps(builder, metadata.aggregatedUninstallModulesDeps(), "uninstallModulesDeps");
addDeps(builder, metadata.aggregatedEarlyEntryPointDeps(), "earlyEntryPointDeps");
return builder.build();
}
private void addDeps(AnnotationSpec.Builder builder, ImmutableSet deps, String name)
throws IOException {
for (XTypeElement dep : deps) {
builder.addMember(name, "$T.class", maybeWrapInPublicProxy(dep));
}
}
/**
* This method will return the public proxy for {@code dep} if it is not public, otherwise it will
* return {@code dep} itself. It will also generate the proxy if it doesn't already exist.
*
* Note: These proxies are only used for serialization. The proxy will be unwrapped when
* converting to {@link ComponentTreeDepsMetadata}.
*
*
Note: The public proxy is needed because Hilt versions < 2.35 generated package-private
* aggregating elements, which can't be referenced directly in the {@code @ComponentTreeDeps}.
*/
private ClassName maybeWrapInPublicProxy(XTypeElement dep) {
Optional proxyName = AggregatedElements.aggregatedElementProxyName(dep);
if (proxyName.isPresent()) {
// Check the set of already generated proxies to ensure we don't regenerate the proxy in
// this round. Also check that the element doesn't already exist to ensure we don't regenerate
// a proxy generated in a previous round.
if (generatedProxies.add(proxyName.get())
&& env.findTypeElement(proxyName.get().canonicalName()) == null) {
generateProxy(dep, proxyName.get());
}
return proxyName.get();
}
return dep.getClassName();
}
private void generateProxy(XTypeElement dep, ClassName proxyName) {
TypeSpec.Builder builder =
TypeSpec.classBuilder(proxyName)
.addModifiers(PUBLIC)
// No originating element since this is generated by the aggregating processor.
.addAnnotation(
AnnotationSpec.builder(ClassNames.AGGREGATED_ELEMENT_PROXY)
.addMember("value", "$T.class", dep.getClassName())
.build());
Processors.addGeneratedAnnotation(builder, env, ClassNames.ROOT_PROCESSOR.toString());
env.getFiler().write(JavaFile.builder(proxyName.packageName(), builder.build()).build(), mode);
}
}