From 2a12c0f5a31ddd68b28b0bee62c258f404384d21 Mon Sep 17 00:00:00 2001 From: MrMaydo <89995856+MrMaydo@users.noreply.github.com> Date: Mon, 5 Jan 2026 20:56:10 +0100 Subject: [PATCH 1/5] add function to modify value to java constant --- src/enum_generator.py | 11 +++++++++++ tests/test_enum_generator.py | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/enum_generator.py create mode 100644 tests/test_enum_generator.py diff --git a/src/enum_generator.py b/src/enum_generator.py new file mode 100644 index 0000000..7793ba5 --- /dev/null +++ b/src/enum_generator.py @@ -0,0 +1,11 @@ +import re + + +def to_java_constant(value: str) -> str: + value = re.sub(r"[^A-Za-z0-9]", "_", value) # delimiters a-a -> A_A + value = re.sub(r"([a-z])[_]?([A-Z])([A-Z])([a-z])", r"\1_\2_\3\4", value) # aBCd / a_BCd-> a_B_CD + value = re.sub(r"([a-z])([A-Z])", r"\1_\2", value) # aA -> A_A + value = re.sub(r"([A-Za-z])([0-9])", r"\1_\2", value) # a9 / A9 -> a_9 + value = re.sub(r"([0-9])([A-Za-z])", r"\1_\2", value) # 9a / 9A -> 9_A + + return value.upper() diff --git a/tests/test_enum_generator.py b/tests/test_enum_generator.py new file mode 100644 index 0000000..b6d223d --- /dev/null +++ b/tests/test_enum_generator.py @@ -0,0 +1,23 @@ +from src.enum_generator import to_java_constant + + +def test_to_java_constant(): + values_expected = { + "kit": "KIT", + "KIT": "KIT", + "kitKat": "KIT_KAT", + "KitKat": "KIT_KAT", + "kit-kat": "KIT_KAT", + "kit-Kat": "KIT_KAT", + "kit_kat": "KIT_KAT", + "kit_Kat": "KIT_KAT", + "kit9": "KIT_9", + "kit-9": "KIT_9", + "kit_9": "KIT_9", + "kit92": "KIT_92", + "KiT": "KI_T", + "KiTKat": "KI_T_KAT", + "KitKAT": "KIT_KAT" + } + for key in values_expected.keys(): + assert to_java_constant(key) == values_expected[key] From 2236baf3df2097a7519a20862fab55d2babeb1fc Mon Sep 17 00:00:00 2001 From: MrMaydo <89995856+MrMaydo@users.noreply.github.com> Date: Tue, 6 Jan 2026 21:18:50 +0100 Subject: [PATCH 2/5] fix set_package function --- src/header_generator.py | 4 ++-- tests/test_header_generator.py | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/header_generator.py b/src/header_generator.py index 416b478..ba1487f 100644 --- a/src/header_generator.py +++ b/src/header_generator.py @@ -2,7 +2,7 @@ def set_package(package: str) -> str: - PACKAGE_REGEX = r"^(?:[a-z_][a-z0-9_]*)(?:\.(?:[a-z_][a-z0-9_]*))*$" + PACKAGE_REGEX = r"^(?:[A-Za-z_][A-Za-z0-9_]*)(?:\.(?:[A-Za-z_][A-Za-z0-9_]*))*$" if not re.match(PACKAGE_REGEX, package): raise ValueError(f"Invalid package: '{package}'") - return f"package {package}" + return f"package {package};" diff --git a/tests/test_header_generator.py b/tests/test_header_generator.py index 5347846..4bba937 100644 --- a/tests/test_header_generator.py +++ b/tests/test_header_generator.py @@ -5,13 +5,17 @@ def test_set_package(): example = "org.example.hyphenated_name" - expected = f"package {example}" + expected = f"package {example};" assert set_package(example) == expected example_2 = "com.example._123name" - expected_2 = f"package {example_2}" + expected_2 = f"package {example_2};" assert set_package(example_2) == expected_2 + example_3 = "com.exAmple.Name" + expected_3 = f"package {example_3};" + assert set_package(example_3) == expected_3 + def test_set_package_illegal_name(): example = "org.example.hyphenated-name" From 435d5106580f1348d9ae1b9a51a8e1d9a7472a52 Mon Sep 17 00:00:00 2001 From: MrMaydo <89995856+MrMaydo@users.noreply.github.com> Date: Wed, 7 Jan 2026 10:16:31 +0100 Subject: [PATCH 3/5] add enum generation --- src/enum_generator.py | 129 +++++++++++++++++++++++++++++++++++ tests/enum_reference_data.py | 102 +++++++++++++++++++++++++++ tests/reference_data.py | 2 +- tests/test_enum_generator.py | 8 ++- 4 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 tests/enum_reference_data.py diff --git a/src/enum_generator.py b/src/enum_generator.py index 7793ba5..c6d8744 100644 --- a/src/enum_generator.py +++ b/src/enum_generator.py @@ -1,4 +1,20 @@ import re +from dataclasses import dataclass +from typing import List, Optional + +from src.header_generator import set_package + + +@dataclass +class EnumClass: + name: str + values: List[str] + description: Optional[str] = None + + +indent_lvl1 = " " +indent_lvl2 = indent_lvl1 * 2 +indent_lvl3 = indent_lvl1 * 3 def to_java_constant(value: str) -> str: @@ -9,3 +25,116 @@ def to_java_constant(value: str) -> str: value = re.sub(r"([0-9])([A-Za-z])", r"\1_\2", value) # 9a / 9A -> 9_A return value.upper() + + +def generate_enum_class(enum_class: EnumClass, package: str) -> str: + enum_body = [ + set_package(package), + "", + f"import java.util.HashMap;", + f"import java.util.Map;", + f"" + ] + + enum_body.extend(_get_javadoc(enum_class.description)) + + enum_body.append(f"public enum {enum_class.name} {{") + enum_body.extend(_get_constants(enum_class.values)) + + enum_body.append(f"") + enum_body.append(f"{indent_lvl1}private final static Map CONSTANTS = new HashMap();") + enum_body.append(f"") + enum_body.extend(_get_static_method(enum_class.name)) + + enum_body.append(f"") + enum_body.append(f"{indent_lvl1}private final String value;") + enum_body.append(f"") + enum_body.extend(_get_constructor(enum_class.name)) + + enum_body.append(f"") + enum_body.extend(_get_fromValue_method(enum_class.name)) + + enum_body.append(f"") + enum_body.extend(_get_toString_method()) + + enum_body.append(f"") + enum_body.extend(_get_value_method()) + enum_body.append("}") + enum_body.append(f"") + + return "\n".join(enum_body) + + +def _get_javadoc(description: str) -> List[str]: + javadoc = [""] + if description is not None: + javadoc = [ + "", + "/**", + f" * {description}", + " */" + ] + return javadoc + + +def _get_constants(constants: List[str]) -> List[str]: + values = [] + for i, value in enumerate(constants): + line_end = ";" if i == (len(constants) - 1) else "," + values.append( + f'{indent_lvl1}{to_java_constant(value)}("{value}"){line_end}' + ) + return values + + +def _get_static_method(class_name: str) -> List[str]: + body = [ + f"{indent_lvl1}static {{", + f"{indent_lvl2}for ({class_name} c : values()) {{", + f"{indent_lvl3}CONSTANTS.put(c.value, c);", + f"{indent_lvl2}}}", + f"{indent_lvl1}}}" + ] + return body + + +def _get_constructor(class_name: str) -> List[str]: + body = [ + f"{indent_lvl1}{class_name}(String value) {{", + f"{indent_lvl2}this.value = value;", + f"{indent_lvl1}}}" + ] + return body + + +def _get_fromValue_method(class_name: str) -> List[str]: + body = [ + f"{indent_lvl1}public static {class_name} fromValue(String value) {{", + f"{indent_lvl2}{class_name} constant = CONSTANTS.get(value);", + f"{indent_lvl2}if (constant == null) {{", + f"{indent_lvl3}throw new IllegalArgumentException(value);", + f"{indent_lvl2}}} else {{", + f"{indent_lvl3}return constant;", + f"{indent_lvl2}}}", + f"{indent_lvl1}}}" + ] + return body + + +def _get_toString_method() -> List[str]: + body = [ + f"{indent_lvl1}@Override", + f"{indent_lvl1}public String toString() {{", + f"{indent_lvl2}return this.value;", + f"{indent_lvl1}}}" + ] + return body + + +def _get_value_method() -> List[str]: + body = [ + f"{indent_lvl1}public String value() {{", + f"{indent_lvl2}return this.value;", + f"{indent_lvl1}}}" + ] + return body diff --git a/tests/enum_reference_data.py b/tests/enum_reference_data.py new file mode 100644 index 0000000..2a2d1e4 --- /dev/null +++ b/tests/enum_reference_data.py @@ -0,0 +1,102 @@ +from src.enum_generator import EnumClass + +enum_AttributeEnum = EnumClass( + name="AttributeEnum", + values=["Actual", "Target", "MinSet", "MaxSet"], + description="Type of attribute: Actual, Target, MinSet, MaxSet.") + +enum_CancelReservationStatusEnum = EnumClass( + name="CancelReservationStatusEnum", + values=["Accepted"]) + +expected_AttributeEnum = """package ocpp.msgDef.Enumerations; + +import java.util.HashMap; +import java.util.Map; + + +/** + * Type of attribute: Actual, Target, MinSet, MaxSet. + */ +public enum AttributeEnum { + ACTUAL("Actual"), + TARGET("Target"), + MIN_SET("MinSet"), + MAX_SET("MaxSet"); + + private final static Map CONSTANTS = new HashMap(); + + static { + for (AttributeEnum c : values()) { + CONSTANTS.put(c.value, c); + } + } + + private final String value; + + AttributeEnum(String value) { + this.value = value; + } + + public static AttributeEnum fromValue(String value) { + AttributeEnum constant = CONSTANTS.get(value); + if (constant == null) { + throw new IllegalArgumentException(value); + } else { + return constant; + } + } + + @Override + public String toString() { + return this.value; + } + + public String value() { + return this.value; + } +} +""" + +expected_CancelReservationStatusEnum = """package ocpp.anotherDef.Enums; + +import java.util.HashMap; +import java.util.Map; + + +public enum CancelReservationStatusEnum { + ACCEPTED("Accepted"); + + private final static Map CONSTANTS = new HashMap(); + + static { + for (CancelReservationStatusEnum c : values()) { + CONSTANTS.put(c.value, c); + } + } + + private final String value; + + CancelReservationStatusEnum(String value) { + this.value = value; + } + + public static CancelReservationStatusEnum fromValue(String value) { + CancelReservationStatusEnum constant = CONSTANTS.get(value); + if (constant == null) { + throw new IllegalArgumentException(value); + } else { + return constant; + } + } + + @Override + public String toString() { + return this.value; + } + + public String value() { + return this.value; + } +} +""" diff --git a/tests/reference_data.py b/tests/reference_data.py index c529088..bbfb810 100644 --- a/tests/reference_data.py +++ b/tests/reference_data.py @@ -32,4 +32,4 @@ expected_setCustomData_CustomObject = """ public void setCustomData(CustomObject customData) { this.customData = customData; - }""" \ No newline at end of file + }""" diff --git a/tests/test_enum_generator.py b/tests/test_enum_generator.py index b6d223d..a6e6de4 100644 --- a/tests/test_enum_generator.py +++ b/tests/test_enum_generator.py @@ -1,4 +1,5 @@ -from src.enum_generator import to_java_constant +from src.enum_generator import to_java_constant, generate_enum_class +from tests.enum_reference_data import * def test_to_java_constant(): @@ -21,3 +22,8 @@ def test_to_java_constant(): } for key in values_expected.keys(): assert to_java_constant(key) == values_expected[key] + + +def test_generate_enum_class(): + assert generate_enum_class(enum_AttributeEnum, "ocpp.msgDef.Enumerations") == expected_AttributeEnum + assert generate_enum_class(enum_CancelReservationStatusEnum, "ocpp.anotherDef.Enums") == expected_CancelReservationStatusEnum From b738d5d6538ab549c1bab4310fe7c0018bd38507 Mon Sep 17 00:00:00 2001 From: MrMaydo <89995856+MrMaydo@users.noreply.github.com> Date: Wed, 7 Jan 2026 18:57:19 +0100 Subject: [PATCH 4/5] create_java model file --- src/enum_generator.py | 29 ++++--------- ..._generator.py => java_method_generator.py} | 42 +++---------------- src/java_model.py | 38 +++++++++++++++++ tests/reference_data.py | 2 +- ...rator.py => test_java_method_generator.py} | 2 +- 5 files changed, 55 insertions(+), 58 deletions(-) rename src/{method_generator.py => java_method_generator.py} (78%) create mode 100644 src/java_model.py rename tests/{test_method_generator.py => test_java_method_generator.py} (99%) diff --git a/src/enum_generator.py b/src/enum_generator.py index c6d8744..5dc43b2 100644 --- a/src/enum_generator.py +++ b/src/enum_generator.py @@ -1,22 +1,10 @@ import re -from dataclasses import dataclass -from typing import List, Optional +from typing import List +from src.java_model import EnumClass, indent_lvl1, indent_lvl2, indent_lvl3 from src.header_generator import set_package -@dataclass -class EnumClass: - name: str - values: List[str] - description: Optional[str] = None - - -indent_lvl1 = " " -indent_lvl2 = indent_lvl1 * 2 -indent_lvl3 = indent_lvl1 * 3 - - def to_java_constant(value: str) -> str: value = re.sub(r"[^A-Za-z0-9]", "_", value) # delimiters a-a -> A_A value = re.sub(r"([a-z])[_]?([A-Z])([A-Z])([a-z])", r"\1_\2_\3\4", value) # aBCd / a_BCd-> a_B_CD @@ -34,7 +22,7 @@ def generate_enum_class(enum_class: EnumClass, package: str) -> str: f"import java.util.HashMap;", f"import java.util.Map;", f"" - ] + ] enum_body.extend(_get_javadoc(enum_class.description)) @@ -42,7 +30,8 @@ def generate_enum_class(enum_class: EnumClass, package: str) -> str: enum_body.extend(_get_constants(enum_class.values)) enum_body.append(f"") - enum_body.append(f"{indent_lvl1}private final static Map CONSTANTS = new HashMap();") + enum_body.append( + f"{indent_lvl1}private final static Map CONSTANTS = new HashMap();") enum_body.append(f"") enum_body.extend(_get_static_method(enum_class.name)) @@ -52,10 +41,10 @@ def generate_enum_class(enum_class: EnumClass, package: str) -> str: enum_body.extend(_get_constructor(enum_class.name)) enum_body.append(f"") - enum_body.extend(_get_fromValue_method(enum_class.name)) + enum_body.extend(_get_from_value_method(enum_class.name)) enum_body.append(f"") - enum_body.extend(_get_toString_method()) + enum_body.extend(_get_to_string_method()) enum_body.append(f"") enum_body.extend(_get_value_method()) @@ -107,7 +96,7 @@ def _get_constructor(class_name: str) -> List[str]: return body -def _get_fromValue_method(class_name: str) -> List[str]: +def _get_from_value_method(class_name: str) -> List[str]: body = [ f"{indent_lvl1}public static {class_name} fromValue(String value) {{", f"{indent_lvl2}{class_name} constant = CONSTANTS.get(value);", @@ -121,7 +110,7 @@ def _get_fromValue_method(class_name: str) -> List[str]: return body -def _get_toString_method() -> List[str]: +def _get_to_string_method() -> List[str]: body = [ f"{indent_lvl1}@Override", f"{indent_lvl1}public String toString() {{", diff --git a/src/method_generator.py b/src/java_method_generator.py similarity index 78% rename from src/method_generator.py rename to src/java_method_generator.py index 6e68abb..ca311b2 100644 --- a/src/method_generator.py +++ b/src/java_method_generator.py @@ -1,38 +1,8 @@ import re -from dataclasses import dataclass -from typing import List, Optional - -JAVA_KEYWORDS = { - "abstract", "assert", "boolean", "break", "byte", "case", "catch", - "char", "class", "const", "continue", "default", "do", "double", - "else", "enum", "extends", "final", "finally", "float", "for", - "goto", "if", "implements", "import", "instanceof", "int", - "interface", "long", "native", "new", "package", "private", - "protected", "public", "return", "short", "static", "strictfp", - "super", "switch", "synchronized", "this", "throw", "throws", - "transient", "try", "void", "volatile", "while" -} - -JAVA_BUILTIN_TYPES = { - "Boolean", "Byte", "Character", "Double", "Float", "Integer", "List", "Long", "Short", - "Class", "Object", "String", "Void" -} - -JAVA_LITERALS = { - "null", "true", "false" -} - -indent_lvl1 = " " -indent_lvl2 = indent_lvl1 * 2 -indent_lvl3 = indent_lvl1 * 3 -return_indent = " " - - -@dataclass -class Field: - name: str - type: str - description: Optional[str] = None +from typing import List + +from src.java_model import JAVA_KEYWORDS, JAVA_BUILTIN_TYPES, JAVA_LITERALS, indent_lvl1, indent_lvl2, indent_lvl3, \ + return_indent, Field def generate_fields_block(fields: List[Field]) -> str: @@ -129,7 +99,8 @@ def generate_equals(class_name: str, fields: List[Field]) -> str: if i == 0: equals.append(f"{indent_lvl2}return Objects.equals({getter_name}(), that.{getter_name}()){semicolon}") else: - equals.append(f"{indent_lvl2}{return_indent}&& Objects.equals({getter_name}(), that.{getter_name}()){semicolon}") + equals.append( + f"{indent_lvl2}{return_indent}&& Objects.equals({getter_name}(), that.{getter_name}()){semicolon}") equals.append(f"{indent_lvl1}}}") return "\n".join(equals) @@ -186,4 +157,3 @@ def _validate_java_identifier(name: str) -> None: if not re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", name): raise ValueError(f"Invalid Java identifier: '{name}'") - diff --git a/src/java_model.py b/src/java_model.py new file mode 100644 index 0000000..7991cd4 --- /dev/null +++ b/src/java_model.py @@ -0,0 +1,38 @@ +from dataclasses import dataclass +from typing import List, Optional + +JAVA_KEYWORDS = { + "abstract", "assert", "boolean", "break", "byte", "case", "catch", + "char", "class", "const", "continue", "default", "do", "double", + "else", "enum", "extends", "final", "finally", "float", "for", + "goto", "if", "implements", "import", "instanceof", "int", + "interface", "long", "native", "new", "package", "private", + "protected", "public", "return", "short", "static", "strictfp", + "super", "switch", "synchronized", "this", "throw", "throws", + "transient", "try", "void", "volatile", "while" +} +JAVA_BUILTIN_TYPES = { + "Boolean", "Byte", "Character", "Double", "Float", "Integer", "List", "Long", "Short", + "Class", "Object", "String", "Void" +} +JAVA_LITERALS = { + "null", "true", "false" +} +indent_lvl1 = " " * 4 +indent_lvl2 = indent_lvl1 * 2 +indent_lvl3 = indent_lvl1 * 3 +return_indent = " " + + +@dataclass +class EnumClass: + name: str + values: List[str] + description: Optional[str] = None + + +@dataclass +class Field: + name: str + type: str + description: Optional[str] = None diff --git a/tests/reference_data.py b/tests/reference_data.py index bbfb810..c68668a 100644 --- a/tests/reference_data.py +++ b/tests/reference_data.py @@ -1,4 +1,4 @@ -from src.method_generator import Field +from src.java_model import Field field_exampleAttribute_int = Field(name="exampleAttribute", type="int", description="javadoc description") field_someName_String = Field(name="someName", type="String") diff --git a/tests/test_method_generator.py b/tests/test_java_method_generator.py similarity index 99% rename from tests/test_method_generator.py rename to tests/test_java_method_generator.py index bcac62b..d511bde 100644 --- a/tests/test_method_generator.py +++ b/tests/test_java_method_generator.py @@ -1,7 +1,7 @@ import pytest from tests.reference_data import * -from src.method_generator import * +from src.java_method_generator import * JAVA_KEYWORDS = { "abstract", "assert", "boolean", "break", "byte", "case", "catch", From 4ef712925d5aba34621e91250de5e92730c76b4c Mon Sep 17 00:00:00 2001 From: MrMaydo <89995856+MrMaydo@users.noreply.github.com> Date: Thu, 8 Jan 2026 12:17:31 +0100 Subject: [PATCH 5/5] Refactor java_method_generator.py --- src/java_method_generator.py | 139 ++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 69 deletions(-) diff --git a/src/java_method_generator.py b/src/java_method_generator.py index ca311b2..5350fe3 100644 --- a/src/java_method_generator.py +++ b/src/java_method_generator.py @@ -14,23 +14,27 @@ def generate_fields_block(fields: List[Field]) -> str: def generate_field_declaration(field: Field) -> str: - _validate_java_identifier(field.name) - if field.description: - javadoc = "\n".join([ - "", - f"{indent_lvl1}/**", - f"{indent_lvl1} * {field.description}", - f"{indent_lvl1} */" - ]) - else: - javadoc = "" + _validate_java_field_name(field.name) + declaration = [ - javadoc, + _render_javadoc(field), f"{indent_lvl1}private {field.type} {field.name};" ] return "\n".join(declaration) +def _render_javadoc(field: Field) -> str: + if field.description is None: + return "" + + return "\n".join([ + "", + f"{indent_lvl1}/**", + f"{indent_lvl1} * {field.description}", + f"{indent_lvl1} */" + ]) + + def generate_getters_and_setters(fields: List[Field]) -> str: methods = [] for field in fields: @@ -41,40 +45,27 @@ def generate_getters_and_setters(fields: List[Field]) -> str: def generate_getter(field: Field) -> str: - field_name = field.name - field_type = field.type - - _validate_java_identifier(field_name) - - getter_name = _get_getter_name(field) + getter_name = _build_getter_name(field.name) getter = [ "", - f"{indent_lvl1}public {field_type} {getter_name}() {{", - f"{indent_lvl2}return {field_name};", + f"{indent_lvl1}public {field.type} {getter_name}() {{", + f"{indent_lvl2}return {field.name};", f"{indent_lvl1}}}" ] - getter = "\n".join(getter) - - return getter + return "\n".join(getter) def generate_setter(field: Field) -> str: - field_name = field.name - field_type = field.type - - _validate_java_identifier(field_name) - - setter_name = _get_setter_name(field_name) + setter_name = _build_setter_name(field.name) setter = [ "", - f"{indent_lvl1}public void {setter_name}({field_type} {field_name}) {{", - f"{indent_lvl2}this.{field_name} = {field_name};", + f"{indent_lvl1}public void {setter_name}({field.type} {field.name}) {{", + f"{indent_lvl2}this.{field.name} = {field.name};", f"{indent_lvl1}}}" ] - setter = "\n".join(setter) - return setter + return "\n".join(setter) def generate_equals(class_name: str, fields: List[Field]) -> str: @@ -89,66 +80,76 @@ def generate_equals(class_name: str, fields: List[Field]) -> str: f"{indent_lvl2}if (!(obj instanceof {class_name}))", f"{indent_lvl3}return false;", - f"{indent_lvl2}{class_name} that = ({class_name}) obj;" + f"{indent_lvl2}{class_name} that = ({class_name}) obj;", + + _render_equals_return_statement(fields), + f"{indent_lvl1}}}" ] + return "\n".join(equals) + + +def _render_equals_return_statement(fields: List[Field]) -> str: + return_statement = [] for i, field in enumerate(fields): - _validate_java_identifier(field.name) - getter_name = _get_getter_name(field) - semicolon = ";" if i == (len(fields) - 1) else "" + getter_name = _build_getter_name(field.name) + end_line = ";" if i == (len(fields) - 1) else "" if i == 0: - equals.append(f"{indent_lvl2}return Objects.equals({getter_name}(), that.{getter_name}()){semicolon}") + return_statement.append(f"{indent_lvl2}return Objects.equals({getter_name}(), that.{getter_name}()){end_line}") else: - equals.append( - f"{indent_lvl2}{return_indent}&& Objects.equals({getter_name}(), that.{getter_name}()){semicolon}") - equals.append(f"{indent_lvl1}}}") - - return "\n".join(equals) + return_statement.append( + f"{indent_lvl2}{return_indent}&& Objects.equals({getter_name}(), that.{getter_name}()){end_line}") + return "\n".join(return_statement) def generate_hash_code(fields: List[Field]) -> str: hash_code = [ "", f"{indent_lvl1}@Override", - f"{indent_lvl1}public int hashCode() {{" + f"{indent_lvl1}public int hashCode() {{", + _render_hashcode_return_statement(fields), + f"{indent_lvl1}}}" ] - for i, field in enumerate(fields): - _validate_java_identifier(field.name) - hash_code.extend(_get_return_hash_lines(fields, index=i)) - - hash_code.append(f"{indent_lvl1}}}") - return "\n".join(hash_code) -def _get_return_hash_lines(fields, index) -> List[str]: - field = fields[index] - getter_name = _get_getter_name(field) - comma = "," if index < (len(fields) - 1) else "" - another_hash_line = f"{indent_lvl2}{return_indent}{getter_name}(){comma}" +def _render_hashcode_return_statement(fields: List[Field]) -> str: if len(fields) == 1: - return [f"{indent_lvl2}return Objects.hash({getter_name}());"] - if len(fields) > 1 and index == 0: - return [f"{indent_lvl2}return Objects.hash(", - f"{another_hash_line}"] - if len(fields) > 1 and index == (len(fields) - 1): - return [f"{another_hash_line}", - f"{indent_lvl2});"] - return [another_hash_line] + return _render_hashcode_return_statement_single_field(fields) + + return _render_hashcode_return_statement_multiple_field(fields) + + +def _render_hashcode_return_statement_single_field(fields: List[Field]) -> str: + field_name = fields[0].name + getter_name = _build_getter_name(field_name) + return f"{indent_lvl2}return Objects.hash({getter_name}());" + + +def _render_hashcode_return_statement_multiple_field(fields: List[Field]) -> str: + return_statement = [f"{indent_lvl2}return Objects.hash("] + for index, field in enumerate(fields): + getter_name = _build_getter_name(field.name) + comma = "," if index < (len(fields) - 1) else "" + return_statement.append(f"{indent_lvl2}{return_indent}{getter_name}(){comma}") + + return_statement.append(f"{indent_lvl2});") + + return "\n".join(return_statement) -def _get_getter_name(field): - getter_name = "get" + field.name[0].upper() + field.name[1:] - return getter_name +def _build_getter_name(field_name: str) -> str: + _validate_java_field_name(field_name) + return "get" + field_name[0].upper() + field_name[1:] -def _get_setter_name(field_name): - setter_name = "set" + field_name[0].upper() + field_name[1:] - return setter_name +def _build_setter_name(field_name: str) -> str: + _validate_java_field_name(field_name) + return "set" + field_name[0].upper() + field_name[1:] -def _validate_java_identifier(name: str) -> None: +def _validate_java_field_name(name: str) -> None: if not name: raise ValueError("Field name cannot be empty")