/* * Copyright (C) 2014 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.internal.codegen.base; import static com.google.auto.common.GeneratedAnnotations.generatedAnnotation; import static com.google.common.base.Preconditions.checkNotNull; import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES; import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; 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.internal.DaggerGenerated; import dagger.internal.codegen.javapoet.AnnotationSpecs; import dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression; import dagger.internal.codegen.langmodel.DaggerElements; import java.util.Optional; import javax.annotation.processing.Filer; import javax.annotation.processing.Messager; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; /** * A template class that provides a framework for properly handling IO while generating source files * from an annotation processor. Particularly, it makes a best effort to ensure that files that fail * to write successfully are deleted. * * @param The input type from which source is to be generated. */ public abstract class SourceFileGenerator { private static final String GENERATED_COMMENTS = "https://dagger.dev"; private final Filer filer; private final DaggerElements elements; private final SourceVersion sourceVersion; public SourceFileGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) { this.filer = checkNotNull(filer); this.elements = checkNotNull(elements); this.sourceVersion = checkNotNull(sourceVersion); } public SourceFileGenerator(SourceFileGenerator delegate) { this(delegate.filer, delegate.elements, delegate.sourceVersion); } /** * Generates a source file to be compiled for {@code T}. Writes any generation exception to {@code * messager} and does not throw. */ public void generate(T input, Messager messager) { try { generate(input); } catch (SourceFileGenerationException e) { e.printMessageTo(messager); } } /** Generates a source file to be compiled for {@code T}. */ public void generate(T input) throws SourceFileGenerationException { for (TypeSpec.Builder type : topLevelTypes(input)) { try { buildJavaFile(input, type).writeTo(filer); } catch (Exception e) { // if the code above threw a SFGE, use that Throwables.propagateIfPossible(e, SourceFileGenerationException.class); // otherwise, throw a new one throw new SourceFileGenerationException(Optional.empty(), e, originatingElement(input)); } } } private JavaFile buildJavaFile(T input, TypeSpec.Builder typeSpecBuilder) { typeSpecBuilder.addOriginatingElement(originatingElement(input)); typeSpecBuilder.addAnnotation(DaggerGenerated.class); Optional generatedAnnotation = generatedAnnotation(elements, sourceVersion) .map( annotation -> AnnotationSpec.builder(ClassName.get(annotation)) .addMember("value", "$S", "dagger.internal.codegen.ComponentProcessor") .addMember("comments", "$S", GENERATED_COMMENTS) .build()); generatedAnnotation.ifPresent(typeSpecBuilder::addAnnotation); // TODO(b/134590785): remove this and only suppress annotations locally, if necessary typeSpecBuilder.addAnnotation( AnnotationSpecs.suppressWarnings( ImmutableSet.builder() .addAll(warningSuppressions()) .add(UNCHECKED, RAWTYPES) .build())); JavaFile.Builder javaFileBuilder = JavaFile.builder( elements.getPackageOf(originatingElement(input)).getQualifiedName().toString(), typeSpecBuilder.build()) .skipJavaLangImports(true); if (!generatedAnnotation.isPresent()) { javaFileBuilder.addFileComment("Generated by Dagger ($L).", GENERATED_COMMENTS); } return javaFileBuilder.build(); } /** Returns the originating element of the generating type. */ public abstract Element originatingElement(T input); /** * Returns {@link TypeSpec.Builder types} be generated for {@code T}, or an empty list if no types * should be generated. * *

Every type will be generated in its own file. */ public abstract ImmutableList topLevelTypes(T input); /** Returns {@link Suppression}s that are applied to files generated by this generator. */ // TODO(b/134590785): When suppressions are removed locally, remove this and inline the usages protected ImmutableSet warningSuppressions() { return ImmutableSet.of(); } }