All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.autobuilder.core.AutoBuilderProcessor Maven / Gradle / Ivy

package net.autobuilder.core;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import net.autobuilder.AutoBuilder;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;
import static javax.lang.model.util.ElementFilter.typesIn;
import static javax.tools.Diagnostic.Kind.ERROR;

public final class AutoBuilderProcessor extends AbstractProcessor {

  private static final String AV_PREFIX = "AutoValue_";

  private final Set deferredTypeNames = new HashSet<>();
  private final Set done = new HashSet<>();

  @Override
  public Set getSupportedAnnotationTypes() {
    Set strings = new HashSet<>();
    strings.add(AutoBuilder.class.getCanonicalName());
    return strings;
  }

  @Override
  public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
  }

  @Override
  public boolean process(Set annotations, RoundEnvironment env) {
    List deferredTypes = deferredTypeNames.stream()
        .map(name -> processingEnv.getElementUtils().getTypeElement(name))
        .collect(toList());
    if (env.processingOver()) {
      for (TypeElement type : deferredTypes) {
        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
            "Could not find " + avPeer(type) +
                ", maybe auto-value is not configured?", type);
      }
      return false;
    }
    List types = Stream.of(
        deferredTypes,
        typesIn(env.getElementsAnnotatedWith(AutoBuilder.class)))
        .map(Collection::stream)
        .flatMap(Function.identity())
        .collect(toList());
    deferredTypeNames.clear();
    Util util = new Util(processingEnv);

    for (TypeElement sourceClassElement : types) {
      String key = sourceClassElement.getQualifiedName().toString();
      if (done.contains(key)) {
        continue;
      }
      ClassName generatedByAutoValue = avPeer(sourceClassElement);
      TypeElement avType = util.typeElement(generatedByAutoValue);
      if (avType == null) {
        // Auto-value hasn't written its class yet.
        // Remember this, so we can notify the user later on.
        deferredTypeNames.add(sourceClassElement.getQualifiedName().toString());
        continue;
      }
      try {
        Model model = Model.create(util, sourceClassElement, avType);
        TypeSpec typeSpec = Analyser.create(model).analyse();
        write(rawType(model.generatedClass), typeSpec);
        done.add(key);
      } catch (ValidationException e) {
        processingEnv.getMessager().printMessage(e.kind, e.getMessage(), e.about);
      } catch (Exception e) {
        String trace = getStackTraceAsString(e);
        String message = "Unexpected error: " + trace;
        processingEnv.getMessager().printMessage(ERROR, message);
      }
    }
    return false;
  }

  private void write(ClassName generatedType, TypeSpec typeSpec) throws IOException {
    JavaFile javaFile = JavaFile.builder(generatedType.packageName(), typeSpec)
        .skipJavaLangImports(true)
        .build();
    JavaFileObject sourceFile;
    sourceFile = processingEnv.getFiler()
        .createSourceFile(generatedType.toString(),
            javaFile.typeSpec.originatingElements.toArray(new Element[0]));
    try (Writer writer = sourceFile.openWriter()) {
      writer.write(javaFile.toString());
    }
  }

  private static ClassName avPeer(TypeElement typeElement) {
    TypeName type = TypeName.get(typeElement.asType());
    String name = AV_PREFIX + String.join("_", rawType(type).simpleNames());
    return rawType(type).topLevelClassName().peerClass(name);
  }

  static ClassName rawType(TypeName typeName) {
    if (typeName instanceof TypeVariableName) {
      return TypeName.OBJECT;
    }
    if (typeName.getClass().equals(TypeName.class)) {
      return TypeName.OBJECT;
    }
    if (typeName instanceof ParameterizedTypeName) {
      return ((ParameterizedTypeName) typeName).rawType;
    }
    return ((ClassName) typeName);
  }

  private static String getStackTraceAsString(Throwable throwable) {
    StringWriter stringWriter = new StringWriter();
    throwable.printStackTrace(new PrintWriter(stringWriter));
    return stringWriter.toString();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy