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