Skip to content

[Java] fix invalid valueOf() usage in enum templates - #24838

Open
jorgerod wants to merge 15 commits into
OpenAPITools:masterfrom
InditexTech:issue-20188-java-fix-inner-enum-template-v3
Open

jorgerod wants to merge 15 commits into
OpenAPITools:masterfrom
InditexTech:issue-20188-java-fix-inner-enum-template-v3

Conversation

@jorgerod

@jorgerod jorgerod commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in Git BASH)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • File the PR against the correct branch: master (upcoming 7.x.0 minor release - breaking changes with fallbacks), 8.0.x (breaking changes without fallbacks)
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

Technical Committee

@bbdouglas (2017/07) @sreeshas (2017/08) @jfiala (2017/08) @lukoyanov (2017/09) @cbornet (2017/09) @jeff9finger (2018/01) @karismann (2019/03) @Zomzog (2019/04) @lwlee2608 (2019/10) @martin-mfg (2023/08)

Description

This supersedes #21055, which had become unmergeable (conflicting with master) and whose author is no longer available. As requested by @wing328 (comment), this is a fresh PR based on the current master. Full credit to @timon-sbr and @martin-mfg (kept as co-authors of the original commit).

Scope update: after an early review, the PR was narrowed down to the Java restclient, resttemplate and webclient libraries. Those are the only generators that consume the shared Java/modelEnum.mustache and Java/modelInnerEnum.mustache templates; every other Java generator (microprofile, native, okhttp-gson, helidon, micronaut, JaxRS/cxf, Groovy...) uses its own copies under Java/libraries/, which are unchanged in this PR.

Problem

In Java/modelInnerEnum.mustache, the Java enum templates wrapped every enum value with {{dataType}}.valueOf(...):

{{{name}}}({{^isUri}}{{dataType}}.valueOf({{/isUri}}{{{value}}}{{^isUri}}){{/isUri}})

