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

net.autobuilder.core.Model 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.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import net.autobuilder.AutoBuilder;

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 java.util.Arrays;
import java.util.List;

import static javax.lang.model.element.Modifier.PUBLIC;
import static net.autobuilder.core.AutoBuilderProcessor.rawType;

public final class Model {

  private static final String SUFFIX = "_Builder";

  private final TypeElement sourceElement;

  // The type that auto-value has generated
  final TypeElement avElement;

  // should gen code reuse builder instances?
  final boolean reuse;

  public final List parameters;

  final TypeName generatedClass;

  private Model(
      TypeElement sourceElement,
      TypeName generatedClass,
      TypeElement avElement,
      boolean reuse,
      List parameters) {
    this.reuse = reuse;
    this.generatedClass = generatedClass;
    this.sourceElement = sourceElement;
    this.avElement = avElement;
    this.parameters = parameters;
  }

  static Model create(
      List parameters,
      TypeElement sourceElement,
      TypeElement avElement) {
    ExecutableElement avConstructor = getAvConstructor(sourceElement, avElement);
    if (avConstructor.getModifiers().contains(Modifier.PRIVATE)) {
      boolean suspicious = ElementFilter.typesIn(sourceElement.getEnclosedElements())
          .stream()
          .anyMatch(
              e -> e.getAnnotationMirrors().stream().anyMatch(annotationMirror -> {
                ClassName className = rawType(TypeName.get(annotationMirror.getAnnotationType()));
                return className.packageName().equals("com.google.auto.value") &&
                    className.simpleNames().equals(Arrays.asList("AutoValue", "Builder"));
              }));
      if (suspicious) {
        throw new ValidationException(
            sourceElement + ": @AutoBuilder and @AutoValue.Builder cannot be used together.",
            sourceElement);
      }
      throw new ValidationException(
          "Expecting the generated auto-value class to have a non-private constructor.",
          sourceElement);
    }
    TypeName generatedClass = generatedClass(sourceElement);
    if (!sourceElement.getTypeParameters().isEmpty()) {
      throw new ValidationException("The class may not have type parameters.",
          sourceElement);
    }
    boolean optionalRefTrackingBuilderClass =
        sourceElement.getAnnotation(AutoBuilder.class).reuseBuilder();
    return new Model(sourceElement, generatedClass, avElement,
        optionalRefTrackingBuilderClass, parameters);
  }

  static ExecutableElement getAvConstructor(TypeElement sourceElement, TypeElement avElement) {
    List avConstructors = ElementFilter.constructorsIn(
        avElement.getEnclosedElements());
    if (avConstructors.size() != 1) {
      throw new ValidationException(
          "Expecting the generated auto-value class to have exactly one constructor.",
          sourceElement);
    }
    return avConstructors.get(0);
  }

  static TypeName generatedClass(TypeElement typeElement) {
    String name = String.join("_", ClassName.get(typeElement).simpleNames()) + SUFFIX;
    return ClassName.get(typeElement).topLevelClassName().peerClass(name);
  }

  static TypeName withTypevars(ClassName className, TypeName[] typevars) {
    if (typevars.length == 0) {
      return className;
    }
    return ParameterizedTypeName.get(className, typevars);
  }

  private boolean isPublic() {
    return sourceElement.getModifiers().contains(PUBLIC);
  }

  Modifier[] maybePublic() {
    if (isPublic()) {
      return new Modifier[]{PUBLIC};
    }
    return new Modifier[]{};
  }

  TypeElement sourceElement() {
    return sourceElement;
  }

  ClassName perThreadFactoryClass() {
    return rawType(generatedClass)
        .nestedClass("PerThreadFactory");
  }

  String uniqueFieldName(String baseName) {
    while (isFieldNameCollision(baseName)) {
      baseName = "_" + baseName;
    }
    return baseName;
  }

  String uniqueSetterMethodName(String baseName, TypeMirror paramType) {
    while (isSetterMethodNameCollision(baseName, paramType)) {
      baseName = "_" + baseName;
    }
    return baseName;
  }

  private boolean isFieldNameCollision(
      String fieldName) {
    for (Parameter parameter : parameters) {
      if (parameter.asRegularParameter().asField().name.equals(fieldName)) {
        return true;
      }
    }
    return false;
  }

  boolean isSetterMethodNameCollision(String methodName, TypeMirror paramType) {
    for (Parameter parameter : parameters) {
      if (parameter.asRegularParameter().setterName.equals(methodName)) {
        if (TypeTool.get().isSameErasure(
            paramType,
            parameter.asRegularParameter().variableElement.asType())) {
          return true;
        }
      }
    }
    return false;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy