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

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

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

import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import static javax.lang.model.element.Modifier.PRIVATE;
import static net.autobuilder.core.Util.downcase;
import static net.autobuilder.core.Util.isDistinct;
import static net.autobuilder.core.Util.upcase;

final class Parameter {

  private static final Pattern GETTER_PATTERN =
      Pattern.compile("^get[A-Z].*$");
  private static final Pattern IS_PATTERN =
      Pattern.compile("^is[A-Z].*$");

  final VariableElement variableElement;
  final String setterName;
  final String getterName;
  final TypeName type;

  private final Optionalish optionalish;
  private final Collectionish collectionish;

  private final Util util;

  private Parameter(
      Util util,
      VariableElement variableElement,
      String setterName,
      String getterName,
      TypeName type,
      Optionalish optionalish,
      Collectionish collectionish) {
    this.util = util;
    this.variableElement = variableElement;
    this.setterName = setterName;
    this.getterName = getterName;
    this.type = type;
    this.optionalish = optionalish;
    this.collectionish = collectionish;
  }

  static List scan(
      Util util,
      ExecutableElement constructor,
      TypeElement avType) {
    Set methodNames = methodNames(avType);
    List parameters = new ArrayList<>();
    for (VariableElement variableElement : constructor.getParameters()) {
      String name = variableElement.getSimpleName().toString();
      TypeName type = TypeName.get(variableElement.asType());
      String getterName = matchingAccessor(methodNames, variableElement);
      String setterName = setterName(name, type);
      Optionalish optionalish = Optionalish.create(type);
      Collectionish collectionish = Collectionish.create(type);
      parameters.add(new Parameter(util, variableElement, setterName, getterName, type,
          optionalish, collectionish));
    }
    if (!parameters.stream()
        .map(p -> p.fieldNames().stream())
        .flatMap(Function.identity())
        .collect(isDistinct()) ||
        !parameters.stream()
            .map(p -> p.setterNames().stream())
            .flatMap(Function.identity())
            .collect(isDistinct())) {
      parameters = parameters.stream()
          .map(Parameter::noBuilder)
          .collect(toList());
    }
    if (!parameters.stream()
        .map(p -> p.setterNames().stream())
        .flatMap(Function.identity())
        .collect(isDistinct())) {
      parameters = parameters.stream()
          .map(Parameter::originalSetter)
          .collect(toList());
    }
    return parameters;
  }

  static String setterName(String name, TypeName type) {
    if (GETTER_PATTERN.matcher(name).matches()) {
      return downcase(name.substring(3));
    }
    if (type.equals(TypeName.BOOLEAN) &&
        IS_PATTERN.matcher(name).matches()) {
      return downcase(name.substring(2));
    }
    return name;
  }

  private static Set methodNames(TypeElement avType) {
    return ElementFilter.methodsIn(
        avType.getEnclosedElements()).stream()
        .filter(m -> m.getParameters().isEmpty())
        .filter(m -> !m.getReturnType().equals(TypeName.VOID))
        .map(ExecutableElement::getSimpleName)
        .map(CharSequence::toString)
        .collect(Collectors.toSet());
  }

  private static String matchingAccessor(Set methodNames,
                                         VariableElement constructorArgument) {
    String name = constructorArgument.getSimpleName().toString();
    TypeName type = TypeName.get(constructorArgument.asType());
    if (methodNames.contains(name)) {
      return name;
    }
    if (type.equals(TypeName.BOOLEAN)) {
      String getter = "is" + upcase(name);
      if (methodNames.contains(getter)) {
        return getter;
      }
    }
    String getter = "get" + upcase(name);
    if (!methodNames.contains(getter)) {
      throw new ValidationException("no matching accessor: " + name, constructorArgument);
    }
    return getter;
  }

  FieldSpec asField() {
    return FieldSpec.builder(type, setterName).addModifiers(PRIVATE).build();
  }

  FieldSpec asBuilderField() {
    return FieldSpec.builder(builderType(),
        builderFieldName()).addModifiers(PRIVATE).build();
  }

  ParameterizedTypeName builderType() {
    return collectionish.accumulatorType.apply(this);
  }

  ParameterSpec asParameter() {
    TypeName type = this.type;
    if (collectionish != null && collectionish.wildTyping) {
      TypeName[] typeArguments = Util.typeArgumentSubtypes(variableElement);
      type = ParameterizedTypeName.get(collectionish.setterParameterClassName,
          typeArguments);
    }
    return ParameterSpec.builder(type, setterName).build();
  }

  Optional optionalish() {
    return Optional.ofNullable(optionalish);
  }

  Optional collectionish() {
    return Optional.ofNullable(collectionish);
  }

  private List setterNames() {
    if (collectionish == null || !collectionish.hasAccumulator()) {
      return singletonList(setterName);
    }
    return asList(setterName, accumulatorName(collectionish));
  }

  String accumulatorName(Collectionish collectionish) {
    return collectionish.type.accumulatorPrefix + upcase(setterName);
  }

  private List fieldNames() {
    if (collectionish == null || !collectionish.hasAccumulator()) {
      return singletonList(setterName);
    }
    return asList(setterName, builderFieldName());
  }

  private String builderFieldName() {
    return downcase(setterName) + "Builder";
  }

  private Parameter originalSetter() {
    return new Parameter(util, variableElement, variableElement.getSimpleName().toString(),
        getterName, type,
        optionalish, collectionish);
  }

  private Parameter noBuilder() {
    if (collectionish == null || !collectionish.hasAccumulator()) {
      return this;
    }
    return new Parameter(util, variableElement, setterName, getterName, type,
        optionalish, collectionish.noBuilder());
  }

  CodeBlock setterAssignment() {
    if (collectionish == null || collectionish.setterAssignment == null) {
      FieldSpec field = asField();
      ParameterSpec p = asParameter();
      return CodeBlock.builder()
          .addStatement("this.$N = $N", field, p).build();
    }
    return collectionish.setterAssignment.apply(this);
  }

  Optional addAllType() {
    if (collectionish == null ||
        !collectionish.hasAccumulator()) {
      return Optional.empty();
    }
    return collectionish.addAllType.apply(this);
  }

  CodeBlock addAllBlock(CodeBlock what) {
    return collectionish.addAllBlock.apply(this, what);
  }

  CodeBlock getFieldValueBlock(ParameterSpec builder) {
    FieldSpec field = asField();
    if (collectionish != null) {
      CodeBlock getCollection = CodeBlock.builder()
          .add("$N.$N != null ? $N.$N : ",
              builder, field, builder, field)
          .add(collectionish.emptyBlock.get())
          .build();
      if (collectionish.hasAccumulator()) {
        FieldSpec builderField = asBuilderField();
        return CodeBlock.builder().add("$N.$N != null ? ", builder, builderField)
            .add(collectionish.buildBlock.apply(builder, builderField))
            .add(" :\n        ")
            .add(getCollection)
            .build();
      } else {
        return getCollection;
      }
    }
    if (optionalish != null) {
      return CodeBlock.of("$N.$N != null ? $N.$N : $T.empty()",
          builder, field,
          builder, field,
          optionalish.wrapper);
    }
    return CodeBlock.of("$N.$N", builder, field);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy