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 @@ -63,6 +63,9 @@ public class BuilderDefinitionCreator {
private static final String ARG_BUILDER_FIELD_WRAPPER = "builderFieldWrapper";
private static final String ARG_HELPER_TYPE = "helperType";

// Type constants
private static final TypeName TRACKED_VALUE_TYPE = TypeName.of(TrackedValue.class);

// Parameter name suffixes
private static final String SUFFIX_CONSUMER = "Consumer";
private static final String SUFFIX_SUPPLIER = "Supplier";
Expand Down Expand Up @@ -230,7 +233,13 @@ private static boolean isMethodRelevantForBuilder(

private static void addAdditionalHelperMethodsForField(
FieldDto result, String fieldName, TypeName fieldType) {
// Only process generic types (List, Set, Map, etc.)
// Check for String type (not array) and add format method
if (isString(fieldType) && !(fieldType instanceof TypeNameArray)) {
result.addMethod(
createStringFormatMethodWithTransform(fieldName, "String.format(format, args)"));
}

// Only process generic types (List, Set, Map, Optional, etc.)
if (!(fieldType instanceof TypeNameGeneric fieldTypeGeneric)) {
return;
}
Expand All @@ -253,6 +262,18 @@ private static void addAdditionalHelperMethodsForField(
false);
result.addMethod(
createFieldSetterWithTransform(fieldName, "Map.ofEntries(%s)", mapEntryType));
} else if (isOptional(fieldType) && innerTypesCnt == 1) {
// Add setter that accepts the inner type T and wraps it in Optional.of()
result.addMethod(
createFieldSetterWithTransform(fieldName, "Optional.of(%s)", innerTypes.get(0)));

// If Optional<String>, add format method
TypeName innerType = innerTypes.get(0);
if (isString(innerType)) {
result.addMethod(
createStringFormatMethodWithTransform(
fieldName, "Optional.of(String.format(format, args))"));
}
}
}

Expand All @@ -271,6 +292,7 @@ private static void addConsumerMethodsForField(
if (isFunctionalInterface(fieldTypeElement)) {
return;
}

Optional<TypeName> builderTypeOpt = findBuilderType(fieldParameter, context);
if (builderTypeOpt.isPresent()) {
TypeName builderType = builderTypeOpt.get();
Expand Down Expand Up @@ -310,6 +332,11 @@ && hasEmptyConstructor(fieldTypeElement, context)) {
fieldName,
map2TypeName(HashSetBuilder.class),
fieldTypeGeneric.getInnerTypeArguments().get(0)));
} else if (shouldGenerateStringBuilderConsumer(fieldType)) {
// Generate StringBuilder consumer for String or Optional<String>
String transform =
isOptionalString(fieldType) ? "Optional.of(builder.toString())" : "builder.toString()";
result.addMethod(createStringBuilderConsumer(fieldName, transform));
}
}

Expand All @@ -319,6 +346,8 @@ private static void addSupplierMethodsForField(
if (isFunctionalInterface(fieldTypeElement)) {
return;
}

// For all fields including Optional<T>, use the real field type for suppliers
result.addMethod(createFieldSupplier(fieldName, fieldType));
}

Expand Down Expand Up @@ -444,7 +473,7 @@ private static MethodDto createFieldSetterWithTransform(
""");
methodDto.addArgument(ARG_FIELD_NAME, fieldName);
methodDto.addArgument(ARG_DTO_METHOD_PARAMS, params);
methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TypeName.of(TrackedValue.class));
methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TRACKED_VALUE_TYPE);
return methodDto;
}

Expand All @@ -468,7 +497,33 @@ private static MethodDto createFieldConsumer(String fieldName, TypeName fieldTyp
methodDto.addArgument(ARG_FIELD_NAME, fieldName);
methodDto.addArgument(ARG_DTO_METHOD_PARAM, parameter.getParameterName());
methodDto.addArgument(ARG_HELPER_TYPE, fieldType);
methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TypeName.of(TrackedValue.class));
methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TRACKED_VALUE_TYPE);
return methodDto;
}

private static MethodDto createStringBuilderConsumer(String fieldName, String transform) {
TypeName stringBuilderType = new TypeName("java.lang", "StringBuilder");
TypeNameGeneric consumerType =
new TypeNameGeneric(map2TypeName(Consumer.class), stringBuilderType);
MethodParameterDto parameter = new MethodParameterDto();
parameter.setParameterName(fieldName + "StringBuilderConsumer");
parameter.setParameterTypeName(consumerType);
MethodDto methodDto = new MethodDto();
methodDto.setMethodName(fieldName);
methodDto.addParameter(parameter);
methodDto.setModifier(Modifier.PUBLIC);
methodDto.setMethodType(MethodTypes.CONSUMER);
methodDto.setCode(
"""
StringBuilder builder = new StringBuilder();
$dtoMethodParam:N.accept(builder);
this.$fieldName:N = $builderFieldWrapper:T.changedValue($transform:N);
return this;
""");
methodDto.addArgument(ARG_FIELD_NAME, fieldName);
methodDto.addArgument(ARG_DTO_METHOD_PARAM, parameter.getParameterName());
methodDto.addArgument("transform", transform);
methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TRACKED_VALUE_TYPE);
return methodDto;
}

Expand Down Expand Up @@ -498,7 +553,7 @@ private static MethodDto createFieldConsumerWithBuilder(String fieldName, TypeNa
methodDto.addArgument(ARG_FIELD_NAME, fieldName);
methodDto.addArgument(ARG_DTO_METHOD_PARAM, parameter.getParameterName());
methodDto.addArgument(ARG_HELPER_TYPE, builderType);
methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TypeName.of(TrackedValue.class));
methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TRACKED_VALUE_TYPE);
return methodDto;
}

Expand All @@ -519,10 +574,56 @@ private static MethodDto createFieldSupplier(String fieldName, TypeName fieldTyp
""");
methodDto.addArgument(ARG_FIELD_NAME, fieldName);
methodDto.addArgument(ARG_DTO_METHOD_PARAM, parameter.getParameterName());
methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TypeName.of(TrackedValue.class));
methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TRACKED_VALUE_TYPE);
return methodDto;
}

