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

net.autobuilder.core.Collectionish 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.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.WildcardTypeName;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

import static net.autobuilder.core.Collectionish.CollectionType.LIST;
import static net.autobuilder.core.Collectionish.CollectionType.MAP;

final class Collectionish {

  private static final String GCC = "com.google.common.collect";
  private static final ClassName ENTRY_CLASS = ClassName.get(Map.Entry.class);

  enum CollectionType {
    LIST(1, "addTo"), MAP(2, "putIn");
    final int typeParams;
    final String accumulatorPrefix;

    CollectionType(int typeParams, String accumulatorPrefix) {
      this.typeParams = typeParams;
      this.accumulatorPrefix = accumulatorPrefix;
    }
  }

  private static final Map KNOWN = map(
      ofUtil(List.class, "emptyList", ArrayList.class, LIST),
      ofUtil(Set.class, "emptySet", HashSet.class, LIST),
      ofUtil(Map.class, "emptyMap", HashMap.class, MAP),
      ofGuava("ImmutableList", Iterable.class, LIST),
      ofGuava("ImmutableSet", Iterable.class, LIST),
      ofGuava("ImmutableMap", Map.class, MAP));

  private final ClassName className;
  private final ClassName accumulatorAddAllType;

  final Function builderInitBlock;
  final Supplier emptyBlock;
  final CollectionType type;
  final ClassName setterParameterClassName;
  final Function setterAssignment;
  final Function accumulatorType;
  final Function> addAllType;
  final BiFunction addAllBlock;
  final BiFunction buildBlock;

  final boolean wildTyping;

  private Collectionish(
      ClassName className,
      Function builderInitBlock,
      Supplier emptyBlock,
      CollectionType type,
      ClassName setterParameterClassName,
      ClassName builderAddAllType,
      Function setterAssignment,
      Function accumulatorType,
      Function> addAllType,
      BiFunction addAllBlock,
      BiFunction buildBlock,
      boolean wildTyping) {
    this.className = className;
    this.builderInitBlock = builderInitBlock;
    this.emptyBlock = emptyBlock;
    this.type = type;
    this.setterParameterClassName = setterParameterClassName;
    this.accumulatorAddAllType = builderAddAllType;
    this.setterAssignment = setterAssignment;
    this.accumulatorType = accumulatorType;
    this.addAllType = addAllType;
    this.addAllBlock = addAllBlock;
    this.buildBlock = buildBlock;
    this.wildTyping = wildTyping;
  }

  private static Collectionish ofUtil(
      Class className, String emptyMethod, Class builderClass, CollectionType type) {
    ClassName accumulatorAddAllType = type == LIST ? ClassName.get(Collection.class) : ClassName.get(Map.class);
    return new Collectionish(
        ClassName.get(className),
        builderField ->
            CodeBlock.builder().addStatement("this.$N = new $T<>()",
                builderField, builderClass).build(),
        () -> CodeBlock.of("$T.$L()", Collections.class, emptyMethod),
        type,
        ClassName.get(className),
        accumulatorAddAllType,
        parameter -> {
          FieldSpec field = parameter.asField();
          ParameterSpec p = parameter.asParameter();
          return CodeBlock.builder()
              .addStatement("this.$N = $N", field, p)
              .build();
        },
        parameter -> {
          ParameterizedTypeName typeName = (ParameterizedTypeName) parameter.type;
          return ParameterizedTypeName.get(ClassName.get(builderClass),
              typeName.typeArguments.toArray(new TypeName[0]));
        },
        type == LIST ?
            normalAddAllType(type, accumulatorAddAllType) :
            parameter -> {
              TypeName[] typeArguments = Util.typeArgumentSubtypes(parameter.variableElement);
              return Optional.of(
                  ParameterizedTypeName.get(accumulatorAddAllType,
                      typeArguments));
            },
        type == LIST ?
            Collectionish::normalAddAll :
            Collectionish::normalPutAll,
        (builder, field) -> CodeBlock.of("$N.$N", builder, field),
        false);
  }

