Skip to content

Add opt-in mapper/transform setters (mapX(UnaryOperator<T>)) #223

Description

@AndreasIgel

Feature: mapper / transform setters (mapX(UnaryOperator<T>))

Summary

Add an optional, per-field mapper method that transforms the value currently set in the builder by applying a function to it, instead of overwriting it. This complements the existing setters (x(value), x(Supplier), x(Consumer)), and pairs naturally with the With copy-and-modify flow.

Motivation

Adjusting a value relative to its current state (trim, clamp, increment, uppercase, default-if-null) currently requires reading the value out, transforming it, and setting it again. A mapX method makes this a single fluent step — a capability FreeBuilder offers via mapProperty(UnaryOperator).

PersonBuilder.create()
    .name("  bob ")
    .mapName(String::trim)          // "bob"
    .mapName(String::toUpperCase)   // "BOB"
    .build();

orderBuilder.quantity(10).mapQuantity(q -> q * 2);  // 20

Proposed generated shape

For each field x of type T, generate:

public XBuilder mapName(UnaryOperator<String> op) {
  if (!this.name.isSet()) {
    throw new IllegalStateException("Cannot map 'name' before it is set");
  }
  this.name = changedValue(op.apply(this.name.value()));
  return this;
}

Behavior when the field is unset — decided

Calling mapX before the field has been set throws IllegalStateException with a clear message ("Cannot map 'name' before it is set").

Rationale:

  • Fail-fast at the exact call site rather than producing a surprising null passed into the user's function (which would NPE later and be hard to trace).
  • Consistent with SB's existing style — build() already throws IllegalStateException for unset required fields.
  • Matches FreeBuilder's behavior for required fields.

A lenient variant is explicitly out of scope for the first cut; if wanted later, add it as a separate, clearly-named method (e.g. mapNameIfSet(...) that no-ops when unset) rather than overloading mapName with surprising behavior.

Scope / design notes

  • Opt-in. Introduce a new option in CompilerArgumentsEnum (e.g. generateMapperHelpers), off by default, consistent with the other helper-generation flags (generateVarArgsHelpers, generateStringFormatHelpers, generateAddToCollectionHelpers, ...). Also expose it via SimpleBuilder.Options and document under Helper Methods in docs/CONFIGURATION.md.
  • Implement as a method generator component so it participates in the existing generator registry / component-filtering (deactivateGenerationComponents).
  • Uses the existing TrackedValue model (isSet(), value(), changedValue(...)).
  • Applies to all field kinds SB already generates setters for (constructor fields and setter fields).

Acceptance criteria

  • New opt-in option generates mapX(UnaryOperator<T>) for each eligible field.
  • mapX throws IllegalStateException with a clear message when the field is unset.
  • Off by default; no change to generated output unless enabled.
  • Documented under Helper Methods in docs/CONFIGURATION.md.
  • Tests: transform on a set value; throw on unset value; interaction with With copy-and-modify.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestfuture-ideaLonger-term idea, not scheduled work

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions