1 /* 2 * Copyright (C) 2014 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.internal.codegen.base; 18 19 import static androidx.room.compiler.processing.JavaPoetExtKt.addOriginatingElement; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.CAST; 22 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.DEPRECATION; 23 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.KOTLIN_INTERNAL; 24 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES; 25 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED; 26 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNINITIALIZED; 27 import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement; 28 29 import androidx.room.compiler.processing.XElement; 30 import androidx.room.compiler.processing.XFiler; 31 import androidx.room.compiler.processing.XMessager; 32 import androidx.room.compiler.processing.XProcessingEnv; 33 import com.google.common.collect.ImmutableList; 34 import com.google.common.collect.ImmutableSet; 35 import com.squareup.javapoet.AnnotationSpec; 36 import com.squareup.javapoet.JavaFile; 37 import com.squareup.javapoet.TypeSpec; 38 import dagger.internal.DaggerGenerated; 39 import dagger.internal.codegen.javapoet.AnnotationSpecs; 40 import dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression; 41 import java.util.Optional; 42 43 /** 44 * A template class that provides a framework for properly handling IO while generating source files 45 * from an annotation processor. Particularly, it makes a best effort to ensure that files that fail 46 * to write successfully are deleted. 47 * 48 * @param <T> The input type from which source is to be generated. 49 */ 50 public abstract class SourceFileGenerator<T> { 51 private static final String GENERATED_COMMENTS = "https://dagger.dev"; 52 53 private final XFiler filer; 54 private final XProcessingEnv processingEnv; 55 SourceFileGenerator(XFiler filer, XProcessingEnv processingEnv)56 public SourceFileGenerator(XFiler filer, XProcessingEnv processingEnv) { 57 this.filer = checkNotNull(filer); 58 this.processingEnv = checkNotNull(processingEnv); 59 } 60 SourceFileGenerator(SourceFileGenerator<T> delegate)61 public SourceFileGenerator(SourceFileGenerator<T> delegate) { 62 this(delegate.filer, delegate.processingEnv); 63 } 64 65 /** Generates a source file to be compiled for {@code T}. */ generate(T input, XMessager messager)66 public void generate(T input, XMessager messager) { 67 generate(input); 68 } 69 70 /** Generates a source file to be compiled for {@code T}. */ generate(T input)71 public void generate(T input) { 72 for (TypeSpec.Builder type : topLevelTypes(input)) { 73 filer.write(buildJavaFile(input, type), XFiler.Mode.Isolating); 74 } 75 } 76 buildJavaFile(T input, TypeSpec.Builder typeSpecBuilder)77 private JavaFile buildJavaFile(T input, TypeSpec.Builder typeSpecBuilder) { 78 XElement originatingElement = originatingElement(input); 79 addOriginatingElement(typeSpecBuilder, originatingElement); 80 typeSpecBuilder.addAnnotation(DaggerGenerated.class); 81 Optional<AnnotationSpec> generatedAnnotation = 82 Optional.ofNullable(processingEnv.findGeneratedAnnotation()) 83 .map( 84 annotation -> 85 AnnotationSpec.builder(annotation.getClassName()) 86 .addMember("value", "$S", "dagger.internal.codegen.ComponentProcessor") 87 .addMember("comments", "$S", GENERATED_COMMENTS) 88 .build()); 89 generatedAnnotation.ifPresent(typeSpecBuilder::addAnnotation); 90 91 // TODO(b/263891456): Remove KOTLIN_INTERNAL and use Object/raw types where necessary. 92 typeSpecBuilder.addAnnotation( 93 AnnotationSpecs.suppressWarnings( 94 ImmutableSet.<Suppression>builder() 95 .addAll(warningSuppressions()) 96 .add(UNCHECKED, RAWTYPES, KOTLIN_INTERNAL, CAST, DEPRECATION, UNINITIALIZED) 97 .build())); 98 99 String packageName = closestEnclosingTypeElement(originatingElement).getPackageName(); 100 JavaFile.Builder javaFileBuilder = 101 JavaFile.builder(packageName, typeSpecBuilder.build()).skipJavaLangImports(true); 102 if (!generatedAnnotation.isPresent()) { 103 javaFileBuilder.addFileComment("Generated by Dagger ($L).", GENERATED_COMMENTS); 104 } 105 return javaFileBuilder.build(); 106 } 107 108 /** Returns the originating element of the generating type. */ originatingElement(T input)109 public abstract XElement originatingElement(T input); 110 111 /** 112 * Returns {@link TypeSpec.Builder types} be generated for {@code T}, or an empty list if no types 113 * should be generated. 114 * 115 * <p>Every type will be generated in its own file. 116 */ topLevelTypes(T input)117 public abstract ImmutableList<TypeSpec.Builder> topLevelTypes(T input); 118 119 /** Returns {@link Suppression}s that are applied to files generated by this generator. */ 120 // TODO(b/134590785): When suppressions are removed locally, remove this and inline the usages warningSuppressions()121 protected ImmutableSet<Suppression> warningSuppressions() { 122 return ImmutableSet.of(); 123 } 124 } 125