From 6134f81d6f7046a1f28ba9555af8b1f814bd213c Mon Sep 17 00:00:00 2001 From: JonasG Date: Thu, 18 Apr 2024 16:37:14 +0200 Subject: [PATCH] feat: enforce mandatory fields through Step Builder This should close: #9 --- .../java/io/jonasg/bob/ConstructorPolicy.java | 2 + .../java/io/jonasg/bob/BuildableField.java | 4 +- .../java/io/jonasg/bob/BuilderGenerator.java | 19 ++- .../io/jonasg/bob/BuilderTypeSpecFactory.java | 54 ++++--- .../StepBuilderInterfaceTypeSpecFactory.java | 138 ++++++++++++++++++ .../jonasg/bob/TypeSpecInterfaceBuilder.java | 90 ++++++++++++ .../java/io/jonasg/bob/BobFeaturesTests.java | 90 +++++++++++- ...ldableConstructorsAnnotationsPresent.java} | 10 +- ...ructorPolicyIsEnforcedStepWiseBuilder.java | 56 +++++++ ...ructorPolicyIsEnforcedStepWiseBuilder.java | 26 ++++ ...enConstructorPolicyIsEnforcedStepWise.java | 51 +++++++ ...rWithSingleArgumentConstructorBuilder.java | 56 +++++++ ...rWithSingleArgumentConstructorBuilder.java | 24 +++ ...pBuilderWithSingleArgumentConstructor.java | 51 +++++++ ...hSingleMandatoryAnnotatedFieldBuilder.java | 58 ++++++++ ...hSingleMandatoryAnnotatedFieldBuilder.java | 24 +++ ...lderWithSingleMandatoryAnnotatedField.java | 47 ++++++ 17 files changed, 768 insertions(+), 32 deletions(-) create mode 100644 processor/src/main/java/io/jonasg/bob/StepBuilderInterfaceTypeSpecFactory.java create mode 100644 processor/src/main/java/io/jonasg/bob/TypeSpecInterfaceBuilder.java rename processor/src/test/resources/tests/failing-compilation/{MultipleBuildableConstructorAnnotationsPresent/MultipleBuildableConstructorAnnotationsPresent.java => MultipleBuildableConstructorsAnnotationsPresent/MultipleBuildableConstructorsAnnotationsPresent.java} (54%) create mode 100644 processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/Expected_DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder.java create mode 100644 processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/Expected_GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder.java create mode 100644 processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise.java create mode 100644 processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/Expected_DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder.java create mode 100644 processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/Expected_GenerateStepBuilderWithSingleArgumentConstructorBuilder.java create mode 100644 processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/GenerateStepBuilderWithSingleArgumentConstructor.java create mode 100644 processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/Expected_DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder.java create mode 100644 processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/Expected_GenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder.java create mode 100644 processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/GenerateStepBuilderWithSingleMandatoryAnnotatedField.java diff --git a/annotations/src/main/java/io/jonasg/bob/ConstructorPolicy.java b/annotations/src/main/java/io/jonasg/bob/ConstructorPolicy.java index 4d61759..69b9fd8 100644 --- a/annotations/src/main/java/io/jonasg/bob/ConstructorPolicy.java +++ b/annotations/src/main/java/io/jonasg/bob/ConstructorPolicy.java @@ -23,6 +23,8 @@ public enum ConstructorPolicy { */ ENFORCED, + ENFORCED_STEPWISE, + /** * Requires all fields * to be explicitly set with a concrete value or {@code null} in the diff --git a/processor/src/main/java/io/jonasg/bob/BuildableField.java b/processor/src/main/java/io/jonasg/bob/BuildableField.java index b3655c4..84edfd3 100644 --- a/processor/src/main/java/io/jonasg/bob/BuildableField.java +++ b/processor/src/main/java/io/jonasg/bob/BuildableField.java @@ -7,13 +7,13 @@ /** * Represents a field that is buildable * - * @param fieldName the name of the field as declared in the type that will be built + * @param name the name of the field as declared in the type that will be built * @param isConstructorArgument indicates if the field can be set through the constructor * @param setterMethodName the name of the setter method to access the field. * @param type the type of the field */ public record BuildableField( - String fieldName, + String name, boolean isConstructorArgument, boolean isMandatory, Optional setterMethodName, diff --git a/processor/src/main/java/io/jonasg/bob/BuilderGenerator.java b/processor/src/main/java/io/jonasg/bob/BuilderGenerator.java index 2ca91d9..a703ce6 100644 --- a/processor/src/main/java/io/jonasg/bob/BuilderGenerator.java +++ b/processor/src/main/java/io/jonasg/bob/BuilderGenerator.java @@ -6,6 +6,8 @@ import com.squareup.javapoet.TypeSpec; import io.jonasg.bob.definitions.TypeDefinition; +import java.util.List; + public class BuilderGenerator { private final Filer filer; @@ -15,15 +17,20 @@ public BuilderGenerator(Filer filer) { } public void generate(TypeDefinition typeDefinition, Buildable buildable, Types typeUtils) { - var abstractTypeSpecFactory = new BuilderTypeSpecFactory(typeDefinition, buildable, typeUtils); - TypeSpec typeSpec = abstractTypeSpecFactory.typeSpec(); - String result; + String packageName = getPackageName(typeDefinition, buildable); + var abstractTypeSpecFactory = new BuilderTypeSpecFactory(typeDefinition, buildable, typeUtils, packageName); + List typeSpecs = abstractTypeSpecFactory.typeSpecs(); + typeSpecs.forEach(t -> TypeWriter.write(filer, packageName, t)); + } + + private String getPackageName(TypeDefinition typeDefinition, Buildable buildable) { + String packageName; if (!buildable.packageName().isEmpty()) { - result = buildable.packageName(); + packageName = buildable.packageName(); } else { - result = String.format("%s.builder", typeDefinition.packageName()); + packageName = String.format("%s.builder", typeDefinition.packageName()); } - TypeWriter.write(filer, result, typeSpec); + return packageName; } } diff --git a/processor/src/main/java/io/jonasg/bob/BuilderTypeSpecFactory.java b/processor/src/main/java/io/jonasg/bob/BuilderTypeSpecFactory.java index f2d5f44..804d4e0 100644 --- a/processor/src/main/java/io/jonasg/bob/BuilderTypeSpecFactory.java +++ b/processor/src/main/java/io/jonasg/bob/BuilderTypeSpecFactory.java @@ -22,6 +22,7 @@ import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeVariableName; +import io.jonasg.bob.StepBuilderInterfaceTypeSpecFactory.BuilderDetails; import io.jonasg.bob.definitions.ConstructorDefinition; import io.jonasg.bob.definitions.FieldDefinition; import io.jonasg.bob.definitions.GenericParameterDefinition; @@ -41,16 +42,24 @@ public class BuilderTypeSpecFactory { private final Types typeUtils; + private final String packageName; + private String builderTypeName(TypeDefinition source) { - return Formatter.format("$typeName$suffix", source.typeName(), "Builder"); + String name = Formatter.format("$typeName$suffix", source.typeName(), "Builder"); + if (this.buildable.constructorPolicy().equals(ConstructorPolicy.ENFORCED_STEPWISE)) { + name = "Default" + name; + } + return name; } - protected BuilderTypeSpecFactory(TypeDefinition typeDefinition, Buildable buildable, Types typeUtils) { + protected BuilderTypeSpecFactory(TypeDefinition typeDefinition, Buildable buildable, Types typeUtils, + String packageName) { this.typeDefinition = typeDefinition; this.buildable = buildable; this.constructorDefinition = extractConstructorDefinitionFrom(typeDefinition); this.buildableFields = extractBuildableFieldsFrom(typeDefinition); this.typeUtils = typeUtils; + this.packageName = packageName; } private List extractBuildableFieldsFrom(TypeDefinition typeDefinition) { @@ -93,11 +102,21 @@ private ConstructorDefinition extractConstructorDefinitionFrom(TypeDefinition ty } } - public TypeSpec typeSpec() { - TypeSpec.Builder builder = TypeSpec.classBuilder(builderTypeName(this.typeDefinition)) + public List typeSpecs() { + List typeSpecs = new ArrayList<>(); + String builderName = builderTypeName(this.typeDefinition); + TypeSpec.Builder builder = TypeSpec.classBuilder(builderName) .addModifiers(Modifier.PUBLIC, Modifier.FINAL); - if (!this.typeDefinition.genericParameters().isEmpty()) + if (this.buildable.constructorPolicy().equals(ConstructorPolicy.ENFORCED_STEPWISE)) { + var factory = new StepBuilderInterfaceTypeSpecFactory(this.typeDefinition, this.buildable, + this.buildableFields, this.packageName); + BuilderDetails builderDetails = factory.typeSpec(builderName); + typeSpecs.add(builderDetails.typeSpec()); + builderDetails.interfaces().forEach(builder::addSuperinterface); + } + if (!this.typeDefinition.genericParameters().isEmpty()) { builder.addTypeVariables(toTypeVariableNames(this.typeDefinition)); + } builder.addMethods(generateSetters()); builder.addFields(generateFields()); builder.addMethod(generateBuildMethod()); @@ -105,7 +124,8 @@ public TypeSpec typeSpec() { if (!this.typeDefinition.genericParameters().isEmpty()) { builder.addMethod(of()); } - return builder.build(); + typeSpecs.add(builder.build()); + return typeSpecs; } private List generateSetters() { @@ -117,14 +137,14 @@ private List generateSetters() { protected MethodSpec generateSetterForField(BuildableField field) { - var builder = MethodSpec.methodBuilder(setterName(field.fieldName())) + var builder = MethodSpec.methodBuilder(setterName(field.name())) .addModifiers(Modifier.PUBLIC) .returns(builderType()) - .addParameter(TypeName.get(field.type()), field.fieldName()); + .addParameter(TypeName.get(field.type()), field.name()); if (field.isConstructorArgument() && isAnEnforcedConstructorPolicy() || field.isMandatory()) { - builder.addStatement("this.$L.set($L)", field.fieldName(), field.fieldName()); + builder.addStatement("this.$L.set($L)", field.name(), field.name()); } else { - builder.addStatement("this.$L = $L", field.fieldName(), field.fieldName()); + builder.addStatement("this.$L = $L", field.name(), field.name()); } return builder.addStatement("return this") .build(); @@ -156,13 +176,13 @@ protected FieldSpec generateField(BuildableField field) { : "nullableOfNameWithinType"; return FieldSpec .builder(ParameterizedTypeName.get(ClassName.get(RequiredField.class), - TypeName.get(boxedType(field.type()))), field.fieldName(), Modifier.PRIVATE, + TypeName.get(boxedType(field.type()))), field.name(), Modifier.PRIVATE, Modifier.FINAL) - .initializer("$T.$L(\"" + field.fieldName() + "\", \"" + .initializer("$T.$L(\"" + field.name() + "\", \"" + this.typeDefinition.typeName() + "\")", RequiredField.class, methodName) .build(); } else { - return FieldSpec.builder(TypeName.get(field.type()), field.fieldName(), Modifier.PRIVATE) + return FieldSpec.builder(TypeName.get(field.type()), field.name(), Modifier.PRIVATE) .build(); } } @@ -200,7 +220,7 @@ protected CodeBlock generateTypeInstantiationStatement() { protected String toConstructorCallingStatement(ConstructorDefinition constructorDefinition) { return constructorDefinition.parameters().stream() - .map(param -> this.buildableFields.stream().anyMatch(f -> Objects.equals(f.fieldName(), param.name())) + .map(param -> this.buildableFields.stream().anyMatch(f -> Objects.equals(f.name(), param.name())) ? String.format("%s%s", param.name(), isAnEnforcedConstructorPolicy() ? ".orElseThrow()" : "") @@ -225,12 +245,12 @@ protected CodeBlock generateFieldAssignment(BuildableField field) { if (field.isConstructorArgument() && isAnEnforcedConstructorPolicy() || field.isMandatory()) { return CodeBlock.builder() .addStatement("instance.$L(this.$L.orElseThrow())", - setterName(field.setterMethodName().orElseThrow()), field.fieldName()) + setterName(field.setterMethodName().orElseThrow()), field.name()) .build(); } else { return CodeBlock.builder() .addStatement("instance.%s(this.%s)".formatted(setterName(field.setterMethodName().orElseThrow()), - field.fieldName())) + field.name())) .build(); } } @@ -336,7 +356,7 @@ protected String setterName(String name) { } private boolean notExcluded(BuildableField field) { - return !Arrays.asList(buildable.excludeFields()).contains(field.fieldName()); + return !Arrays.asList(buildable.excludeFields()).contains(field.name()); } private MethodSpec generateConstructor() { diff --git a/processor/src/main/java/io/jonasg/bob/StepBuilderInterfaceTypeSpecFactory.java b/processor/src/main/java/io/jonasg/bob/StepBuilderInterfaceTypeSpecFactory.java new file mode 100644 index 0000000..07316f9 --- /dev/null +++ b/processor/src/main/java/io/jonasg/bob/StepBuilderInterfaceTypeSpecFactory.java @@ -0,0 +1,138 @@ +package io.jonasg.bob; + +import static io.jonasg.bob.ConstructorPolicy.ENFORCED; +import static io.jonasg.bob.ConstructorPolicy.ENFORCED_STEPWISE; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import javax.lang.model.element.Modifier; + +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeSpec; +import com.squareup.javapoet.TypeSpec.Builder; +import io.jonasg.bob.TypeSpecInterfaceBuilder.InterfaceBuilder; +import io.jonasg.bob.definitions.TypeDefinition; + +public class StepBuilderInterfaceTypeSpecFactory { + + private final Buildable buildable; + + private final TypeDefinition typeDefinition; + + private final List buildableFields; + + private final String packageName; + + public StepBuilderInterfaceTypeSpecFactory(TypeDefinition typeDefinition, + Buildable buildable, + List buildableFields, + String packageName) { + this.buildable = buildable; + this.typeDefinition = typeDefinition; + this.buildableFields = buildableFields; + this.packageName = packageName; + } + + record BuilderDetails(TypeSpec typeSpec, Set interfaces) { + + } + + BuilderDetails typeSpec(String builderImplName) { + Set interfaces = new HashSet<>(); + String builderInterfaceName = String.format("%sBuilder", this.typeDefinition.typeName()); + Builder stepBuilderBuilder = TypeSpec.interfaceBuilder(builderInterfaceName) + .addModifiers(Modifier.PUBLIC); + interfaces.add(ClassName.get(this.packageName, builderInterfaceName)); + + // add static newBuilder method + stepBuilderBuilder.addMethod(MethodSpec.methodBuilder("newBuilder") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns(ClassName.get(this.packageName, builderInterfaceName)) + .addStatement("return new $L()", builderImplName) + .build()); + + List reversedBuildableFields = reverseList(this.buildableFields); + + // add final BuildStep containing all none mandatory fields + InterfaceBuilder buildStepInterfaceBuilder = TypeSpecInterfaceBuilder.anInterface("BuildStep"); + reversedBuildableFields.stream() + .filter(this::notExcluded) + .filter(field -> (!field.isConstructorArgument()) && !field.isMandatory()) + .forEach(field -> buildStepInterfaceBuilder.addMethod(MethodSpec.methodBuilder(field.name()) + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .returns(ClassName.get("", "BuildStep")) + .addParameter(TypeName.get(field.type()), field.name()) + .build())); + // add terminal build method + buildStepInterfaceBuilder.addMethod(MethodSpec.methodBuilder("build") + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .returns(ClassName.get(this.typeDefinition.packageName(), this.typeDefinition.typeName())) + .build()); + buildStepInterfaceBuilder.build(); + TypeSpec buildStep = buildStepInterfaceBuilder.build(); + stepBuilderBuilder.addType(buildStep); + interfaces.add(ClassName.get("", builderInterfaceName + "." + "BuildStep")); + + // add each mandatory field as a separate interface + // skipping the last element because that should be defined as a method within + // the interface itself + AtomicReference nextStep = new AtomicReference<>(buildStep); + List mandatoryFields = reversedBuildableFields + .stream() + .filter(field -> (field.isConstructorArgument() && isEnforcedConstructorPolicy()) + || field.isMandatory()) + .toList(); + mandatoryFields + .subList(0, mandatoryFields.size() - 1) + .stream() + .filter(this::notExcluded) + .map(field -> { + String name = String.format("%sStep", capitalize(field.name())); + interfaces.add(ClassName.get("", builderInterfaceName + "." + name)); + return TypeSpecInterfaceBuilder.functionalInterface(name) + .methodName(field.name()) + .addArgument(TypeName.get(field.type()), field.name()) + .returns(ClassName.get("", nextStep.get().name)) + .build(); + }) + .peek(nextStep::set) + .forEach(stepBuilderBuilder::addType); + + // the initial field to be built + BuildableField buildableField = mandatoryFields + .get(mandatoryFields.size() - 1); + stepBuilderBuilder.addMethod(MethodSpec.methodBuilder(buildableField.name()) + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .addParameter(TypeName.get(buildableField.type()), buildableField.name()) + .returns(ClassName.get("", nextStep.get().name)) + .build()); + return new BuilderDetails(stepBuilderBuilder.build(), interfaces); + } + + private String capitalize(String value) { + return value.substring(0, 1).toUpperCase() + value.substring(1); + } + + private boolean isEnforcedConstructorPolicy() { + return List.of(ENFORCED, ENFORCED_STEPWISE).contains(this.buildable.constructorPolicy()); + } + + private boolean notExcluded(BuildableField field) { + return !Arrays.asList(buildable.excludeFields()).contains(field.name()); + } + + private List reverseList(List originalList) { + List reversedList = new ArrayList<>(); + for (int i = originalList.size() - 1; i >= 0; i--) { + reversedList.add(originalList.get(i)); + } + return reversedList; + } +} diff --git a/processor/src/main/java/io/jonasg/bob/TypeSpecInterfaceBuilder.java b/processor/src/main/java/io/jonasg/bob/TypeSpecInterfaceBuilder.java new file mode 100644 index 0000000..dd7fe37 --- /dev/null +++ b/processor/src/main/java/io/jonasg/bob/TypeSpecInterfaceBuilder.java @@ -0,0 +1,90 @@ +package io.jonasg.bob; + +import java.util.ArrayList; +import java.util.List; + +import javax.lang.model.element.Modifier; + +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeSpec; + +public class TypeSpecInterfaceBuilder { + + public static FunctionalInterfaceBuilder functionalInterface(String name) { + return new FunctionalInterfaceBuilder(name); + } + + public static InterfaceBuilder anInterface(String name) { + return new InterfaceBuilder(name); + } + + public static class InterfaceBuilder { + + private final String name; + + private final List methods = new ArrayList<>(); + + public InterfaceBuilder(String name) { + this.name = name; + } + + @SuppressWarnings("UnusedReturnValue") + public InterfaceBuilder addMethod(MethodSpec methodSpec) { + this.methods.add(methodSpec); + return this; + } + + public TypeSpec build() { + var builder = TypeSpec.interfaceBuilder(this.name); + builder.addModifiers(Modifier.PUBLIC, Modifier.STATIC); + this.methods.forEach(builder::addMethod); + return builder.build(); + } + } + + public static class FunctionalInterfaceBuilder { + + private final String name; + + private TypeName returnType; + + private final List arguments = new ArrayList<>(); + + private String methodName; + + public FunctionalInterfaceBuilder(String name) { + this.name = name; + } + + public FunctionalInterfaceBuilder methodName(String methodName) { + this.methodName = methodName; + return this; + } + + public FunctionalInterfaceBuilder returns(TypeName returnType) { + this.returnType = returnType; + return this; + } + + public FunctionalInterfaceBuilder addArgument(TypeName type, String name) { + this.arguments.add(new Argument(type, name)); + return this; + } + + public TypeSpec build() { + var builder = TypeSpec.interfaceBuilder(this.name); + builder.addModifiers(Modifier.PUBLIC, Modifier.STATIC); + MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(this.methodName) + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .returns(this.returnType); + this.arguments.forEach(a -> methodBuilder.addParameter(a.typeName, a.name)); + builder.addMethod(methodBuilder.build()); + return builder.build(); + + } + + record Argument(TypeName typeName, String name) { + } + } +} diff --git a/processor/src/test/java/io/jonasg/bob/BobFeaturesTests.java b/processor/src/test/java/io/jonasg/bob/BobFeaturesTests.java index bf26eae..0fc187c 100644 --- a/processor/src/test/java/io/jonasg/bob/BobFeaturesTests.java +++ b/processor/src/test/java/io/jonasg/bob/BobFeaturesTests.java @@ -2,6 +2,7 @@ import java.util.List; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import io.toolisticon.cute.Cute; @@ -11,12 +12,12 @@ public class BobFeaturesTests { @Test - void failWhenMultipleConstructorAreAnnotatedWithBuildableConstructor() { + void failWhenMultipleConstructorsAreAnnotatedWithBuildableConstructor() { Cute.blackBoxTest() .given() .processors(List.of(BuildableProcessor.class)) .andSourceFiles( - "/tests/failing-compilation/MultipleBuildableConstructorAnnotationsPresent/MultipleBuildableConstructorAnnotationsPresent.java") + "/tests/failing-compilation/MultipleBuildableConstructorsAnnotationsPresent/MultipleBuildableConstructorsAnnotationsPresent.java") .whenCompiled() .thenExpectThat() .compilationFails() @@ -259,6 +260,91 @@ void constructorParametersAreEnforcedWhenConstructorPolicyIsEnforced() { .executeTest(); } + @Nested + class StepWise { + + @Test + void generateStepBuilderWhenConstructorPolicyIsEnforcedStepWise() { + Cute.blackBoxTest() + .given() + .processors(List.of(BuildableProcessor.class)) + .andSourceFiles( + "/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise.java") + .whenCompiled() + .thenExpectThat() + .compilationSucceeds() + .andThat() + .generatedSourceFile( + "io.jonasg.bob.test.builder.DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder") + .matches( + CuteApi.ExpectedFileObjectMatcherKind.BINARY, + JavaFileObjectUtils.readFromResource( + "/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/Expected_DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder.java")) + .andThat() + .generatedSourceFile( + "io.jonasg.bob.test.builder.GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder") + .matches( + CuteApi.ExpectedFileObjectMatcherKind.BINARY, + JavaFileObjectUtils.readFromResource( + "/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/Expected_GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder.java")) + .executeTest(); + } + + @Test + void generateStepBuilderWithSingleArgumentConstructor() { + Cute.blackBoxTest() + .given() + .processors(List.of(BuildableProcessor.class)) + .andSourceFiles( + "/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/GenerateStepBuilderWithSingleArgumentConstructor.java") + .whenCompiled() + .thenExpectThat() + .compilationSucceeds() + .andThat() + .generatedSourceFile( + "io.jonasg.bob.test.builder.DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder") + .matches( + CuteApi.ExpectedFileObjectMatcherKind.BINARY, + JavaFileObjectUtils.readFromResource( + "/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/Expected_DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder.java")) + .andThat() + .generatedSourceFile( + "io.jonasg.bob.test.builder.GenerateStepBuilderWithSingleArgumentConstructorBuilder") + .matches( + CuteApi.ExpectedFileObjectMatcherKind.BINARY, + JavaFileObjectUtils.readFromResource( + "/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/Expected_GenerateStepBuilderWithSingleArgumentConstructorBuilder.java")) + .executeTest(); + } + + @Test + void generateStepBuilderWithSingleMandatoryAnnotatedField() { + Cute.blackBoxTest() + .given() + .processors(List.of(BuildableProcessor.class)) + .andSourceFiles( + "/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/GenerateStepBuilderWithSingleMandatoryAnnotatedField.java") + .whenCompiled() + .thenExpectThat() + .compilationSucceeds() + .andThat() + .generatedSourceFile( + "io.jonasg.bob.test.builder.DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder") + .matches( + CuteApi.ExpectedFileObjectMatcherKind.BINARY, + JavaFileObjectUtils.readFromResource( + "/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/Expected_DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder.java")) + .andThat() + .generatedSourceFile( + "io.jonasg.bob.test.builder.GenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder") + .matches( + CuteApi.ExpectedFileObjectMatcherKind.BINARY, + JavaFileObjectUtils.readFromResource( + "/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/Expected_GenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder.java")) + .executeTest(); + } + } + @Test void constructorParametersAreEnforcedAndNullableWhenConstructorPolicyIsEnforcedAllowNulls() { Cute.blackBoxTest() diff --git a/processor/src/test/resources/tests/failing-compilation/MultipleBuildableConstructorAnnotationsPresent/MultipleBuildableConstructorAnnotationsPresent.java b/processor/src/test/resources/tests/failing-compilation/MultipleBuildableConstructorsAnnotationsPresent/MultipleBuildableConstructorsAnnotationsPresent.java similarity index 54% rename from processor/src/test/resources/tests/failing-compilation/MultipleBuildableConstructorAnnotationsPresent/MultipleBuildableConstructorAnnotationsPresent.java rename to processor/src/test/resources/tests/failing-compilation/MultipleBuildableConstructorsAnnotationsPresent/MultipleBuildableConstructorsAnnotationsPresent.java index 3221e62..62f749b 100644 --- a/processor/src/test/resources/tests/failing-compilation/MultipleBuildableConstructorAnnotationsPresent/MultipleBuildableConstructorAnnotationsPresent.java +++ b/processor/src/test/resources/tests/failing-compilation/MultipleBuildableConstructorsAnnotationsPresent/MultipleBuildableConstructorsAnnotationsPresent.java @@ -3,7 +3,7 @@ import io.jonasg.bob.Buildable; @Buildable -public class MultipleBuildableConstructorAnnotationsPresent { +public class MultipleBuildableConstructorsAnnotationsPresent { private String make; private int year; @@ -14,17 +14,17 @@ public class MultipleBuildableConstructorAnnotationsPresent { private float fuelEfficiency; - public MultipleBuildableConstructorAnnotationsPresent() { + public MultipleBuildableConstructorsAnnotationsPresent() { } - public MultipleBuildableConstructorAnnotationsPresent(double engineSize, boolean isElectric, float fuelEfficiency) { + public MultipleBuildableConstructorsAnnotationsPresent(double engineSize, boolean isElectric, float fuelEfficiency) { this.engineSize = engineSize; this.isElectric = isElectric; this.fuelEfficiency = fuelEfficiency; } @Buildable.Constructor - public MultipleBuildableConstructorAnnotationsPresent(String make, int year, double engineSize, boolean isElectric, float fuelEfficiency) { + public MultipleBuildableConstructorsAnnotationsPresent(String make, int year, double engineSize, boolean isElectric, float fuelEfficiency) { this.make = make; this.year = year; this.engineSize = engineSize; @@ -33,7 +33,7 @@ public MultipleBuildableConstructorAnnotationsPresent(String make, int year, dou } @Buildable.Constructor - public MultipleBuildableConstructorAnnotationsPresent(String make, int year) { + public MultipleBuildableConstructorsAnnotationsPresent(String make, int year) { this.make = make; this.year = year; } diff --git a/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/Expected_DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder.java b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/Expected_DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder.java new file mode 100644 index 0000000..c88810e --- /dev/null +++ b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/Expected_DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder.java @@ -0,0 +1,56 @@ +package io.jonasg.bob.test.builder; + +import io.jonasg.bob.test.GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise; +import java.lang.String; + +public final class DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder implements GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder.YearStep, GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder.BuildStep, GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder { + private String make; + + private int year; + + private double engineSize; + + private boolean isElectric; + + private float fuelEfficiency; + + public DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder() { + } + + public DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder make( + String make) { + this.make = make; + return this; + } + + public DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder year(int year) { + this.year = year; + return this; + } + + public DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder engineSize( + double engineSize) { + this.engineSize = engineSize; + return this; + } + + public DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder isElectric( + boolean isElectric) { + this.isElectric = isElectric; + return this; + } + + public DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder fuelEfficiency( + float fuelEfficiency) { + this.fuelEfficiency = fuelEfficiency; + return this; + } + + public GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise build() { + var instance = new GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise(make, year); + instance.setEngineSize(this.engineSize); + instance.setIsElectric(this.isElectric); + instance.setFuelEfficiency(this.fuelEfficiency); + return instance; + } +} diff --git a/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/Expected_GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder.java b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/Expected_GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder.java new file mode 100644 index 0000000..795d622 --- /dev/null +++ b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/Expected_GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder.java @@ -0,0 +1,26 @@ +package io.jonasg.bob.test.builder; + +import io.jonasg.bob.test.GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise; +import java.lang.String; + +public interface GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder { + static GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder newBuilder() { + return new DefaultGenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWiseBuilder(); + } + + YearStep make(String make); + + interface BuildStep { + BuildStep fuelEfficiency(float fuelEfficiency); + + BuildStep isElectric(boolean isElectric); + + BuildStep engineSize(double engineSize); + + GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise build(); + } + + interface YearStep { + BuildStep year(int year); + } +} diff --git a/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise.java b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise.java new file mode 100644 index 0000000..5f7d068 --- /dev/null +++ b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise/GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise.java @@ -0,0 +1,51 @@ +package io.jonasg.bob.test; + +import io.jonasg.bob.Buildable; +import io.jonasg.bob.ConstructorPolicy; +import java.lang.String; + +@Buildable(constructorPolicy = ConstructorPolicy.ENFORCED_STEPWISE) +public class GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise { + private String make; + + private int year; + + private double engineSize; + + private boolean isElectric; + + private float fuelEfficiency; + + public GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise() { + } + + public GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise(String make, int year) { + this.make = make; + this.year = year; + } + + public GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise setEngineSize(double engineSize) { + this.engineSize = engineSize; + return this; + } + + public GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise setIsElectric(boolean isElectric) { + isElectric = isElectric; + return this; + } + + public GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise setFuelEfficiency(float fuelEfficiency) { + this.fuelEfficiency = fuelEfficiency; + return this; + } + + public GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise setMake(String make) { + this.make = make; + return this; + } + + public GenerateStepBuilderWhenConstructorPolicyIsEnforcedStepWise setYear(int year) { + this.year = year; + return this; + } +} diff --git a/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/Expected_DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder.java b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/Expected_DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder.java new file mode 100644 index 0000000..4341fe2 --- /dev/null +++ b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/Expected_DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder.java @@ -0,0 +1,56 @@ +package io.jonasg.bob.test.builder; + +import io.jonasg.bob.test.GenerateStepBuilderWithSingleArgumentConstructor; +import java.lang.String; + +public final class DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder implements GenerateStepBuilderWithSingleArgumentConstructorBuilder.BuildStep, GenerateStepBuilderWithSingleArgumentConstructorBuilder { + private int year; + + private String make; + + private double engineSize; + + private boolean isElectric; + + private float fuelEfficiency; + + public DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder() { + } + + public DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder year(int year) { + this.year = year; + return this; + } + + public DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder make(String make) { + this.make = make; + return this; + } + + public DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder engineSize( + double engineSize) { + this.engineSize = engineSize; + return this; + } + + public DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder isElectric( + boolean isElectric) { + this.isElectric = isElectric; + return this; + } + + public DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder fuelEfficiency( + float fuelEfficiency) { + this.fuelEfficiency = fuelEfficiency; + return this; + } + + public GenerateStepBuilderWithSingleArgumentConstructor build() { + var instance = new GenerateStepBuilderWithSingleArgumentConstructor(year); + instance.setMake(this.make); + instance.setEngineSize(this.engineSize); + instance.setIsElectric(this.isElectric); + instance.setFuelEfficiency(this.fuelEfficiency); + return instance; + } +} diff --git a/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/Expected_GenerateStepBuilderWithSingleArgumentConstructorBuilder.java b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/Expected_GenerateStepBuilderWithSingleArgumentConstructorBuilder.java new file mode 100644 index 0000000..117dfcf --- /dev/null +++ b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/Expected_GenerateStepBuilderWithSingleArgumentConstructorBuilder.java @@ -0,0 +1,24 @@ +package io.jonasg.bob.test.builder; + +import io.jonasg.bob.test.GenerateStepBuilderWithSingleArgumentConstructor; +import java.lang.String; + +public interface GenerateStepBuilderWithSingleArgumentConstructorBuilder { + static GenerateStepBuilderWithSingleArgumentConstructorBuilder newBuilder() { + return new DefaultGenerateStepBuilderWithSingleArgumentConstructorBuilder(); + } + + BuildStep year(int year); + + interface BuildStep { + BuildStep fuelEfficiency(float fuelEfficiency); + + BuildStep isElectric(boolean isElectric); + + BuildStep engineSize(double engineSize); + + BuildStep make(String make); + + GenerateStepBuilderWithSingleArgumentConstructor build(); + } +} diff --git a/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/GenerateStepBuilderWithSingleArgumentConstructor.java b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/GenerateStepBuilderWithSingleArgumentConstructor.java new file mode 100644 index 0000000..ea40fb1 --- /dev/null +++ b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleArgumentConstructor/GenerateStepBuilderWithSingleArgumentConstructor.java @@ -0,0 +1,51 @@ +package io.jonasg.bob.test; + +import io.jonasg.bob.Buildable; +import io.jonasg.bob.ConstructorPolicy; +import java.lang.String; + +@Buildable(constructorPolicy = ConstructorPolicy.ENFORCED_STEPWISE) +public class GenerateStepBuilderWithSingleArgumentConstructor { + private String make; + + private int year; + + private double engineSize; + + private boolean isElectric; + + private float fuelEfficiency; + + public GenerateStepBuilderWithSingleArgumentConstructor() { + } + + public GenerateStepBuilderWithSingleArgumentConstructor(int year) { + this.make = make; + this.year = year; + } + + public GenerateStepBuilderWithSingleArgumentConstructor setEngineSize(double engineSize) { + this.engineSize = engineSize; + return this; + } + + public GenerateStepBuilderWithSingleArgumentConstructor setIsElectric(boolean isElectric) { + isElectric = isElectric; + return this; + } + + public GenerateStepBuilderWithSingleArgumentConstructor setFuelEfficiency(float fuelEfficiency) { + this.fuelEfficiency = fuelEfficiency; + return this; + } + + public GenerateStepBuilderWithSingleArgumentConstructor setMake(String make) { + this.make = make; + return this; + } + + public GenerateStepBuilderWithSingleArgumentConstructor setYear(int year) { + this.year = year; + return this; + } +} diff --git a/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/Expected_DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder.java b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/Expected_DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder.java new file mode 100644 index 0000000..8ac83e9 --- /dev/null +++ b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/Expected_DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder.java @@ -0,0 +1,58 @@ +package io.jonasg.bob.test.builder; + +import io.jonasg.bob.RequiredField; +import io.jonasg.bob.test.GenerateStepBuilderWithSingleMandatoryAnnotatedField; +import java.lang.String; + +public final class DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder implements GenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder.BuildStep, GenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder { + private int year; + + private final RequiredField make = RequiredField.nullableOfNameWithinType("make", "GenerateStepBuilderWithSingleMandatoryAnnotatedField"); + + private double engineSize; + + private boolean isElectric; + + private float fuelEfficiency; + + public DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder() { + } + + public DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder year(int year) { + this.year = year; + return this; + } + + public DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder make(String make) { + this.make.set(make); + return this; + } + + public DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder engineSize( + double engineSize) { + this.engineSize = engineSize; + return this; + } + + public DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder isElectric( + boolean isElectric) { + this.isElectric = isElectric; + return this; + } + + public DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder fuelEfficiency( + float fuelEfficiency) { + this.fuelEfficiency = fuelEfficiency; + return this; + } + + public GenerateStepBuilderWithSingleMandatoryAnnotatedField build() { + var instance = new GenerateStepBuilderWithSingleMandatoryAnnotatedField(); + instance.setYear(this.year); + instance.setMake(this.make.orElseThrow()); + instance.setEngineSize(this.engineSize); + instance.setIsElectric(this.isElectric); + instance.setFuelEfficiency(this.fuelEfficiency); + return instance; + } +} diff --git a/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/Expected_GenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder.java b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/Expected_GenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder.java new file mode 100644 index 0000000..14b200f --- /dev/null +++ b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/Expected_GenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder.java @@ -0,0 +1,24 @@ +package io.jonasg.bob.test.builder; + +import io.jonasg.bob.test.GenerateStepBuilderWithSingleMandatoryAnnotatedField; +import java.lang.String; + +public interface GenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder { + static GenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder newBuilder() { + return new DefaultGenerateStepBuilderWithSingleMandatoryAnnotatedFieldBuilder(); + } + + BuildStep make(String make); + + interface BuildStep { + BuildStep fuelEfficiency(float fuelEfficiency); + + BuildStep isElectric(boolean isElectric); + + BuildStep engineSize(double engineSize); + + BuildStep year(int year); + + GenerateStepBuilderWithSingleMandatoryAnnotatedField build(); + } +} diff --git a/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/GenerateStepBuilderWithSingleMandatoryAnnotatedField.java b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/GenerateStepBuilderWithSingleMandatoryAnnotatedField.java new file mode 100644 index 0000000..eba41da --- /dev/null +++ b/processor/src/test/resources/tests/successful-compilation/StepWise/GenerateStepBuilderWithSingleMandatoryAnnotatedField/GenerateStepBuilderWithSingleMandatoryAnnotatedField.java @@ -0,0 +1,47 @@ +package io.jonasg.bob.test; + +import io.jonasg.bob.Buildable; +import io.jonasg.bob.Buildable.Mandatory; +import io.jonasg.bob.ConstructorPolicy; + +@Buildable(constructorPolicy = ConstructorPolicy.ENFORCED_STEPWISE) +public class GenerateStepBuilderWithSingleMandatoryAnnotatedField { + private int year; + + @Mandatory + private String make; + + private double engineSize; + + private boolean isElectric; + + private float fuelEfficiency; + + public GenerateStepBuilderWithSingleMandatoryAnnotatedField() { + } + + public GenerateStepBuilderWithSingleMandatoryAnnotatedField setEngineSize(double engineSize) { + this.engineSize = engineSize; + return this; + } + + public GenerateStepBuilderWithSingleMandatoryAnnotatedField setIsElectric(boolean isElectric) { + isElectric = isElectric; + return this; + } + + public GenerateStepBuilderWithSingleMandatoryAnnotatedField setFuelEfficiency(float fuelEfficiency) { + this.fuelEfficiency = fuelEfficiency; + return this; + } + + public GenerateStepBuilderWithSingleMandatoryAnnotatedField setMake(String make) { + this.make = make; + return this; + } + + public GenerateStepBuilderWithSingleMandatoryAnnotatedField setYear(int year) { + this.year = year; + return this; + } +}