valueOf() is not available for every possible data type, which produces code that does not compile:

  • BigDecimal has no BigDecimal.valueOf(BigDecimal) — e.g. type: number enums.

  • UUID has no valueOf either. AbstractJavaCodegen.toEnumValue already returns UUID.fromString("...") for UUID enums, so the template emitted UUID.valueOf(UUID.fromString(...)). This mirrors URI, which was already excluded.

  • Object has no valueOf at all — e.g. an inline enum: without an explicit type, or a type: string enum with additionalProperties: false, which currently map to Object:

    public enum OrderStatusEnum {
      PENDING(Object.valueOf("PENDING")),   // does not compile
      PROCESSING(Object.valueOf("PROCESSING"));

Fix

Two changes in each of the two shared templates:

  1. Constructor (modelInnerEnum.mustache only): valueOf() is now only emitted for types that actually provide it, i.e. it is skipped for isUri, isUuid, isNumeric and isFreeFormObject:

    {{{name}}}({{^isUri}}{{^isUuid}}{{^isNumeric}}{{^isFreeFormObject}}{{dataType}}.valueOf({{/isFreeFormObject}}{{/isNumeric}}{{/isUuid}}{{/isUri}}{{{value}}})

    (As a visible consequence, numeric enums now emit NUMBER_1(1) instead of Integer.valueOf(1); Boolean/String keep using valueOf(), so this is non-breaking. The wrapper was originally added to fix Boolean enums, [JAVA] fix inner enums of booleans #19815.)

  2. fromValue (both templates): numeric enums (isNumber, e.g. BigDecimal) are now compared with compareTo() instead of equals(), because BigDecimal.equals is scale-sensitive (1 != 1.0 numerically identical values would throw IllegalArgumentException at deserialization):

    {{^isString}}{{^isNumber}}b.value.equals(value){{/isNumber}}{{#isNumber}}value != null && b.value.compareTo(value) == 0{{/isNumber}}{{/isString}}

    For free-form-object enums (isFreeFormObject), the raw equals() comparison is kept.

  3. JSON-B serializer (both templates): generator.write(obj.value) does not compile for enums backed by Object (jakarta.json.stream.JsonGenerator has no write(Object) overload); the value is now written as String.valueOf(obj.value) when isFreeFormObject.

Only modelEnum.mustache and modelInnerEnum.mustache (the shared templates used by restclient, resttemplate and webclient) are modified.

Test coverage

The shared petstore test spec (petstore-with-fake-endpoints-models-for-testing.yaml) gained:

  • Order.paymentMethod — a numeric (type: number) enum, covering the BigDecimal case.
  • Order.OrderStatus — a type: string enum with additionalProperties: false, covering the Object case.
  • enum_form_integer / enum_form_double form parameters on testEnumParameters.

Samples were regenerated for the in-scope generators; all changes are limited to generated EnumTest model files.

This PR closes #20188 and replaces #21055.

The Java enum templates wrapped every enum value with `{{dataType}}.valueOf(...)`.
That method does not exist for every possible data type (e.g. `BigDecimal` has no
`BigDecimal.valueOf(BigDecimal)` and `Object` has no `valueOf` at all), producing
code that does not compile.

`valueOf()` is now only emitted for types where it actually exists, i.e. it is
skipped for `isUri`, `isNumeric` and `isFreeFormObject` enum values. The wrapper
was originally introduced to fix Boolean enums (OpenAPITools#19815), which keeps working.

Templates updated:
- Java/modelInnerEnum.mustache
- Java/libraries/microprofile/enumClass.mustache
- JavaJaxRS/{spec,cxf,cxf-cdi,cxf-ext}/enumClass.mustache
- java-helidon/client/libraries/{mp,se}/enumClass.mustache
- java-helidon/server/libraries/mp/enumClass.mustache

Test coverage added to the petstore test specs: a numeric enum (`Order.paymentMethod`),
a string enum with `additionalProperties: false` (`Order.OrderStatus`) and integer /
double form enum parameters.

Fixes OpenAPITools#20188

Co-authored-by: Timon Link <timon.link@sbroker.de>
Co-authored-by: Martin <2026226+martin-mfg@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 265 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/client/petstore/java-helidon-client/v3/mp/src/main/java/org/openapitools/client/model/Order.java">

<violation number="1" location="samples/client/petstore/java-helidon-client/v3/mp/src/main/java/org/openapitools/client/model/Order.java:66">
P2: The newly added numeric inner enum PaymentMethodEnum cannot round-trip its wire value. Its constant names NUMBER_1/NUMBER_2 diverge from the spec values "1"/"2", and the java-helidon enumClass.mustache template emits no @JsonValue/@JsonCreator, so Jackson's name-based default serializes PaymentMethodEnum.NUMBER_1 as "NUMBER_1" and throws on deserializing "1". The standalone numeric enum OuterEnumInteger in the same sample carries @JsonValue/@JsonCreator and round-trips correctly, so the two are inconsistent. Add the @JsonValue/@JsonCreator (fromValue) pattern to the inner enum template, matching enumClass/standalone enums, so the new numeric enum test case is functional rather than compile-only.</violation>
</file>

Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.

Re-trigger cubic

Comment thread samples/client/petstore/ruby-autoload/lib/petstore/models/order.rb Outdated
Comment thread samples/client/petstore/crystal/src/petstore/models/order.cr Outdated
@jorgerod
jorgerod marked this pull request as draft September 2, 2026 12:54
jorgerod and others added 5 commits September 2, 2026 15:38
- Exclude `isUuid` from the `valueOf()` wrapper. `AbstractJavaCodegen.toEnumValue`
  already returns `UUID.fromString("...")` for UUID enums, so the templates were
  emitting `UUID.valueOf(UUID.fromString(...))`, which does not compile. This is
  the same class of bug this PR fixes and mirrors the existing `isUri` handling.
  Regenerating all 800 samples produces no output change, as no test fixture
  currently declares a UUID enum.

- Revert the Ameba version change in the Crystal sample. It was edited in the
  generated `shard.yml` only, while the value comes from `crystal/shard.mustache`,
  so it would have been reverted by `Samples up-to-date`. The Ameba `1.7.0-dev`
  resolution failure is a pre-existing breakage unrelated to this PR.

- Drop the `FakeApiTest.java` entries that were added to `.openapi-generator/FILES`.
  The generator never overwrites existing test files, so those entries are not
  reproducible on regeneration and would have failed `Samples up-to-date`.
@jorgerod
jorgerod marked this pull request as ready for review September 2, 2026 15:17

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 292 files

Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.

Re-trigger cubic

Comment thread modules/openapi-generator/src/main/resources/JavaJaxRS/cxf/enumClass.mustache Outdated
Comment thread modules/openapi-generator/src/main/resources/JavaJaxRS/cxf-cdi/enumClass.mustache Outdated
…withXml

Two additional compile failures surfaced by the new PaymentMethodEnum (BigDecimal)
and OrderStatusEnum (Object) test fixtures, both unrelated to the valueOf() fix
but exposed by the same regression tests:

1. JSON-B serializer: `generator.write(obj.value)` requires an overload matching
   the static type of `obj.value`. `jakarta.json.stream.JsonGenerator` has no
   `write(Object)` overload, so any enum backed by a free-form/Object dataType
   (e.g. OrderStatusEnum, `additionalProperties: false`) failed to compile with:

     no suitable method found for write(java.lang.Object)

   Fixed by writing `String.valueOf(obj.value)` when `isFreeFormObject`, in every
   template with this JSON-B `Serializer` (Java, java-helidon client/server,
   microprofile, for both inner and standalone enums).

2. `@XmlEnumValue`: the annotation requires a compile-time constant `String`.
   The existing quoting logic only handles `isInteger`/`isDouble`/`isLong`/
   `isFloat`. For any other numeric format (plain `type: number`, dataType
   `BigDecimal`), `{{{value}}}` is `new BigDecimal("1")` — an expression, not a
   constant — which is invalid as an annotation argument:

     expression not allowed as annotation value

   The same applies to `isUri` (`URI.create(...)`) and `isUuid`
   (`UUID.fromString(...)`), which were never exercised with `withXml` before.
   Fixed by omitting `@XmlEnumValue` for `isUri`/`isUuid`/`isNumber` enums;
   JAXB falls back to the enum constant name, which still compiles.

Regenerating all 800 samples changes only the 3 previously-broken outputs
(microprofile-rest-client, microprofile-rest-client-3.0, resttemplate-withXml);
all three now compile (`mvn compile`).

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 23 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread modules/openapi-generator/src/main/resources/Java/modelEnum.mustache Outdated
Comment thread modules/openapi-generator/src/main/resources/Java/modelInnerEnum.mustache Outdated
Comment thread modules/openapi-generator/src/main/resources/JavaJaxRS/cxf-ext/enumClass.mustache Outdated
Resolve conflicts: take master's javaStringLiteral-based @XmlEnumValue
and keep this PR's valueOf guards (^isUuid ^isNumeric ^isFreeFormObject)
in the Java/microprofile/JavaJaxRS/helidon/micronaut enum templates.
Restored master's @nullable handling in regenerated resttemplate samples
while keeping the new enum_form_integer/enum_form_double parameters.
@jorgerod
jorgerod marked this pull request as draft September 24, 2026 12:30
The shared Java/modelEnum.mustache and Java/modelInnerEnum.mustache are
consumed by the restclient, resttemplate and webclient libraries only;
every other Java generator uses its own copies under libraries/.

This reverts all other template changes (microprofile, native, JaxRS,
helidon, micronaut, okhttp-gson, Groovy), the shared petstore test-spec
changes and all out-of-scope sample churn so the PR stays reviewable and
CI-green. Verification for the remaining two templates is covered by the
generated samples of the in-scope libraries when regenerated, and by the
existing model enum unit tests.
The previous refactor left '(value)' outside the isString section, so
non-string enums rendered invalid code like 'if ((value)|| ...)'
(OuterEnumInteger) or 'if (b.value.equals)' (StringEnumRef). Rewritten
with mutually-exclusive mustache sections: string branch keeps master's
equals/[IgnoreCase]/(value), non-string renders equals or compareTo for
numeric types, and free-form object enums use equals.
@jorgerod
jorgerod marked this pull request as ready for review September 24, 2026 15:18

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 39 files

Re-trigger cubic

This branch has not been deployed

No deployments
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.

[BUG][JAVA] Inner Enum generation with BigDecimal values results in compilation error

1 participant