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

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

There is a newer version: 2.9.3
Show newest version
package net.autobuilder.core;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
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 java.util.List;
import java.util.stream.Collectors;

import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.STATIC;
import static net.autobuilder.core.AutoBuilderProcessor.rawType;
import static net.autobuilder.core.Util.joinCodeBlocks;

/**
 * Generates the *_Builder class.
 */
final class Builder {

  private final Model model;

  private final MethodSpec initMethod;

  private final FieldSpec inUse;

  private Builder(Model model) {
    this.model = model;
    this.initMethod = initMethod(model, model.parameters);
    String inUseFieldName = model.uniqueFieldName("inUse");
    this.inUse = FieldSpec.builder(TypeName.BOOLEAN, inUseFieldName)
        .addModifiers(PRIVATE).build();
  }

  static Builder create(Model model) {
    return new Builder(model);
  }

  TypeSpec define() {
    TypeSpec.Builder spec = TypeSpec.classBuilder(rawType(model.generatedClass));
    spec.addMethod(initMethod);
    spec.addMethod(buildMethod(model, inUse, model.parameters));
    MethodSpec toBuilderMethod;
    if (model.reuse) {
      FieldSpec factoryField = createFactoryField();
      spec.addField(factoryField);
      spec.addField(inUse);
      spec.addType(PerThreadFactory.create(
          model, initMethod, inUse).define());
      spec.addMethod(staticBuilderMethodReuse(factoryField));
      toBuilderMethod = staticToBuilderMethodReuse(factoryField, model.uniqueSetterMethodName("toBuilder", model.sourceElement().asType()));
    } else {
      spec.addMethod(staticBuilderMethod());
      toBuilderMethod = staticToBuilderMethod(model.uniqueSetterMethodName("toBuilder", model.sourceElement().asType()));
    }
    spec.addMethod(toBuilderMethod);
    spec.addMethod(toBuilderAlias(toBuilderMethod.name));
    for (Parameter parameter : model.parameters) {
      spec.addField(parameter.asRegularParameter().asField());
      spec.addMethod(setterMethod(parameter));
      parameter.extraField().ifPresent(spec::addField);
      parameter.getExtraMethods(model).forEach(spec::addMethod);
    }
    return spec.addModifiers(FINAL)
        .addModifiers(model.maybePublic())
        .addMethod(MethodSpec.constructorBuilder().addModifiers(PRIVATE).build())
        .addJavadoc(generatedInfo())
        .build();
  }

  private FieldSpec createFactoryField() {
    ClassName perThreadFactoryClass = model.perThreadFactoryClass();
    ParameterizedTypeName factoryFieldType = ParameterizedTypeName.get(ClassName.get(ThreadLocal.class), perThreadFactoryClass);
    String factoryFieldName = model.uniqueFieldName("FACTORY");
    return FieldSpec.builder(factoryFieldType, factoryFieldName)
        .addModifiers(PRIVATE, STATIC, FINAL)
        .initializer("$T.withInitial($T::new)", ThreadLocal.class, perThreadFactoryClass).build();
  }


  private static MethodSpec initMethod(
      Model model, List parameters) {
    ParameterSpec input = ParameterSpec.builder(TypeName.get(model.sourceElement().asType()), "input").build();
    CodeBlock.Builder block = CodeBlock.builder();
    for (Parameter parameter : parameters) {
      block.addStatement("$N = $N.$L()",
          parameter.asRegularParameter().setterName, input,
          parameter.asRegularParameter().getterName);
    }
    return MethodSpec.methodBuilder(model.uniqueSetterMethodName("init", model.sourceElement().asType()))
        .addCode(block.build())
        .addParameter(input)
        .addModifiers(PRIVATE)
        .build();
  }

  private MethodSpec setterMethod(Parameter parameter) {
    ParameterSpec p = parameter.asSetterParameter();
    CodeBlock.Builder block = CodeBlock.builder();
    block.add(parameter.codeInsideSetter());
    block.addStatement("return this");
    return MethodSpec.methodBuilder(
        parameter.asRegularParameter().setterName)
        .addCode(block.build())
        .addParameter(p)
        .addModifiers(FINAL)
        .addModifiers(model.maybePublic())
        .returns(model.generatedClass)
        .build();
  }

