net.autobuilder.core.Collectionish Maven / Gradle / Ivy
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