From a3383fb550e611a0ff6bddb52802e264501f0582 Mon Sep 17 00:00:00 2001 From: Ulysse Mavrocordatos Date: Wed, 8 Oct 2025 13:54:34 +0200 Subject: [PATCH] fix oneof methods name When there is a oneof with at least two similar type, ie List and List this cause some conflict. --- .generator/src/generator/cli.py | 1 + .generator/src/generator/formatter.py | 73 +++++++++++++++++++ .../src/generator/templates/modelOneOf.j2 | 28 ++++--- 3 files changed, 92 insertions(+), 10 deletions(-) diff --git a/.generator/src/generator/cli.py b/.generator/src/generator/cli.py index 6e340f7777c..c5f2cb227bf 100644 --- a/.generator/src/generator/cli.py +++ b/.generator/src/generator/cli.py @@ -68,6 +68,7 @@ def cli(specs, output): env.globals["get_default"] = openapi.get_default env.globals["get_container_type"] = openapi.get_container_type env.globals["get_security_names"] = openapi.get_security_names + env.globals["prepare_oneof_methods"] = formatter.prepare_oneof_methods api_j2 = env.get_template("Api.j2") model_j2 = env.get_template("model.j2") diff --git a/.generator/src/generator/formatter.py b/.generator/src/generator/formatter.py index adcb2a7daa5..583f5f89177 100644 --- a/.generator/src/generator/formatter.py +++ b/.generator/src/generator/formatter.py @@ -650,3 +650,76 @@ def get_response_type(schema, version): def attribute_path(attribute): return ".".join(attribute_name(a) for a in attribute.split(".")) + + +def prepare_oneof_methods(model, get_type_func): + """ + Pre-compute method information for oneOf types to handle erasure collisions. + + Returns a list of dicts with: + - schema: the original oneOf schema + - param_type: full parameterized type (e.g., "List") + - unparam_type: unparameterized type (e.g., "List") + - use_factory: True if factory method needed (collision detected) + - constructor_name: name for constructor/factory method + - getter_name: name for getter method + """ + # Handle both dict-style and object-style access + if isinstance(model, dict): + one_of = model.get('oneOf', []) + elif hasattr(model, 'oneOf'): + one_of = model.oneOf + elif hasattr(model, 'get'): + one_of = model.get('oneOf', []) + else: + return [] + + if not one_of: + return [] + + # First pass: count unparameterized types + unparam_counts = {} + for oneOf in one_of: + param_type = get_type_func(oneOf) + unparam_type = un_parameterize_type(param_type) + unparam_counts[unparam_type] = unparam_counts.get(unparam_type, 0) + 1 + + # Second pass: compute method names + result = [] + for oneOf in one_of: + param_type = get_type_func(oneOf) + unparam_type = un_parameterize_type(param_type) + has_collision = unparam_counts[unparam_type] > 1 + + # Compute constructor/factory method name + if has_collision: + if param_type.startswith('List<'): + inner_type = param_type[5:-1] + constructor_name = f"from{inner_type}List" + else: + safe_type = param_type.replace('<', '').replace('>', '').replace(' ', '').replace(',', '') + constructor_name = f"from{safe_type}" + else: + constructor_name = None # Regular constructor + + # Compute getter method name + if has_collision: + if param_type.startswith('List<'): + inner_type = param_type[5:-1] + getter_name = f"get{inner_type}List" + else: + safe_type = param_type.replace('<', '').replace('>', '').replace(' ', '').replace(',', '') + getter_name = f"get{safe_type}" + else: + getter_name = f"get{unparam_type}" + + result.append({ + 'schema': oneOf, + 'param_type': param_type, + 'unparam_type': unparam_type, + 'use_factory': has_collision, + 'constructor_name': constructor_name, + 'getter_name': getter_name, + }) + + return result diff --git a/.generator/src/generator/templates/modelOneOf.j2 b/.generator/src/generator/templates/modelOneOf.j2 index 1af07158b68..309bba65674 100644 --- a/.generator/src/generator/templates/modelOneOf.j2 +++ b/.generator/src/generator/templates/modelOneOf.j2 @@ -139,11 +139,20 @@ public class {{ name }} extends AbstractOpenApiSchema { super("oneOf", Boolean.{{ "TRUE" if model.nullable else "FALSE" }}); } - {%- for oneOf in model.oneOf %} - public {{ name }}({{ get_type(oneOf) }} o) { + {%- set oneof_methods = prepare_oneof_methods(model, get_type) %} + {%- for method_info in oneof_methods %} + {%- if method_info.use_factory %} + public static {{ name }} {{ method_info.constructor_name }}({{ method_info.param_type }} o) { + {{ name }} instance = new {{ name }}(); + instance.setActualInstance(o); + return instance; + } + {%- else %} + public {{ name }}({{ method_info.param_type }} o) { super("oneOf", Boolean.{{ "TRUE" if model.nullable else "FALSE" }}); setActualInstance(o); } + {%- endif %} {%- endfor %} static { @@ -204,19 +213,18 @@ public class {{ name }} extends AbstractOpenApiSchema { return super.getActualInstance(); } - {%- for oneOf in model.oneOf %} - {%- set dataType = get_type(oneOf) %} - {%- set unParameterizedDataType = get_type(oneOf)|un_parameterize_type %} + {%- set oneof_methods = prepare_oneof_methods(model, get_type) %} + {%- for method_info in oneof_methods %} /** - * Get the actual instance of `{{ dataType|escape_html }}`. If the actual instance is not `{{ dataType|escape_html }}`, + * Get the actual instance of `{{ method_info.param_type|escape_html }}`. If the actual instance is not `{{ method_info.param_type|escape_html }}`, * the ClassCastException will be thrown. * - * @return The actual instance of `{{ dataType|escape_html }}` - * @throws ClassCastException if the instance is not `{{ dataType|escape_html }}` + * @return The actual instance of `{{ method_info.param_type|escape_html }}` + * @throws ClassCastException if the instance is not `{{ method_info.param_type|escape_html }}` */ - public {{ dataType }} get{{ unParameterizedDataType }}() throws ClassCastException { - return ({{ dataType }})super.getActualInstance(); + public {{ method_info.param_type }} {{ method_info.getter_name }}() throws ClassCastException { + return ({{ method_info.param_type }})super.getActualInstance(); } {%- endfor %}