  private MethodSpec staticBuilderMethod() {
    return MethodSpec.methodBuilder("builder")
        .addModifiers(STATIC)
        .addModifiers(model.maybePublic())
        .addStatement("return new $T()", model.generatedClass)
        .returns(model.generatedClass)
        .build();
  }

  private MethodSpec staticToBuilderMethod(String methodName) {
    ParameterSpec builder = ParameterSpec.builder(model.generatedClass,
        "builder").build();
    ParameterSpec input = ParameterSpec.builder(TypeName.get(model.sourceElement().asType()), "input").build();
    CodeBlock.Builder block = CodeBlock.builder()
        .addStatement("$T $N = new $T()", builder.type, builder, model.generatedClass)
        .addStatement("$N.$N($N)", builder, initMethod, input)
        .addStatement("return $N", builder);
    return MethodSpec.methodBuilder(methodName)
        .addCode(block.build())
        .addParameter(input)
        .addModifiers(STATIC)
        .addModifiers(model.maybePublic())
        .returns(model.generatedClass)
        .build();
  }

  private MethodSpec staticBuilderMethodReuse(FieldSpec factoryField) {
    return MethodSpec.methodBuilder("builder")
        .addModifiers(STATIC)
        .addModifiers(model.maybePublic())
        .addStatement("return $N.get().builder()", factoryField)
        .returns(model.generatedClass)
        .build();
  }

  private MethodSpec staticToBuilderMethodReuse(
      FieldSpec factoryField, String methodName) {
    ParameterSpec param = ParameterSpec.builder(TypeName.get(model.sourceElement().asType()), "input").build();
    return MethodSpec.methodBuilder(methodName)
        .addModifiers(STATIC)
        .addModifiers(model.maybePublic())
        .addStatement("return $N.get().builder($N)", factoryField, param)
        .addParameter(param)
        .returns(model.generatedClass)
        .build();
  }

  private MethodSpec toBuilderAlias(String toBuilderMethodName) {
    ParameterSpec param = ParameterSpec.builder(TypeName.get(model.sourceElement().asType()), "input").build();
    return MethodSpec.methodBuilder(model.uniqueSetterMethodName("builder", model.sourceElement().asType()))
        .addParameter(param)
        .returns(model.generatedClass)
        .addModifiers(STATIC)
        .addModifiers(model.maybePublic())
        .addStatement("return $L($N)", toBuilderMethodName, param)
        .build();
  }

  private static MethodSpec buildMethod(
      Model model,
      FieldSpec inUse,
      List parameters) {
    ParameterSpec result = ParameterSpec.builder(TypeName.get(model.sourceElement().asType()), "result")
        .build();
    List invocation = parameters.stream()
        .map(Parameter::extract)
        .collect(Collectors.toList());
    CodeBlock cleanupAfterBuildCode = cleanupAfterBuildCode(model, parameters);
    MethodSpec.Builder spec = MethodSpec.methodBuilder("build");
    spec.addCode("$T $N = new $T(\n", TypeName.get(model.sourceElement().asType()), result, model.avElement)
        .addCode(invocation.stream().collect(joinCodeBlocks(",\n")))
        .addCode(");\n")
        .addCode(cleanupAfterBuildCode);
    if (model.reuse) {
      spec.addStatement("$N = $L", inUse, false);
    }
    return spec.addStatement("return $N", result)
        .returns(TypeName.get(model.sourceElement().asType()))
        .addModifiers(model.maybePublic())
        .build();
  }

  private static CodeBlock cleanupAfterBuildCode(Model model, List parameters) {
    CodeBlock.Builder builder = CodeBlock.builder();
    if (model.reuse) {
      parameters.stream().map(Parameter::cleanupCode).forEach(builder::add);
    }
    return builder.build();
  }

  private CodeBlock generatedInfo() {
    return CodeBlock.builder().add("Generated by " +
        "\nauto-builder " +
        getClass().getPackage().getImplementationVersion() +
        "\n").build();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy