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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions