Skip to content

Default values for builder fields via @Default / @DefaultValue (#227) - #230

Merged
AndreasIgel merged 25 commits into
mainfrom
AndreasIgel/issue227
Aug 5, 2026
Merged

Default values for builder fields via @Default / @DefaultValue (#227)#230
AndreasIgel merged 25 commits into
mainfrom
AndreasIgel/issue227

Conversation

@AndreasIgel

Copy link
Copy Markdown
Collaborator

Summary

Implements default values for builder fields (issue #227): a field annotated with @Default (or any @Default/@DefaultValue with a String value()) gets that value applied in build() when the field was never set. Works for both record constructor parameters and class setter-fields.

@SimpleBuilder
public record Product(
    String name,
    double price,
    @Default("GENERAL") String category,   // -> "GENERAL"
    @Default("true") boolean active) {}     // -> true

ProductBuilder.create().name("Laptop").price(1500.0).build();
// category == "GENERAL", active == true

Why the existing implementation didn't cover this: the builder always constructs via the max-parameter (canonical) constructor and passed null/0 for unset fields, so a smaller delegating record constructor is never selected and there was no "use this when omitted" hook. This PR adds that hook without changing constructor selection.

How it works

  • Detection — framework-agnostic by simple name (Default, DefaultValue, any package, e.g. Jakarta REST @DefaultValue). Extracted in BuilderDefinitionCreator for both constructor params and the backing field element (FieldAnnotationExtractor), then formatted per field type (String -> quoted, char -> single-quoted, numeric/boolean -> as-is, complex -> raw expression).
  • ModelFieldDto gains defaultValue + isRequired() (a defaulted field is never required, even with @NotNull, so no validation error when unset).
  • CodegenCoreMethodsEnhancer.build():
    • constructor args: this.field.valueOr(<default>)
    • setter fields: this.field.ifSet(result::setX).orElse(<default>)
  • RuntimeTrackedValue gains valueOr(T) and a backward-compatible fluent ifSet(consumer).orElse(default) (existing ifSet(consumer); callers are unaffected):
public DefaultValueApplier<T> ifSet(Consumer<T> consumer) {
  if (isSet()) consumer.accept(value);
  return new DefaultValueApplier<>(isSet(), consumer);   // .orElse(default) no-ops if already applied
}

Docs / examples / tests

  • README "Default Values" usage section.
  • New example DTOs ProductWithDefaults (record) and OrderWithDefaults (class) + their committed generated builders.
  • DefaultValueTest (7 cases) and TrackedValueTest additions.
  • example/pom.xml: registers generated-example-builder as a compiled source root via build-helper-maven-plugin so the committed generated builders are compiled.

Full build green, 312 tests pass; the committed generated example builders regenerate byte-identical to a fresh mvn -pl example clean compile (generated-source gate passes).

AndreasIgel and others added 20 commits April 23, 2026 23:04
…or that it checks if there is a builder or a parameterless constructor
…method-definition and to map them afterwards to generation specific classes
…initionCreator, only on fallback cases it is in RoasterCodeGenerator
Bring the default-value feature up to date with current main (PR #229
JavaDoc-example generation + model refactor is now merged there).

Conflicts resolved by taking main's version of the JavaDoc-example /
model-refactor code and re-applying the default-value feature on top:
- TrackedValue.valueOr / ifSet(...).orElse(...) fluent default API
- FieldDto default-value field + isRequired()
- CoreMethodsEnhancer applies defaults in build()
- BuilderDefinitionCreator + FieldAnnotationExtractor extract @Default/@DefaultValue
- README Default Values section, example DTOs, DefaultValueTest

Generated example builders regenerated via 'mvn -pl example clean compile'
(CI-consistent, no fmt); BookDtoBuilder is byte-identical to main, only the
new OrderWithDefaultsBuilder/ProductWithDefaultsBuilder are added.

main's strict-mode, resilience and exception-isolation retained.

Co-Authored-By: Andreas Igel <andreas.igel@computacenter.com>
@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.18750% with 5 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...processor/processing/BuilderDefinitionCreator.java 88.88% 1 Missing and 2 partials ⚠️
...s/processor/analysis/FieldAnnotationExtractor.java 88.88% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Comment thread example/pom.xml Outdated
@sonarqubecloud

sonarqubecloud Bot commented Aug 5, 2026

Copy link
Copy Markdown

@AndreasIgel
AndreasIgel merged commit 2f9d677 into main Aug 5, 2026
8 checks passed
@AndreasIgel
AndreasIgel deleted the AndreasIgel/issue227 branch August 5, 2026 20:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant