From e702c7dc065c7e5cf83e7cd174c87b5e3d7f8736 Mon Sep 17 00:00:00 2001 From: MrMaydo <89995856+MrMaydo@users.noreply.github.com> Date: Fri, 2 Jan 2026 20:04:24 +0100 Subject: [PATCH 1/7] add package line --- src/header_generator.py | 8 ++++++++ tests/test_header_generator.py | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/header_generator.py create mode 100644 tests/test_header_generator.py diff --git a/src/header_generator.py b/src/header_generator.py new file mode 100644 index 0000000..416b478 --- /dev/null +++ b/src/header_generator.py @@ -0,0 +1,8 @@ +import re + + +def set_package(package: str) -> str: + PACKAGE_REGEX = r"^(?:[a-z_][a-z0-9_]*)(?:\.(?:[a-z_][a-z0-9_]*))*$" + if not re.match(PACKAGE_REGEX, package): + raise ValueError(f"Invalid package: '{package}'") + return f"package {package}" diff --git a/tests/test_header_generator.py b/tests/test_header_generator.py new file mode 100644 index 0000000..5347846 --- /dev/null +++ b/tests/test_header_generator.py @@ -0,0 +1,23 @@ +import pytest + +from src.header_generator import set_package + + +def test_set_package(): + example = "org.example.hyphenated_name" + expected = f"package {example}" + assert set_package(example) == expected + + example_2 = "com.example._123name" + expected_2 = f"package {example_2}" + assert set_package(example_2) == expected_2 + + +def test_set_package_illegal_name(): + example = "org.example.hyphenated-name" + with pytest.raises(ValueError): + set_package(example) + + example_2 = "com.example.123name" + with pytest.raises(ValueError): + set_package(example_2) From 58b68e706a26f568a0e9cd40a1dfdfe9b022421a Mon Sep 17 00:00:00 2001 From: MrMaydo <89995856+MrMaydo@users.noreply.github.com> Date: Fri, 2 Jan 2026 20:08:09 +0100 Subject: [PATCH 2/7] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index b4e3f9a..2730aba 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 MrMaydo +Copyright (c) 2025 Zbigniew Brzezicki Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 2f78e73157854fab8f632e43050d2c71cb84c5a2 Mon Sep 17 00:00:00 2001 From: MrMaydo <89995856+MrMaydo@users.noreply.github.com> Date: Fri, 2 Jan 2026 21:50:40 +0100 Subject: [PATCH 3/7] add equals generation --- src/method_generator.py | 28 ++++++++++++++++++++++++++++ tests/test_method_generator.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/src/method_generator.py b/src/method_generator.py index 9073342..77f7afa 100644 --- a/src/method_generator.py +++ b/src/method_generator.py @@ -106,6 +106,34 @@ def generate_setter(field: Field) -> str: return setter +def generate_equals(class_name: str, fields: List[Field]) -> str: + equals = [ + "", + f"{indent_lvl1}@Override", + f"{indent_lvl1}public boolean equals(Object obj) {{", + + f"{indent_lvl2}if (this == obj)", + f"{indent_lvl3}return true;", + + f"{indent_lvl2}if (!(obj instanceof {class_name}))", + f"{indent_lvl3}return false;", + + f"{indent_lvl2}{class_name} that = ({class_name}) obj;" + ] + + for i, field in enumerate(fields): + _validate_java_identifier(field.name) + getter_name = "get" + field.name[0].upper() + field.name[1:] + semicolon = ";" if i == (len(fields) - 1) else "" + if i == 0: + equals.append(f"{indent_lvl2}return Objects.equals({getter_name}(), that.{getter_name}()){semicolon}") + else: + equals.append(f"{indent_lvl2} && Objects.equals({getter_name}(), that.{getter_name}()){semicolon}") + equals.append(f"{indent_lvl1}}}") + + return "\n".join(equals) + + def generate_hash_code(fields: List[Field]) -> str: hash_code = [ "", diff --git a/tests/test_method_generator.py b/tests/test_method_generator.py index 3fa3f58..575cae3 100644 --- a/tests/test_method_generator.py +++ b/tests/test_method_generator.py @@ -160,3 +160,31 @@ def test_generate_hash_code_invalid_name(): attr = Field(name=name, type="String") with pytest.raises(ValueError): generate_hash_code([attr]) + + +def test_generate_equals(): + class_name = "MyClass" + attr1 = field_exampleAttribute_int + attr2 = field_someName_String + attr3 = field_customData_CustomObject + attributes = [attr1, attr2, attr3] + expected = """ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof MyClass)) + return false; + MyClass that = (MyClass) obj; + return Objects.equals(getExampleAttribute(), that.getExampleAttribute()) + && Objects.equals(getSomeName(), that.getSomeName()) + && Objects.equals(getCustomData(), that.getCustomData()); + }""" + assert generate_equals(class_name, attributes) == expected + + +def test_generate_hash_code_invalid_name(): + for name in illegal_names: + attr = Field(name=name, type="String") + with pytest.raises(ValueError): + generate_equals("MyClass", [attr]) From ab6314f884ea97708920f0d5d5b76648741837ee Mon Sep 17 00:00:00 2001 From: MrMaydo <89995856+MrMaydo@users.noreply.github.com> Date: Fri, 2 Jan 2026 22:07:30 +0100 Subject: [PATCH 4/7] improve generate_hash_code function --- src/method_generator.py | 16 ++++++++++++---- tests/test_method_generator.py | 10 ++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/method_generator.py b/src/method_generator.py index 77f7afa..579d2f9 100644 --- a/src/method_generator.py +++ b/src/method_generator.py @@ -138,17 +138,25 @@ def generate_hash_code(fields: List[Field]) -> str: hash_code = [ "", f"{indent_lvl1}@Override", - f"{indent_lvl1}public int hashCode() {{", - f"{indent_lvl2}return Objects.hash(" + f"{indent_lvl1}public int hashCode() {{" ] for i, field in enumerate(fields): _validate_java_identifier(field.name) getter_name = "get" + field.name[0].upper() + field.name[1:] comma = "," if i < (len(fields) - 1) else "" - hash_code.append(f"{indent_lvl2} {getter_name}(){comma}") - hash_code.append(f"{indent_lvl2});") + if len(fields) == 1: + hash_code.append(f"{indent_lvl2}return Objects.hash({getter_name}());") + elif len(fields) > 1 and i == 0: + hash_code.append(f"{indent_lvl2}return Objects.hash(") + hash_code.append(f"{indent_lvl2} {getter_name}(){comma}") + elif len(fields) > 1 and i == (len(fields) - 1): + hash_code.append(f"{indent_lvl2} {getter_name}(){comma}") + hash_code.append(f"{indent_lvl2});") + else: + hash_code.append(f"{indent_lvl2} {getter_name}(){comma}") + hash_code.append(f"{indent_lvl1}}}") return "\n".join(hash_code) diff --git a/tests/test_method_generator.py b/tests/test_method_generator.py index 575cae3..688ff17 100644 --- a/tests/test_method_generator.py +++ b/tests/test_method_generator.py @@ -155,6 +155,16 @@ def test_generate_hash_code(): assert generate_hash_code(attributes) == expected +def test_generate_hash_code_one_field(): + attr = field_exampleAttribute_int + expected = """ + @Override + public int hashCode() { + return Objects.hash(getExampleAttribute()); + }""" + assert generate_hash_code([attr]) == expected + + def test_generate_hash_code_invalid_name(): for name in illegal_names: attr = Field(name=name, type="String") From fd6c859d7e3462c2d17087a0e56b398841574502 Mon Sep 17 00:00:00 2001 From: MrMaydo <89995856+MrMaydo@users.noreply.github.com> Date: Fri, 2 Jan 2026 22:13:51 +0100 Subject: [PATCH 5/7] add more tests for generate_equals function --- tests/test_method_generator.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/test_method_generator.py b/tests/test_method_generator.py index 688ff17..bcac62b 100644 --- a/tests/test_method_generator.py +++ b/tests/test_method_generator.py @@ -193,7 +193,23 @@ def test_generate_equals(): assert generate_equals(class_name, attributes) == expected -def test_generate_hash_code_invalid_name(): +def test_generate_equals_one_field(): + class_name = "AnotherClass" + attr = field_someName_String + expected = """ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof AnotherClass)) + return false; + AnotherClass that = (AnotherClass) obj; + return Objects.equals(getSomeName(), that.getSomeName()); + }""" + assert generate_equals(class_name, [attr]) == expected + + +def test_generate_equals_invalid_name(): for name in illegal_names: attr = Field(name=name, type="String") with pytest.raises(ValueError): From 9d89e51fd3bd35588f6f6248c252c6074757506b Mon Sep 17 00:00:00 2001 From: MrMaydo <89995856+MrMaydo@users.noreply.github.com> Date: Fri, 2 Jan 2026 23:04:54 +0100 Subject: [PATCH 6/7] refactor generate_hash_code --- src/method_generator.py | 49 +++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/method_generator.py b/src/method_generator.py index 579d2f9..6e68abb 100644 --- a/src/method_generator.py +++ b/src/method_generator.py @@ -25,6 +25,7 @@ indent_lvl1 = " " indent_lvl2 = indent_lvl1 * 2 indent_lvl3 = indent_lvl1 * 3 +return_indent = " " @dataclass @@ -75,7 +76,7 @@ def generate_getter(field: Field) -> str: _validate_java_identifier(field_name) - getter_name = "get" + field_name[0].upper() + field_name[1:] + getter_name = _get_getter_name(field) getter = [ "", @@ -94,7 +95,7 @@ def generate_setter(field: Field) -> str: _validate_java_identifier(field_name) - setter_name = "set" + field_name[0].upper() + field_name[1:] + setter_name = _get_setter_name(field_name) setter = [ "", @@ -123,12 +124,12 @@ def generate_equals(class_name: str, fields: List[Field]) -> str: for i, field in enumerate(fields): _validate_java_identifier(field.name) - getter_name = "get" + field.name[0].upper() + field.name[1:] + getter_name = _get_getter_name(field) semicolon = ";" if i == (len(fields) - 1) else "" if i == 0: equals.append(f"{indent_lvl2}return Objects.equals({getter_name}(), that.{getter_name}()){semicolon}") else: - equals.append(f"{indent_lvl2} && 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) @@ -143,25 +144,39 @@ def generate_hash_code(fields: List[Field]) -> str: for i, field in enumerate(fields): _validate_java_identifier(field.name) - getter_name = "get" + field.name[0].upper() + field.name[1:] - comma = "," if i < (len(fields) - 1) else "" - - if len(fields) == 1: - hash_code.append(f"{indent_lvl2}return Objects.hash({getter_name}());") - elif len(fields) > 1 and i == 0: - hash_code.append(f"{indent_lvl2}return Objects.hash(") - hash_code.append(f"{indent_lvl2} {getter_name}(){comma}") - elif len(fields) > 1 and i == (len(fields) - 1): - hash_code.append(f"{indent_lvl2} {getter_name}(){comma}") - hash_code.append(f"{indent_lvl2});") - else: - hash_code.append(f"{indent_lvl2} {getter_name}(){comma}") + 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}" + 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] + + +def _get_getter_name(field): + getter_name = "get" + field.name[0].upper() + field.name[1:] + return getter_name + + +def _get_setter_name(field_name): + setter_name = "set" + field_name[0].upper() + field_name[1:] + return setter_name + + def _validate_java_identifier(name: str) -> None: if not name: raise ValueError("Field name cannot be empty") From ee09ebfcfd729ec4e329c50d8b1ace71a75e789e Mon Sep 17 00:00:00 2001 From: MrMaydo <89995856+MrMaydo@users.noreply.github.com> Date: Sun, 4 Jan 2026 22:16:08 +0100 Subject: [PATCH 7/7] Update python-publish.yml --- .github/workflows/python-publish.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 1fd71d2..73836eb 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -1,8 +1,6 @@ name: Python tests on: - push: - branches: ["main"] pull_request: branches: ["main"]