Java tutorial
/* * Copyright (C) 2014 Google, Inc. * * 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; import com.google.common.base.Optional; import com.google.common.base.Throwables; import com.google.common.collect.Iterables; import com.google.common.io.CharSink; import com.google.common.io.CharSource; import com.google.googlejavaformat.java.Formatter; import com.google.googlejavaformat.java.FormatterException; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.TypeSpec; import java.io.IOException; import java.io.Writer; import javax.annotation.Generated; import javax.annotation.processing.Filer; import javax.lang.model.element.Element; import javax.lang.model.util.Elements; import javax.tools.JavaFileObject; import static com.google.common.base.Preconditions.checkNotNull; /** * 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 <T> The input type from which source is to be generated. */ abstract class JavaPoetSourceFileGenerator<T> { private static final String GENERATED_COMMENTS = "https://google.github.io/dagger"; private static final AnnotationSpec GENERATED = AnnotationSpec.builder(Generated.class) .addMember("value", "$S", ComponentProcessor.class.getName()) .addMember("comments", "$S", GENERATED_COMMENTS).build(); private final Filer filer; private final boolean generatedAnnotationAvailable; JavaPoetSourceFileGenerator(Filer filer, Elements elements) { this.filer = checkNotNull(filer); generatedAnnotationAvailable = elements.getTypeElement("javax.annotation.Generated") != null; } /** Generates a source file to be compiled for {@code T}. */ void generate(T input) throws SourceFileGenerationException { ClassName generatedTypeName = nameGeneratedType(input); try { Optional<TypeSpec.Builder> type = write(generatedTypeName, input); if (!type.isPresent()) { return; } JavaFile javaFile = buildJavaFile(generatedTypeName, type.get()); final JavaFileObject sourceFile = filer.createSourceFile(generatedTypeName.toString(), Iterables.toArray(javaFile.typeSpec.originatingElements, Element.class)); try { new Formatter().formatSource(CharSource.wrap(javaFile.toString()), new CharSink() { @Override public Writer openStream() throws IOException { return sourceFile.openWriter(); } }); } catch (FormatterException e) { throw new SourceFileGenerationException(Optional.of(generatedTypeName), e, getElementForErrorReporting(input)); } } 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.<ClassName>absent(), e, getElementForErrorReporting(input)); } } private JavaFile buildJavaFile(ClassName generatedTypeName, TypeSpec.Builder typeSpecBuilder) { if (generatedAnnotationAvailable) { typeSpecBuilder.addAnnotation(GENERATED); } JavaFile.Builder javaFileBuilder = JavaFile .builder(generatedTypeName.packageName(), typeSpecBuilder.build()).skipJavaLangImports(true); if (!generatedAnnotationAvailable) { javaFileBuilder.addFileComment("Generated by %s (%s).", ComponentProcessor.class.getName(), GENERATED_COMMENTS); } return javaFileBuilder.build(); } /** * Implementations should return the {@link ClassName} for the top-level type to be generated. */ abstract ClassName nameGeneratedType(T input); /** * Returns an optional element to be used for reporting errors. This returns a single element * rather than a collection to reduce output noise. */ abstract Optional<? extends Element> getElementForErrorReporting(T input); /** * Returns a {@link TypeSpec.Builder type} to be generated for {@code T}, or {@link * Optional#absent()} if no file should be generated. */ // TODO(ronshapiro): write() makes more sense in JavaWriter where all writers are mutable. // consider renaming to something like typeBuilder() which conveys the mutability of the result abstract Optional<TypeSpec.Builder> write(ClassName generatedTypeName, T input); }