  private static Collectionish ofGuava(
      String simpleName,
      Class setterParameterClass,
      CollectionType type) {
    ClassName className = ClassName.get(GCC, simpleName);
    ClassName builderAddAllType = ClassName.get(Iterable.class);
    return new Collectionish(
        className,
        builderField ->
            CodeBlock.builder().addStatement("this.$N = $T.builder()",
                builderField, className).build(),
        () -> CodeBlock.of("$T.of()", className),
        type,
        ClassName.get(setterParameterClass),
        builderAddAllType,
        parameter -> {
          FieldSpec field = parameter.asField();
          ParameterSpec p = parameter.asParameter();
          return CodeBlock.builder()
              .addStatement("this.$N = $N != null ? $T.copyOf($N) : null",
                  field, p, className, p)
              .build();
        },
        parameter -> {
          ParameterizedTypeName typeName =
              (ParameterizedTypeName) TypeName.get(parameter.variableElement.asType());
          return ParameterizedTypeName.get(className.nestedClass("Builder"),
              typeName.typeArguments.toArray(new TypeName[typeName.typeArguments.size()]));
        },
        normalAddAllType(type, builderAddAllType),
        type == LIST ?
            Collectionish::normalAddAll :
            Collectionish::normalPutAll,
        (builder, field) -> CodeBlock.of("$N.$N.build()", builder, field),
        true);
  }

  static Collectionish create(TypeName typeName) {
    if (!(typeName instanceof ParameterizedTypeName)) {
      return null;
    }
    ParameterizedTypeName type = (ParameterizedTypeName) typeName;
    Collectionish collectionish = KNOWN.get(type.rawType);
    if (collectionish == null) {
      return null;
    }
    if (collectionish.type.typeParams != type.typeArguments.size()) {
      return null;
    }
    return collectionish;
  }

  private static Map map(Collectionish... collectionishes) {
    Map map = new HashMap<>(collectionishes.length);
    for (Collectionish collectionish : collectionishes) {
      map.put(collectionish.className, collectionish);
    }
    return map;
  }

  Collectionish noBuilder() {
    if (!hasAccumulator()) {
      return this;
    }
    return new Collectionish(className, builderInitBlock, emptyBlock, type,
        setterParameterClassName, null, setterAssignment, accumulatorType, addAllType, addAllBlock, buildBlock,
        wildTyping);
  }

  private static CodeBlock normalAddAll(Parameter parameter, CodeBlock what) {
    FieldSpec builderField = parameter.asBuilderField();
    return CodeBlock.builder()
        .addStatement("this.$N.addAll($L)",
            builderField, what)
        .build();
  }
  private static CodeBlock normalPutAll(Parameter parameter, CodeBlock what) {
    FieldSpec builderField = parameter.asBuilderField();
    return CodeBlock.builder()
        .addStatement("this.$N.putAll($L)",
            builderField, what)
        .build();
  }

  String addMethod() {
    return type == LIST ? "add" : "put";
  }

  boolean hasAccumulator() {
    return accumulatorAddAllType != null;
  }

  private static Function> normalAddAllType(
      CollectionType type,
      ClassName accumulatorAddAllType) {
    return parameter -> {
      ParameterizedTypeName typeName = (ParameterizedTypeName) parameter.type;
      if (type.typeParams == 1 &&
          typeName.rawType.equals(accumulatorAddAllType)) {
        return Optional.empty();
      }
      TypeName[] typeArguments = Util.typeArgumentSubtypes(parameter.variableElement);
      if (type == Collectionish.CollectionType.LIST) {
        return Optional.of(ParameterizedTypeName.get(
            accumulatorAddAllType, typeArguments));
      }
      return Optional.of(
          ParameterizedTypeName.get(accumulatorAddAllType,
              WildcardTypeName.subtypeOf(ParameterizedTypeName.get(
                  ENTRY_CLASS, typeArguments))));
    };
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy