Skip to content

Commit

Permalink
feat(processor): add option for custom static factory method name
Browse files Browse the repository at this point in the history
  • Loading branch information
jonas-grgt committed Apr 25, 2024
1 parent a65a7e6 commit 286a122
Show file tree
Hide file tree
Showing 11 changed files with 381 additions and 1 deletion.
10 changes: 10 additions & 0 deletions annotations/src/main/java/io/jonasg/bob/Buildable.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@
*/
Strategy[] strategy() default Strategy.PERMISSIVE;

/**
* For Step Builders instigated by the {@link Strategy#STRICT} strategy this
* will
* the static factory name within the Builder interface.
* For others, a static factory method will be added to the Builder class.
*
* @return the name of the factory method
*/
String factoryName() default "";

/**
* Marks a constructor as buildable.
* This means that a builder will be generated
Expand Down
11 changes: 11 additions & 0 deletions processor/src/main/java/io/jonasg/bob/BuilderTypeSpecFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ public List<TypeSpec> typeSpecs() {
if (!this.typeDefinition.genericParameters().isEmpty()) {
builder.addMethod(of());
}
if (!this.buildable.factoryName().isEmpty() && !this.strategy().contains(Strategy.STEP_WISE)) {
builder.addMethod(addStaticFactoryMethod(this.buildable.factoryName()));
}
typeSpecs.add(builder.build());
return typeSpecs;
}
Expand Down Expand Up @@ -349,6 +352,14 @@ private List<TypeName> simpleClassNames(List<SimpleTypeDefinition> definitions)
return typeNames;
}

private MethodSpec addStaticFactoryMethod(String name) {
return MethodSpec.methodBuilder(name)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(this.builderType())
.addStatement("return new $T()", this.builderType())
.build();
}

protected String setterName(String name) {
if (buildable.setterPrefix().isEmpty()) {
return name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ BuilderDetails typeSpec(String builderImplName) {
interfaces.add(ClassName.get(this.packageName, builderInterfaceName));

// add static newBuilder method
stepBuilderBuilder.addMethod(MethodSpec.methodBuilder("newBuilder")
stepBuilderBuilder.addMethod(MethodSpec.methodBuilder(builderStaticFactoryMethodName())
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(ClassName.get(this.packageName, builderInterfaceName))
.addStatement("return new $L()", builderImplName)
Expand Down Expand Up @@ -156,6 +156,10 @@ BuilderDetails typeSpec(String builderImplName) {
return new BuilderDetails(stepBuilderBuilder.build(), interfaces);
}

private String builderStaticFactoryMethodName() {
return this.buildable.factoryName().isEmpty() ? "newBuilder" : this.buildable.factoryName();
}

private String capitalize(String value) {
return value.substring(0, 1).toUpperCase() + value.substring(1);
}
Expand Down
67 changes: 67 additions & 0 deletions processor/src/test/java/io/jonasg/bob/StrategyTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,26 @@ public class StrategyTests {
@Nested
class PermissiveStrategy {

@Test
void customFactoryMethodName() {
Cute.blackBoxTest()
.given()
.processors(List.of(BuildableProcessor.class))
.andSourceFiles(
"/tests/Strategies/Permissive/CustomFactoryMethodName/CustomFactoryMethodName.java")
.whenCompiled()
.thenExpectThat()
.compilationSucceeds()
.andThat()
.generatedSourceFile(
"io.jonasg.bob.test.CustomFactoryMethodNameBuilder")
.matches(
CuteApi.ExpectedFileObjectMatcherKind.BINARY,
JavaFileObjectUtils.readFromResource(
"/tests/Strategies/Permissive/CustomFactoryMethodName/Expected_CustomFactoryMethodNameBuilder.java"))
.executeTest();
}

@Test
void failWhenMandatoryAnnotationIsUsedWithPermissiveStrategy() {
Cute.blackBoxTest()
Expand Down Expand Up @@ -76,6 +96,26 @@ void throwExceptionWhenMandatoryAnnotatedFieldsAreNotSet() {
.executeTest();
}

@Test
void customFactoryMethodName() {
Cute.blackBoxTest()
.given()
.processors(List.of(BuildableProcessor.class))
.andSourceFiles(
"/tests/Strategies/Strict/CustomFactoryMethodName/CustomFactoryMethodName.java")
.whenCompiled()
.thenExpectThat()
.compilationSucceeds()
.andThat()
.generatedSourceFile(
"io.jonasg.bob.test.CustomFactoryMethodNameBuilder")
.matches(
CuteApi.ExpectedFileObjectMatcherKind.BINARY,
JavaFileObjectUtils.readFromResource(
"/tests/Strategies/Strict/CustomFactoryMethodName/Expected_CustomFactoryMethodNameBuilder.java"))
.executeTest();
}

@Nested
class AllowNulls {

Expand Down Expand Up @@ -145,6 +185,33 @@ void fieldsDeclaredInBuildableAnnotationCanBeSetToNull() {
@Nested
class StepWiseStrategy {

@Test
void customFactoryMethodName() {
Cute.blackBoxTest()
.given()
.processors(List.of(BuildableProcessor.class))
.andSourceFiles(
"/tests/Strategies/StepWise/CustomFactoryMethodName/CustomFactoryMethodName.java")
.whenCompiled()
.thenExpectThat()
.compilationSucceeds()
.andThat()
.generatedSourceFile(
"io.jonasg.bob.test.DefaultCustomFactoryMethodNameBuilder")
.matches(
CuteApi.ExpectedFileObjectMatcherKind.BINARY,
JavaFileObjectUtils.readFromResource(
"/tests/Strategies/StepWise/CustomFactoryMethodName/Expected_DefaultCustomFactoryMethodNameBuilder.java"))
.andThat()
.generatedSourceFile(
"io.jonasg.bob.test.CustomFactoryMethodNameBuilder")
.matches(
CuteApi.ExpectedFileObjectMatcherKind.BINARY,
JavaFileObjectUtils.readFromResource(
"/tests/Strategies/StepWise/CustomFactoryMethodName/Expected_CustomFactoryMethodNameBuilder.java"))
.executeTest();
}

@Test
void generateStepBuilderWhenConstructorPolicyIsEnforcedStepWise() {
Cute.blackBoxTest()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.jonasg.bob.test;

import io.jonasg.bob.Buildable;
import io.jonasg.bob.Strategy;

@Buildable(factoryName = "customName")
public class CustomFactoryMethodName {
private String make;

private int year;

private double engineSize;

private boolean isElectric;

private float fuelEfficiency;

public CustomFactoryMethodName(double engineSize, boolean isElectric, float fuelEfficiency) {
this.engineSize = engineSize;
this.isElectric = isElectric;
this.fuelEfficiency = fuelEfficiency;
}

public CustomFactoryMethodName setMake(String make) {
this.make = make;
return this;
}

public CustomFactoryMethodName setYear(int year) {
this.year = year;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.jonasg.bob.test;

import java.lang.String;

public final class CustomFactoryMethodNameBuilder {
private double engineSize;

private boolean isElectric;

private float fuelEfficiency;

private String make;

private int year;

public CustomFactoryMethodNameBuilder() {
}

public CustomFactoryMethodNameBuilder engineSize(double engineSize) {
this.engineSize = engineSize;
return this;
}

public CustomFactoryMethodNameBuilder isElectric(boolean isElectric) {
this.isElectric = isElectric;
return this;
}

public CustomFactoryMethodNameBuilder fuelEfficiency(float fuelEfficiency) {
this.fuelEfficiency = fuelEfficiency;
return this;
}

public CustomFactoryMethodNameBuilder make(String make) {
this.make = make;
return this;
}

public CustomFactoryMethodNameBuilder year(int year) {
this.year = year;
return this;
}

public CustomFactoryMethodName build() {
var instance = new CustomFactoryMethodName(engineSize, isElectric, fuelEfficiency);
instance.setMake(this.make);
instance.setYear(this.year);
return instance;
}

public static CustomFactoryMethodNameBuilder customName() {
return new CustomFactoryMethodNameBuilder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.jonasg.bob.test;

import io.jonasg.bob.Buildable;
import io.jonasg.bob.Strategy;

@Buildable(strategy = Strategy.STEP_WISE, factoryName = "customName")
public class CustomFactoryMethodName {
private String make;

private int year;

private double engineSize;

private boolean isElectric;

private float fuelEfficiency;

public CustomFactoryMethodName(double engineSize, boolean isElectric, float fuelEfficiency) {
this.engineSize = engineSize;
this.isElectric = isElectric;
this.fuelEfficiency = fuelEfficiency;
}

public CustomFactoryMethodName setMake(String make) {
this.make = make;
return this;
}

public CustomFactoryMethodName setYear(int year) {
this.year = year;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.jonasg.bob.test;

import java.lang.String;

public interface CustomFactoryMethodNameBuilder {
static CustomFactoryMethodNameBuilder customName() {
return new DefaultCustomFactoryMethodNameBuilder();
}

IsElectricStep engineSize(double engineSize);

interface BuildStep {
BuildStep year(int year);

BuildStep make(String make);

CustomFactoryMethodName build();
}

interface FuelEfficiencyStep {
BuildStep fuelEfficiency(float fuelEfficiency);
}

interface IsElectricStep {
FuelEfficiencyStep isElectric(boolean isElectric);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.jonasg.bob.test;

import java.lang.String;

public final class DefaultCustomFactoryMethodNameBuilder implements CustomFactoryMethodNameBuilder.BuildStep, CustomFactoryMethodNameBuilder, CustomFactoryMethodNameBuilder.FuelEfficiencyStep, CustomFactoryMethodNameBuilder.IsElectricStep {
private double engineSize;

private boolean isElectric;

private float fuelEfficiency;

private String make;

private int year;

public DefaultCustomFactoryMethodNameBuilder() {
}

public DefaultCustomFactoryMethodNameBuilder engineSize(double engineSize) {
this.engineSize = engineSize;
return this;
}

public DefaultCustomFactoryMethodNameBuilder isElectric(boolean isElectric) {
this.isElectric = isElectric;
return this;
}

public DefaultCustomFactoryMethodNameBuilder fuelEfficiency(float fuelEfficiency) {
this.fuelEfficiency = fuelEfficiency;
return this;
}

public DefaultCustomFactoryMethodNameBuilder make(String make) {
this.make = make;
return this;
}

public DefaultCustomFactoryMethodNameBuilder year(int year) {
this.year = year;
return this;
}

public CustomFactoryMethodName build() {
var instance = new CustomFactoryMethodName(engineSize, isElectric, fuelEfficiency);
instance.setMake(this.make);
instance.setYear(this.year);
return instance;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.jonasg.bob.test;

import io.jonasg.bob.Buildable;
import io.jonasg.bob.Strategy;

@Buildable(strategy = Strategy.STRICT, factoryName = "customName")
public class CustomFactoryMethodName {
private String make;

private int year;

private double engineSize;

private boolean isElectric;

private float fuelEfficiency;

public CustomFactoryMethodName(double engineSize, boolean isElectric, float fuelEfficiency) {
this.engineSize = engineSize;
this.isElectric = isElectric;
this.fuelEfficiency = fuelEfficiency;
}

public CustomFactoryMethodName setMake(String make) {
this.make = make;
return this;
}

public CustomFactoryMethodName setYear(int year) {
this.year = year;
return this;
}
}
Loading

0 comments on commit 286a122

Please sign in to comment.