private static MethodDto createStringFormatMethodWithTransform(
String fieldName, String transform) {
TypeName stringType = new TypeName("java.lang", "String");

MethodParameterDto formatParam = new MethodParameterDto();
formatParam.setParameterName("format");
formatParam.setParameterTypeName(stringType);

MethodParameterDto argsParam = new MethodParameterDto();
argsParam.setParameterName("args");
argsParam.setParameterTypeName(new TypeNameArray(TypeName.of(Object.class), false));

MethodDto methodDto = new MethodDto();
methodDto.setMethodName(fieldName);
methodDto.addParameter(formatParam);
methodDto.addParameter(argsParam);
methodDto.setModifier(Modifier.PUBLIC);
methodDto.setMethodType(MethodTypes.PROXY);
methodDto.setCode(
"""
this.$fieldName:N = $builderFieldWrapper:T.changedValue($transform:N);
return this;
""");
methodDto.addArgument(ARG_FIELD_NAME, fieldName);
methodDto.addArgument("transform", transform);
methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TRACKED_VALUE_TYPE);
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.
*
* @param fieldType the type of the field
* @return true if StringBuilder consumer should be generated, false otherwise
*/
private static boolean shouldGenerateStringBuilderConsumer(TypeName fieldType) {
// Check for plain String (not array)
if (isString(fieldType) && !(fieldType instanceof TypeNameArray)) {
return true;
}

// Check for Optional<String>
return isOptionalString(fieldType);
}

private static Optional<TypeName> findBuilderType(
VariableElement param, ProcessingContext context) {
TypeMirror typeOfParameter = param.asType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import org.apache.commons.lang3.Strings;
import org.javahelpers.simple.builders.processor.dtos.TypeName;
import org.javahelpers.simple.builders.processor.dtos.TypeNameGeneric;

/** Helperclass to extract insights from TypeName. */
public class TypeNameAnalyser {
Expand Down Expand Up @@ -76,4 +77,41 @@ public static boolean isList(TypeName typeName) {
return Strings.CI.equals(typeName.getPackageName(), JAVA_UTIL_PACKAGE)
&& Strings.CI.equals(typeName.getClassName(), "List");
}

/**
* Helper to check if the type is a {@code java.util.Optional}.
*
* @param typeName Type to be validated
* @return {@code true}, if it is a {@code java.util.Optional}
*/
public static boolean isOptional(TypeName typeName) {
return Strings.CI.equals(typeName.getPackageName(), JAVA_UTIL_PACKAGE)
&& Strings.CI.equals(typeName.getClassName(), "Optional");
}

/**
* Helper to check if the type is a {@code java.lang.String}.
*
* @param typeName Type to be validated
* @return {@code true}, if it is a {@code java.lang.String}
*/
public static boolean isString(TypeName typeName) {
return Strings.CI.equals(typeName.getPackageName(), "java.lang")
&& Strings.CI.equals(typeName.getClassName(), "String");
}

/**
* Checks if the field type is Optional&lt;String&gt;.
*
* @param fieldType the type of the field
* @return true if the type is Optional&lt;String&gt;, false otherwise
*/
public static boolean isOptionalString(TypeName fieldType) {
if (fieldType instanceof TypeNameGeneric fieldTypeGeneric
&& isOptional(fieldType)
&& fieldTypeGeneric.getInnerTypeArguments().size() == 1) {
return isString(fieldTypeGeneric.getInnerTypeArguments().get(0));
}
return false;
}
}
Loading
Loading