Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
import org.javahelpers.simple.builders.processor.dtos.*;
import org.javahelpers.simple.builders.processor.exceptions.BuilderException;

import com.google.common.graph.ElementOrder.Type;

/** Class for creating a specific BuilderDefinitionDto for an annotated DTO class. */
public class BuilderDefinitionCreator {
private static final String BUILDER_SUFFIX = "Builder";
Expand Down Expand Up @@ -252,6 +254,22 @@ private static void addAdditionalHelperMethodsForField(
fieldName, "String.format(format, args)", annotations, builderType));
}

if ((fieldType instanceof TypeNameArray arrayType)) {
TypeName elementType = arrayType.getTypeOfArray();

// Add method accepting List<ElementType> and converting to array
TypeNameGeneric listType = new TypeNameGeneric(map2TypeName(List.class), elementType);
result.addMethod(
createFieldSetterForArrayFromList(fieldName, listType, elementType, builderType));

// Add Consumer<ArrayListBuilder<ElementType>> method
TypeName collectionBuilderType = map2TypeName(ArrayListBuilder.class);
result.addMethod(
createFieldConsumerWithArrayBuilder(
fieldName, collectionBuilderType, elementType, builderType));
return;
}

// Only process generic types (List, Set, Map, Optional, etc.)
if (!(fieldType instanceof TypeNameGeneric fieldTypeGeneric)) {
return;
Expand Down Expand Up @@ -855,6 +873,78 @@ private static MethodDto createStringFormatMethodWithTransform(
return methodDto;
}

/**
* Creates a field setter method that accepts a List and converts it to an array.
*
* @param fieldName the field name
* @param listType the List<ElementType> parameter type
* @param elementType the element type of the array
* @param builderType the builder type to return
* @return the method DTO for the setter
*/
private static MethodDto createFieldSetterForArrayFromList(
String fieldName, TypeName listType, TypeName elementType, TypeName builderType) {
MethodParameterDto parameter = new MethodParameterDto();
parameter.setParameterName(fieldName);
parameter.setParameterTypeName(listType);

MethodDto methodDto = new MethodDto();
methodDto.setMethodName(fieldName);
methodDto.setReturnType(builderType);
methodDto.addParameter(parameter);
methodDto.setModifier(Modifier.PUBLIC);
methodDto.setMethodType(MethodTypes.PROXY);
methodDto.setCode(
"""
this.$fieldName:N = $builderFieldWrapper:T.changedValue($dtoMethodParams:N.toArray(new $elementType:T[0]));
return this;
""");
methodDto.addArgument(ARG_FIELD_NAME, fieldName);
methodDto.addArgument(ARG_DTO_METHOD_PARAMS, fieldName);
methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TRACKED_VALUE_TYPE);
methodDto.addArgument("elementType", elementType);
return methodDto;
}

/**
* Creates a consumer method for array fields with ArrayListBuilder. This allows building arrays
* using the fluent ArrayListBuilder API.
*/
private static MethodDto createFieldConsumerWithArrayBuilder(
String fieldName,
TypeName collectionBuilderType,
TypeName elementType,
TypeName returnBuilderType) {
TypeNameGeneric builderTypeGeneric = new TypeNameGeneric(collectionBuilderType, elementType);
TypeNameGeneric consumerType =
new TypeNameGeneric(map2TypeName(Consumer.class), builderTypeGeneric);

MethodParameterDto parameter = new MethodParameterDto();
parameter.setParameterName(fieldName + BUILDER_SUFFIX + SUFFIX_CONSUMER);
parameter.setParameterTypeName(consumerType);

MethodDto methodDto = new MethodDto();
methodDto.setMethodName(fieldName);
methodDto.setReturnType(returnBuilderType);
methodDto.addParameter(parameter);
methodDto.setModifier(Modifier.PUBLIC);
methodDto.setMethodType(MethodTypes.CONSUMER_BY_BUILDER);

methodDto.setCode(
"""
$helperType:T builder = this.$fieldName:N.isSet() ? new $helperType:T(java.util.List.of(this.$fieldName:N.value())) : new $helperType:T();
$dtoMethodParam:N.accept(builder);
this.$fieldName:N = $builderFieldWrapper:T.changedValue(builder.build().toArray(new $elementType:T[0]));
return this;
""");
methodDto.addArgument(ARG_FIELD_NAME, fieldName);
methodDto.addArgument(ARG_DTO_METHOD_PARAM, parameter.getParameterName());
methodDto.addArgument(ARG_HELPER_TYPE, builderTypeGeneric);
methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TRACKED_VALUE_TYPE);
methodDto.addArgument("elementType", elementType);
return methodDto;
}

