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

com.ryanharter.auto.value.moshi.AutoValueMoshiAdapterFactoryProcessor Maven / Gradle / Ivy

There is a newer version: 0.4.7
Show newest version
package com.ryanharter.auto.value.moshi;

import com.google.auto.service.AutoService;
import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.AutoValueExtension;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic;

import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PUBLIC;

/**
 * Created by rharter on 4/27/16.
 */
@AutoService(Processor.class)
public class AutoValueMoshiAdapterFactoryProcessor extends AbstractProcessor {

  private final AutoValueMoshiExtension extension = new AutoValueMoshiExtension();

  @Override
  public Set getSupportedAnnotationTypes() {
    return ImmutableSet.of(AutoValue.class.getName());
  }

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

  @Override
  public boolean process(Set annotations, RoundEnvironment roundEnv) {
    List elements = new LinkedList();
    for (Element element : roundEnv.getElementsAnnotatedWith(AutoValue.class)) {
      AutoValueExtension.Context context = new LimitedContext(processingEnv, (TypeElement) element);
      if (extension.applicable(context)) {
        elements.add(element);
      }
    }

    if (!elements.isEmpty()) {
      TypeSpec jsonAdapterFactory = createJsonAdapterFactory(elements);
      JavaFile file = JavaFile.builder("com.ryanharter.auto.value.moshi", jsonAdapterFactory).build();
      try {
        file.writeTo(processingEnv.getFiler());
      } catch (IOException e) {
        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to write TypeAdapterFactory: " + e.getLocalizedMessage());
      }
    }

    return false;
  }

  private TypeSpec createJsonAdapterFactory(List elements) {
    TypeSpec.Builder factory = TypeSpec.classBuilder(ClassName.get("com.ryanharter.auto.value.moshi", "AutoValueMoshiAdapterFactory"));
    factory.addModifiers(PUBLIC, FINAL);
    factory.addSuperinterface(TypeName.get(JsonAdapter.Factory.class));

    ParameterSpec type = ParameterSpec.builder(Type.class, "type").build();
    WildcardTypeName extendsAnnotation = WildcardTypeName.subtypeOf(Annotation.class);
    ParameterSpec annotations = ParameterSpec
        .builder(ParameterizedTypeName.get(ClassName.get(Set.class), extendsAnnotation), "annotations")
        .build();
    ParameterSpec moshi = ParameterSpec.builder(Moshi.class, "moshi").build();
    ParameterizedTypeName result = ParameterizedTypeName.get(ClassName.get(JsonAdapter.class),
        WildcardTypeName.subtypeOf(TypeName.OBJECT));
    MethodSpec.Builder create = MethodSpec.methodBuilder("create")
        .addModifiers(PUBLIC)
        .addAnnotation(Override.class)
        .addParameters(ImmutableSet.of(type, annotations, moshi))
        .returns(result);

    for (int i = 0; i < elements.size(); i++) {
      Element element = elements.get(i);
      if (i == 0) {
        create.beginControlFlow("if ($N.equals($T.class))", type, element);
      } else {
        create.nextControlFlow("else if ($N.equals($T.class))", type, element);
      }
      ExecutableElement jsonAdapterMethod = getJsonAdapterMethod(element);
      create.addStatement("return $T." + jsonAdapterMethod.getSimpleName() + "($N)", element, moshi);
    }
    create.nextControlFlow("else");
    create.addStatement("return null");
    create.endControlFlow();

    factory.addMethod(create.build());
    return factory.build();
  }

  private ExecutableElement getJsonAdapterMethod(Element element) {
    ParameterizedTypeName jsonAdapterType = ParameterizedTypeName.get(
        ClassName.get(JsonAdapter.class), TypeName.get(element.asType()));
    for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
      if (method.getModifiers().contains(Modifier.STATIC)
          && method.getModifiers().contains(Modifier.PUBLIC)) {
        TypeMirror rType = method.getReturnType();
        TypeName returnType = TypeName.get(rType);
        if (returnType.equals(jsonAdapterType)) {
          return method;
        }
      }
    }
    return null;
  }

  private static class LimitedContext implements AutoValueExtension.Context {

    private final ProcessingEnvironment processingEnvironment;
    private final TypeElement autoValueClass;

    private LimitedContext(ProcessingEnvironment processingEnvironment, TypeElement autoValueClass) {
      this.processingEnvironment = processingEnvironment;
      this.autoValueClass = autoValueClass;
    }

    @Override public ProcessingEnvironment processingEnvironment() {
      return processingEnvironment;
    }

    @Override public String packageName() {
      String fqn = autoValueClass.getQualifiedName().toString();
      return fqn.substring(0, fqn.lastIndexOf('.'));
    }

    @Override public TypeElement autoValueClass() {
      return autoValueClass;
    }

    @Override public Map properties() {
      return null;
    }

    @Override public Set abstractMethods() {
      return null;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy