net.zerobuilder.modules.builder.Builder Maven / Gradle / Ivy
The newest version!
package net.zerobuilder.modules.builder;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import net.zerobuilder.compiler.generate.DtoGoalDetails;
import net.zerobuilder.compiler.generate.DtoGoalDetails.AbstractRegularDetails;
import net.zerobuilder.compiler.generate.DtoRegularGoalDescription.SimpleRegularGoalDescription;
import net.zerobuilder.compiler.generate.DtoRegularParameter.SimpleParameter;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.stream.Stream;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static com.squareup.javapoet.TypeName.BOOLEAN;
import static com.squareup.javapoet.TypeName.VOID;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Optional.empty;
import static java.util.stream.Collectors.toList;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
import static net.zerobuilder.compiler.generate.DtoContext.ContextLifecycle.REUSE_INSTANCES;
import static net.zerobuilder.compiler.generate.DtoGoalDetails.regularDetailsCases;
import static net.zerobuilder.compiler.generate.ZeroUtil.downcase;
import static net.zerobuilder.compiler.generate.ZeroUtil.fieldSpec;
import static net.zerobuilder.compiler.generate.ZeroUtil.flatList;
import static net.zerobuilder.compiler.generate.ZeroUtil.joinCodeBlocks;
import static net.zerobuilder.compiler.generate.ZeroUtil.parameterSpec;
import static net.zerobuilder.compiler.generate.ZeroUtil.presentInstances;
import static net.zerobuilder.compiler.generate.ZeroUtil.rawClassName;
import static net.zerobuilder.compiler.generate.ZeroUtil.simpleName;
import static net.zerobuilder.compiler.generate.ZeroUtil.statement;
import static net.zerobuilder.compiler.generate.ZeroUtil.upcase;
import static net.zerobuilder.modules.builder.Generator.instanceField;
import static net.zerobuilder.modules.builder.Step.nullCheck;
final class Builder {
static TypeName nextType(int i, SimpleRegularGoalDescription description) {
if (i < description.parameters.size() - 1) {
return description.context.generatedType
.nestedClass(upcase(description.details.name() + "Builder"))
.nestedClass(upcase(description.parameters.get(i + 1).name));
}
return description.details.type();
}
private static final BiFunction> maybeField =
regularDetailsCases(
(constructor, description) -> empty(),
(staticMethod, description) -> empty(),
(method, description) -> Optional.of(instanceField(description)));
static final Function> fields
= description -> {
List steps = description.parameters;
return Stream.of(
presentInstances(maybeField.apply(description.details, description)),
description.details.lifecycle == REUSE_INSTANCES ?
singletonList(fieldSpec(BOOLEAN, "_currently_in_use", PRIVATE)) :
Collections.emptyList(),
steps.stream()
.limit(steps.size() - 1)
.map(parameter -> fieldSpec(parameter.type, parameter.name, PRIVATE))
.collect(toList()))
.collect(flatList());
};
static IntFunction steps(SimpleRegularGoalDescription description) {
return i -> {
SimpleParameter step = description.parameters.get(i);
TypeName type = step.type;
String name = step.name;
ParameterSpec parameter = parameterSpec(type, name);
List thrownTypes = i < description.parameters.size() - 1 ?
emptyList() :
description.thrownTypes;
TypeName nextType = nextType(i, description);
return methodBuilder(step.name)
.addAnnotation(Override.class)
.addParameter(parameter)
.returns(nextType)
.addCode(nullCheck.apply(step))
.addCode(normalAssignment(i, description))
.addModifiers(PUBLIC)
.addExceptions(thrownTypes)
.build();
};
}
private static CodeBlock normalAssignment(int i, SimpleRegularGoalDescription description) {
SimpleParameter step = description.parameters.get(i);
TypeName type = step.type;
String name = step.name;
ParameterSpec parameter = parameterSpec(type, name);
if (i == description.parameters.size() - 1) {
return regularInvoke.apply(description.details, description);
} else {
return CodeBlock.builder()
.addStatement("this.$N = $N", fieldSpec(step.type, step.name), parameter)
.addStatement("return this")
.build();
}
}
private static final BiFunction regularInvoke =
regularDetailsCases(
(constructor, description) -> constructorCall(description, constructor),
(staticMethod, description) -> staticCall(description, staticMethod),
(instanceMethod, description) -> instanceCall(description, instanceMethod));
private static CodeBlock constructorCall(SimpleRegularGoalDescription description,
DtoGoalDetails.ConstructorGoalDetails details) {
TypeName type = details.type();
ParameterSpec varGoal = parameterSpec(type,
'_' + downcase(simpleName(type)));
CodeBlock.Builder builder = CodeBlock.builder();
if (details.lifecycle == REUSE_INSTANCES) {
builder.addStatement("this._currently_in_use = false");
}
CodeBlock args = description.invocationParameters();
builder.addStatement("$T $N = new $T($L)", varGoal.type, varGoal, type, args);
if (details.lifecycle == REUSE_INSTANCES) {
builder.add(free(description.parameters));
}
return builder.addStatement("return $N", varGoal).build();
}
private static CodeBlock instanceCall(SimpleRegularGoalDescription description,
DtoGoalDetails.InstanceMethodGoalDetails details) {
TypeName type = details.goalType;
String method = details.methodName;
ParameterSpec varGoal = parameterSpec(type,
'_' + downcase(simpleName(type)));
CodeBlock.Builder builder = CodeBlock.builder();
if (details.lifecycle == REUSE_INSTANCES) {
builder.addStatement("this._currently_in_use = false");
}
if (VOID.equals(type)) {
builder.addStatement("this.$N.$N($L)", instanceField(description),
method, description.invocationParameters());
} else {
builder.addStatement("$T $N = this.$N.$N($L)", varGoal.type, varGoal, instanceField(description),
method, description.invocationParameters());
}
if (details.lifecycle == REUSE_INSTANCES) {
builder.addStatement("this.$N = null", instanceField(description));
builder.add(free(description.parameters));
}
if (!VOID.equals(type)) {
builder.addStatement("return $N", varGoal);
}
return builder.build();
}
private static CodeBlock staticCall(SimpleRegularGoalDescription description,
DtoGoalDetails.StaticMethodGoalDetails details) {
TypeName type = details.goalType;
String method = details.methodName;
ParameterSpec varGoal = parameterSpec(type,
'_' + downcase(simpleName(type)));
CodeBlock.Builder builder = CodeBlock.builder();
if (details.lifecycle == REUSE_INSTANCES) {
builder.addStatement("this._currently_in_use = false");
}
if (VOID.equals(type)) {
builder.addStatement("$T.$N($L)", rawClassName(description.context.type),
method, description.invocationParameters());
} else {
builder.addStatement("$T $N = $T.$N($L)", varGoal.type, varGoal,
rawClassName(description.context.type),
method, description.invocationParameters());
}
if (details.lifecycle == REUSE_INSTANCES) {
builder.add(free(description.parameters));
}
if (!VOID.equals(type)) {
builder.addStatement("return $N", varGoal);
}
return builder.build();
}
private static CodeBlock free(List steps) {
return steps.stream()
.limit(steps.size() - 1)
.filter(parameter -> !parameter.type.isPrimitive())
.map(parameter -> statement("this.$N = null", parameter.name))
.collect(joinCodeBlocks);
}
private Builder() {
throw new UnsupportedOperationException("no instances");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy