Skip to content
This repository has been archived by the owner on May 26, 2020. It is now read-only.

Commit

Permalink
Fix builder optional arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
mapingo committed Jan 10, 2020
1 parent c5054e6 commit 423e9a5
Show file tree
Hide file tree
Showing 9 changed files with 227 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package uk.gov.justice.generation.pojo.plugin.classmodifying.builder;

import static java.util.stream.Collectors.toList;
import static javax.lang.model.element.Modifier.PRIVATE;

import uk.gov.justice.generation.pojo.dom.Definition;
import uk.gov.justice.generation.pojo.generators.ClassNameFactory;
Expand All @@ -10,29 +9,29 @@
import java.util.List;

import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.TypeName;

/**
* Factory for creating the fields in the Builder class
*/
public class BuilderFieldFactory {

private final FieldSpecFactory fieldSpecFactory;

public BuilderFieldFactory(final FieldSpecFactory fieldSpecFactory) {
this.fieldSpecFactory = fieldSpecFactory;
}

/**
*
* @param fieldDefinitions The list of {@link Definition}s from which to create fields
* @param classNameFactory The factory for creating the class name
*
* @return A list of {@link FieldSpec}s of the fields of the Builder
*/
public List<FieldSpec> createFields(
final List<Definition> fieldDefinitions,
final ClassNameFactory classNameFactory,
final PluginContext pluginContext) {
public List<FieldSpec> createFields(final List<Definition> fieldDefinitions,
final ClassNameFactory classNameFactory,
final PluginContext pluginContext) {

return fieldDefinitions.stream()
.map(fieldDefinition -> {
final TypeName typeName = classNameFactory.createTypeNameFrom(fieldDefinition, pluginContext);
return FieldSpec.builder(typeName, fieldDefinition.getFieldName(), PRIVATE).build();
})
.map(fieldDefinition -> fieldSpecFactory.createFieldSpecFor(fieldDefinition, classNameFactory, pluginContext))
.collect(toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,6 @@ private MethodSpec buildMethod(final ClassName pojoClassName, final List<Definit
return builderMethodFactory.createTheBuildMethodWithAdditionalProperties(fieldDefinitions, pojoClassName);
}

return builderMethodFactory.createTheBuildMethod(fieldDefinitions, pojoClassName);
return builderMethodFactory.createTheBuildMethod(fieldDefinitions, pojoClassName, pluginContext);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ public class BuilderGeneratorFactory {
/**
* Creates a {@link BuilderGenerator}
*
* @param classDefinition The {@link ClassDefinition} of the outer POJO which will contain
* the Builder as a static inner class
* @param classDefinition The {@link ClassDefinition} of the outer POJO which will contain the
* Builder as a static inner class
* @param classNameFactory A factory for creating the field and method class names
*
* @return a newly constructed {@link BuilderGenerator}
*/
public BuilderGenerator create(final ClassDefinition classDefinition,
Expand All @@ -26,8 +25,8 @@ public BuilderGenerator create(final ClassDefinition classDefinition,
return new BuilderGenerator(
classDefinition,
classNameFactory,
new BuilderFieldFactory(),
new BuilderMethodFactory(),
new BuilderFieldFactory(new FieldSpecFactory()),
new BuilderMethodFactory(classNameFactory),
new AdditionalPropertiesDeterminer(),
pluginContext
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,26 @@
import uk.gov.justice.generation.pojo.plugin.PluginContext;

import java.util.List;
import java.util.Objects;
import java.util.Optional;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;

/**
* Factory for creating the 'with' and 'build' methods of the POJO's static inner Builder
*/
public class BuilderMethodFactory {

private final ClassNameFactory classNameFactory;

public BuilderMethodFactory(final ClassNameFactory classNameFactory) {
this.classNameFactory = classNameFactory;
}

public List<MethodSpec> createTheWithMethods(
final List<Definition> fieldDefinitions,
final ClassNameFactory classNameFactory,
Expand Down Expand Up @@ -60,10 +69,20 @@ public List<MethodSpec> createTheWithMethodsWithAdditionalProperties(

public MethodSpec createTheBuildMethod(
final List<Definition> fieldDefinitions,
final ClassName pojoClassName) {
final ClassName pojoClassName,
final PluginContext pluginContext) {

final String params = fieldDefinitions.stream()
.map(Definition::getFieldName)
.map(definition -> {

final TypeName typeName = classNameFactory.createTypeNameFrom(definition, pluginContext);

if (typeName instanceof ParameterizedTypeName && Objects.equals(((ParameterizedTypeName) typeName).rawType.simpleName(), Optional.class.getSimpleName())) {
return "Optional.ofNullable(" + definition.getFieldName() + ")";
}

return definition.getFieldName();
})
.collect(joining(", "));

return MethodSpec.methodBuilder("build")
Expand Down Expand Up @@ -107,6 +126,21 @@ private MethodSpec generateWithMethod(
final String fieldName = fieldDefinition.getFieldName();
final TypeName typeName = classNameFactory.createTypeNameFrom(fieldDefinition, pluginContext);

if (typeName instanceof ParameterizedTypeName && Objects.equals(((ParameterizedTypeName) typeName).rawType.simpleName(), Optional.class.getSimpleName())) {

final TypeName parameterType = ((ParameterizedTypeName) typeName).typeArguments.get(0);

return methodBuilder("with" + capitalize(fieldName))
.addModifiers(PUBLIC)
.addParameter(parameterType, fieldName, FINAL)
.returns(builderClassName)
.addCode(CodeBlock.builder()
.addStatement("this.$L = $L", fieldName, fieldName)
.addStatement("return this")
.build())
.build();
}

return methodBuilder("with" + capitalize(fieldName))
.addModifiers(PUBLIC)
.addParameter(typeName, fieldName, FINAL)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package uk.gov.justice.generation.pojo.plugin.classmodifying.builder;

import static javax.lang.model.element.Modifier.PRIVATE;

import uk.gov.justice.generation.pojo.dom.Definition;
import uk.gov.justice.generation.pojo.generators.ClassNameFactory;
import uk.gov.justice.generation.pojo.plugin.PluginContext;

import java.util.List;
import java.util.Objects;
import java.util.Optional;

import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;

public class FieldSpecFactory {

public FieldSpec createFieldSpecFor(final Definition fieldDefinition,
final ClassNameFactory classNameFactory,
final PluginContext pluginContext) {

final TypeName typeName = classNameFactory.createTypeNameFrom(fieldDefinition, pluginContext);

if (isOptionalType(typeName)) {
final TypeName parameterizedType = getParameterizedType((ParameterizedTypeName) typeName);
return FieldSpec
.builder(parameterizedType, fieldDefinition.getFieldName(), PRIVATE)
.build();
}

return FieldSpec
.builder(typeName, fieldDefinition.getFieldName(), PRIVATE)
.build();
}

private TypeName getParameterizedType(final ParameterizedTypeName typeName) {
final List<TypeName> typeArguments = typeName.typeArguments;

if (null != typeArguments && !typeArguments.isEmpty()) {
return typeArguments.get(0);
}

return typeName;
}

private boolean isOptionalType(final TypeName typeName) {
return typeName instanceof ParameterizedTypeName
&& Objects.equals(((ParameterizedTypeName) typeName).rawType.simpleName(), Optional.class.getSimpleName());
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package uk.gov.justice.generation.pojo.plugin.classmodifying.builder;

import static com.squareup.javapoet.TypeName.get;
import static java.util.Arrays.asList;
import static javax.lang.model.element.Modifier.PRIVATE;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
Expand All @@ -15,15 +12,21 @@

import java.util.List;

import javax.lang.model.element.Modifier;

import com.squareup.javapoet.FieldSpec;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class BuilderFieldFactoryTest {

@Mock
private FieldSpecFactory fieldSpecFactory;

@InjectMocks
private BuilderFieldFactory builderFieldFactory;

Expand All @@ -32,32 +35,29 @@ public void shouldGenerateThePrivateFieldsForOurBuilder() throws Exception {

final Definition fieldDefinition_1 = mock(Definition.class);
final Definition fieldDefinition_2 = mock(Definition.class);
final List<Definition> fieldDefinitions = asList(fieldDefinition_1, fieldDefinition_2);
final Definition fieldDefinition_3 = mock(Definition.class);
final List<Definition> fieldDefinitions = asList(fieldDefinition_1, fieldDefinition_2, fieldDefinition_3);

final ClassNameFactory classNameFactory = mock(ClassNameFactory.class);
final PluginContext pluginContext = mock(PluginContext.class);

when(fieldDefinition_1.getFieldName()).thenReturn("fieldDefinition_1");
when(fieldDefinition_2.getFieldName()).thenReturn("fieldDefinition_2");
final FieldSpec fieldSpec_1 = FieldSpec.builder(String.class, "field_1", Modifier.PUBLIC).build();
final FieldSpec fieldSpec_2 = FieldSpec.builder(String.class, "field_2", Modifier.PUBLIC).build();
final FieldSpec fieldSpec_3 = FieldSpec.builder(String.class, "field_3", Modifier.PUBLIC).build();

when(classNameFactory.createTypeNameFrom(fieldDefinition_1, pluginContext)).thenReturn(get(String.class));
when(classNameFactory.createTypeNameFrom(fieldDefinition_2, pluginContext)).thenReturn(get(Integer.class));
when(fieldSpecFactory.createFieldSpecFor(fieldDefinition_1, classNameFactory, pluginContext)).thenReturn(fieldSpec_1);
when(fieldSpecFactory.createFieldSpecFor(fieldDefinition_2, classNameFactory, pluginContext)).thenReturn(fieldSpec_2);
when(fieldSpecFactory.createFieldSpecFor(fieldDefinition_3, classNameFactory, pluginContext)).thenReturn(fieldSpec_3);

final List<FieldSpec> fields = builderFieldFactory.createFields(
fieldDefinitions,
classNameFactory,
pluginContext);

assertThat(fields.size(), is(2));

assertThat(fields.get(0).name, is("fieldDefinition_1"));
assertThat(fields.get(0).modifiers.size(), is(1));
assertThat(fields.get(0).modifiers, hasItem(PRIVATE));
assertThat(fields.get(0).type.toString(), is("java.lang.String"));
assertThat(fields.size(), is(3));

assertThat(fields.get(1).name, is("fieldDefinition_2"));
assertThat(fields.get(1).modifiers.size(), is(1));
assertThat(fields.get(1).modifiers, hasItem(PRIVATE));
assertThat(fields.get(1).type.toString(), is("java.lang.Integer"));
assertThat(fields.get(0), is(fieldSpec_1));
assertThat(fields.get(1), is(fieldSpec_2));
assertThat(fields.get(2), is(fieldSpec_3));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public void shouldGenerateABuilderWithTheCorrectFieldsBuildMethodAndWithMethods(


when(additionalPropertiesDeterminer.shouldAddAdditionalProperties(classDefinition, pluginContext)).thenReturn(false);
when(builderMethodFactory.createTheBuildMethod(fieldDefinitions, pojoClassName)).thenReturn(buildMethod);
when(builderMethodFactory.createTheBuildMethod(fieldDefinitions, pojoClassName, pluginContext)).thenReturn(buildMethod);

final TypeSpec builder = builderGenerator.generate();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,23 @@
import uk.gov.justice.generation.pojo.plugin.PluginContext;

import java.util.List;
import java.util.Optional;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class BuilderMethodFactoryTest {

@Mock
private ClassNameFactory classNameFactory;

@InjectMocks
private BuilderMethodFactory builderMethodFactory;

Expand All @@ -33,18 +39,24 @@ public void shouldCreateTheBuildMethod() throws Exception {

final Definition fieldDefinition_1 = mock(Definition.class);
final Definition fieldDefinition_2 = mock(Definition.class);
final List<Definition> fieldDefinitions = asList(fieldDefinition_1, fieldDefinition_2);
final Definition fieldDefinition_3 = mock(Definition.class);
final List<Definition> fieldDefinitions = asList(fieldDefinition_1, fieldDefinition_2, fieldDefinition_3);
final PluginContext pluginContext = mock(PluginContext.class);

final ClassName pojoClassName = get("org.bloggs.fred", "AlcubierreDrive");
final ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(get(Optional.class), get(String.class));

when(fieldDefinition_1.getFieldName()).thenReturn("fieldDefinition_1");
when(fieldDefinition_2.getFieldName()).thenReturn("fieldDefinition_2");
when(fieldDefinition_3.getFieldName()).thenReturn("fieldDefinition_3");

final MethodSpec theBuildMethod = builderMethodFactory.createTheBuildMethod(fieldDefinitions, pojoClassName);
when(classNameFactory.createTypeNameFrom(fieldDefinition_3, pluginContext)).thenReturn(parameterizedTypeName);

final MethodSpec theBuildMethod = builderMethodFactory.createTheBuildMethod(fieldDefinitions, pojoClassName, pluginContext);

final String expectedBuildMethod =
"public org.bloggs.fred.AlcubierreDrive build() {\n " +
"return new org.bloggs.fred.AlcubierreDrive(fieldDefinition_1, fieldDefinition_2);\n" +
"return new org.bloggs.fred.AlcubierreDrive(fieldDefinition_1, fieldDefinition_2, Optional.ofNullable(fieldDefinition_3));\n" +
"}\n";

assertThat(theBuildMethod.toString(), is(expectedBuildMethod));
Expand Down Expand Up @@ -84,12 +96,13 @@ public void shouldCreateTheWithMethods() throws Exception {
final PluginContext pluginContext = mock(PluginContext.class);

final ClassName builderClassName = get("org.bloggs.fred", "AlcubierreDrive").nestedClass("Builder");
final ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(get(Optional.class), get(String.class));

when(fieldDefinition_1.getFieldName()).thenReturn("fieldDefinition_1");
when(fieldDefinition_2.getFieldName()).thenReturn("fieldDefinition_2");

when(classNameFactory.createTypeNameFrom(fieldDefinition_1, pluginContext)).thenReturn(get(String.class));
when(classNameFactory.createTypeNameFrom(fieldDefinition_2, pluginContext)).thenReturn(get(Integer.class));
when(classNameFactory.createTypeNameFrom(fieldDefinition_2, pluginContext)).thenReturn(parameterizedTypeName);

final List<MethodSpec> withMethods = builderMethodFactory.createTheWithMethods(
fieldDefinitions,
Expand All @@ -107,7 +120,7 @@ public void shouldCreateTheWithMethods() throws Exception {
"}\n";
final String expectedWithMethod_2 =
"public org.bloggs.fred.AlcubierreDrive.Builder withFieldDefinition_2(" +
"final java.lang.Integer fieldDefinition_2) {\n " +
"final java.lang.String fieldDefinition_2) {\n " +
"this.fieldDefinition_2 = fieldDefinition_2;\n " +
"return this;\n" +
"}\n";
Expand Down
Loading

0 comments on commit 423e9a5

Please sign in to comment.