/**
* Checks if a StringBuilder consumer should be generated for the given field type. This applies
* to plain String fields and Optional&lt;String&gt; fields.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1681,7 +1681,7 @@ void shouldIgnoreIfAnnotatedWithIgnoreInBuilder() {
}

@Test
void shouldNotGenerateConsumersForPrimitiveAndArrayFields() {
void shouldNotGenerateConsumersForPrimitiveFields() {
// Given
String packageName = "test";
String className = "PrimAndArray";
Expand All @@ -1708,13 +1708,13 @@ void shouldNotGenerateConsumersForPrimitiveAndArrayFields() {
// Then
String generatedCode = loadGeneratedSource(compilation, builderClassName);
assertGenerationSucceeded(compilation, builderClassName, generatedCode);
// Expect simple setters and suppliers only; no consumer methods for primitive/array
// Primitives should not get consumer methods, but arrays now get ArrayListBuilder consumers
ProcessorAsserts.assertingResult(
generatedCode,
contains("public PrimAndArrayBuilder count(Supplier"),
contains("public PrimAndArrayBuilder names(Supplier"),
notContains("public PrimAndArrayBuilder count(Consumer"),
notContains("public PrimAndArrayBuilder names(Consumer"));
contains("public PrimAndArrayBuilder names(Consumer<ArrayListBuilder<String>>"));
}

@Test
Expand Down Expand Up @@ -1896,9 +1896,12 @@ void shouldHandleMixedFields() {
// primitive int: direct and supplier (boxed)
contains("public HasMixedBuilder a(int a)"),
contains("public HasMixedBuilder a(Supplier<Integer> aSupplier)"),
// array String[]: direct and supplier only (no varargs, no collection-builder consumers)
// array String[]: varargs, supplier, List parameter, and ArrayListBuilder consumer
contains("public HasMixedBuilder names(String... names)"),
contains("public HasMixedBuilder names(Supplier<String[]> namesSupplier)"),
contains("public HasMixedBuilder names(List<String> names)"),
contains(
"public HasMixedBuilder names(Consumer<ArrayListBuilder<String>> namesBuilderConsumer)"),
// List<String>: direct, supplier, varargs convenience and consumer with ArrayListBuilder
contains("public HasMixedBuilder list(List<String> list)"),
contains("public HasMixedBuilder list(Supplier<List<String>> listSupplier)"),
Expand Down Expand Up @@ -1937,14 +1940,14 @@ void shouldHandleObjectArrayField() {
// Then
String generatedCode = loadGeneratedSource(compilation, builderClassName);
assertGenerationSucceeded(compilation, builderClassName, generatedCode);
// Arrays should get setter and supplier
// Arrays should get setter, supplier, List parameter, and ArrayListBuilder consumer
ProcessorAsserts.assertingResult(
generatedCode,
contains("public HasObjectArrayBuilder names(Supplier<String[]> namesSupplier)"),
contains("public HasObjectArrayBuilder names(String... names)"),
notContains("public HasObjectArrayBuilder names(String[] names)"),
notContains("public HasObjectArrayBuilder names(List<String> names)"), // TODO: feature
notContains("Consumer<ArrayListBuilder"), // TODO: feature
contains("public HasObjectArrayBuilder names(List<String> names)"),
contains("Consumer<ArrayListBuilder<String>> namesBuilderConsumer"),
notContains("Consumer<HashSetBuilder"));
}

Expand Down
Loading