Skip to content

Commit 5ac6db2

Browse files
authored
Merge pull request #3 from M4TTH3/fixes
Add fixes to newline and TypeName issues, and updated a few api names
2 parents 48d5387 + 4fbcf76 commit 5ac6db2

18 files changed

+322
-211
lines changed

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717
- Complex control flow structures
1818
- Records (Java 14+)
1919
- File I/O operations
20+
- Enhanced TypeName support for common Java library types (List, Map, Set, etc.)
21+
- Better Python type mapping (bool -> boolean, int -> int, etc.)
22+
- Added `add_javadoc` method alongside existing `add_javadoc_line`
23+
- Added `add_raw_line` method for raw code with newlines
24+
- Support for handling None values in TypeName.get()
2025

2126
### Changed
2227
- Updated README structure with clear Python-to-Java code mappings
2328
- Enhanced documentation with working examples
29+
- Improved TypeName.get() to handle Python types directly (e.g., `TypeName.get(bool)` returns `TypeName.BOOLEAN`)
30+
- Updated method API: `add_code()` renamed to `add_raw_code()` for clarity
31+
- Enhanced JavaDoc generation with better newline handling
32+
- Fixed annotation newline formatting issues
33+
- Improved static class name handling
34+
35+
### Fixed
36+
- TypeName.get() now properly handles Python type objects instead of just type names
37+
- JavaDoc emission now properly handles newlines and prefixes
38+
- Annotation formatting with correct newline placement
39+
- Primitive type boxing to proper wrapper classes (e.g., int -> java.lang.Integer)
2440

2541
## [0.1.0] - 2025-07-29
2642

@@ -128,3 +144,8 @@ and the following files were added:
128144
- Added begin_statement_chain, add_chained_item, and end_statement_chain to MethodSpec builder
129145
- Includes corresponding changes in CodeBlock
130146
- Switched add_javadoc to add_javadoc_line
147+
- Added more default types to TypeName
148+
149+
### Fixes
150+
- Fixed annotation newline issue
151+
- Fixed passing in python types to give you a TypeName

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -977,8 +977,8 @@ print(str(java_file))
977977

978978
# Java file with imports and file comment
979979
java_file_with_imports = JavaFile.builder("com.example", simple_class) \
980-
.add_file_comment("This is a generated file.") \
981-
.add_file_comment("Do not edit manually.") \
980+
.add_file_comment_line("This is a generated file.") \
981+
.add_file_comment_line("Do not edit manually.") \
982982
.add_static_import(ClassName.get("java.lang", "System"), "out") \
983983
.build()
984984
print(str(java_file_with_imports))
@@ -1026,6 +1026,7 @@ method = MethodSpec.method_builder("example") \
10261026
```
10271027

10281028
## TODOs
1029+
I think of these as nice-ities, but they are more about convenience rather than correctness. There are work-arounds/other tools that can be used to create these desired affects.
10291030

10301031
1. TreeSitter API to synactically validate java file
10311032
2. Add kwargs to method spec builder. Currently code block will have an issue of overwriting previous
@@ -1035,8 +1036,7 @@ method = MethodSpec.method_builder("example") \
10351036
5. Name Allocator if we so desire (?)
10361037
6. Annotation member has to be valid java identifier
10371038
7. Handle primitive types better in ClassName i.e. validation
1038-
8. Improve tests with exact output strings and also slim down unneeded tests
1039-
9. Pass in TypeSpec for Types as well (for nested classes) ? It might work and we can include a self key too
1039+
8. Pass in TypeSpec for Types as well (for nested classes) ? It might work and we can include a self key too
10401040

10411041
## License
10421042

examples/complex_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def main():
115115
# Create the Java file
116116
java_file = (
117117
JavaFile.builder("com.example.processor", processor)
118-
.add_file_comment("This is a generated file. Do not edit!")
118+
.add_file_comment_line("This is a generated file. Do not edit!")
119119
.build()
120120
)
121121

pyjavapoet/annotation_spec.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,6 @@
1515
1616
Modified by Matthew Au-Yeung on 2025-07-29; see changelog.md for more details.
1717
- Similar APIs ported from Java to Python.
18-
19-
Changes and Current API:
20-
- The API is modeled after JavaPoet's AnnotationSpec, but adapted for Python.
21-
- AnnotationSpec is immutable; use the builder to create new instances.
22-
- Supports representing Java annotations for classes, methods, fields, parameters, etc.
23-
- The main API:
24-
- AnnotationSpec(type_name, members)
2518
"""
2619

2720
from typing import Any, Union

pyjavapoet/code_block.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@
1515
1616
Modified by Matthew Au-Yeung on 2025-07-29; see changelog.md for more details.
1717
- Similar APIs ported from Java to Python.
18-
19-
Changes and Current API:
20-
- The API is modeled after JavaPoet's CodeBlock, but adapted for Python.
21-
- CodeBlock is immutable; use the builder to create new instances.
22-
- Supports formatting Java code with placeholders.
2318
"""
2419

2520
import re
@@ -51,7 +46,7 @@ class CodeBlock(Code["CodeBlock"]):
5146
placeholder_match = re.compile(
5247
r"""
5348
\$(
54-
(?P<type1>[LSTN<>]) # $L, $S, $T, $N, $<, $>
49+
(?P<type1>[LSTN<>]) # $L, $S, $T, $N, $<, $>
5550
| # or
5651
(?P<name>[a-zA-Z_][a-zA-Z0-9_]*) # $name
5752
: # :
@@ -64,6 +59,26 @@ class CodeBlock(Code["CodeBlock"]):
6459
re.VERBOSE,
6560
)
6661

62+
placeholder_match_with_newlines = re.compile(
63+
r"""
64+
(
65+
\$(
66+
(?P<type1>[LSTN<>]) # $L, $S, $T, $N, $<, $>
67+
| # or
68+
(?P<name>[a-zA-Z_][a-zA-Z0-9_]*) # $name
69+
: # :
70+
(?P<type2>[LSTN]) # T, L, S, N
71+
| # or
72+
(?P<index>\d+) # $1, $2, etc.
73+
(?P<type3>[LSTN<>]) # L, S, T, N, $<, $>
74+
)
75+
|
76+
(\n) # or a literal newline
77+
)
78+
""",
79+
re.VERBOSE,
80+
)
81+
6782
def __init__(self, format_parts: list[str], args: list[Any], named_args: dict[str, Any]):
6883
self.format_parts = format_parts
6984
self.args = args
@@ -140,9 +155,10 @@ def emit(self, code_writer: "CodeWriter", new_line_prefix: str = "") -> None:
140155
code_writer.emit(part, new_line_prefix)
141156

142157
def emit_javadoc(self, code_writer: "CodeWriter") -> None:
143-
code_writer.emit("/**\n * ")
158+
code_writer.emit("/**\n")
144159
self.emit(code_writer, " * ")
145-
code_writer.emit("\n */")
160+
code_writer.emit("\n", " * ")
161+
code_writer.emit(" */")
146162

147163
def javadoc(self) -> str:
148164
writer = CodeWriter()
@@ -178,6 +194,13 @@ def join_to_code(code_blocks: list["CodeBlock"], separator: str = "") -> "CodeBl
178194

179195
@staticmethod
180196
def add_javadoc(javadoc: Optional["CodeBlock"], format_string: str, *args) -> "CodeBlock":
197+
if javadoc:
198+
return CodeBlock.join_to_code([javadoc, CodeBlock.of(format_string, *args)])
199+
else:
200+
return CodeBlock.of(format_string, *args)
201+
202+
@staticmethod
203+
def add_javadoc_line(javadoc: Optional["CodeBlock"], format_string: str, *args) -> "CodeBlock":
181204
if javadoc:
182205
return CodeBlock.join_to_code([javadoc, CodeBlock.of(format_string, *args)], "\n")
183206
else:
@@ -207,7 +230,7 @@ def __init__(
207230

208231
def add(self, format_string: str, *args, **kwargs) -> "CodeBlock.Builder":
209232
# Check for arguments in the format string
210-
matches = list(re.finditer(CodeBlock.placeholder_match, format_string))
233+
matches = list(re.finditer(CodeBlock.placeholder_match_with_newlines, format_string))
211234

212235
# Simple case: no arguments
213236
if not matches:
@@ -248,6 +271,11 @@ def add_statement(self, format_string: str, *args, **kwargs) -> "CodeBlock.Build
248271
self.add(format_string, *args, **kwargs)
249272
self.add(";\n")
250273
return self
274+
275+
def add_line(self, format_string: str, *args, **kwargs) -> "CodeBlock.Builder":
276+
self.add(format_string, *args, **kwargs)
277+
self.add("\n")
278+
return self
251279

252280
def begin_statement(self, format_string: str, *args, **kwargs) -> "CodeBlock.Builder":
253281
parts = format_string.split("\n")

pyjavapoet/code_writer.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@
1515
1616
Modified by Matthew Au-Yeung on 2025-07-29; see changelog.md for more details.
1717
- Similar APIs ported from Java to Python.
18-
19-
Changes and Current API:
20-
- The API is modeled after JavaPoet's CodeWriter, but adapted for Python.
21-
- CodeWriter is immutable; use the builder to create new instances.
22-
- Supports emitting Java code with proper formatting.
2318
"""
2419

2520
from typing import Annotated, Literal
@@ -80,6 +75,10 @@ def unindent(self, count: int = 1) -> None:
8075

8176
def emit(self, s: str | Constant, new_line_prefix: str = "") -> "CodeWriter":
8277
if s.startswith("\n"):
78+
if self.__line_start and new_line_prefix:
79+
self.__out.append(self.__indent * self.__indent_level)
80+
self.__out.append(new_line_prefix)
81+
8382
# Reset line start
8483
self.__out.append("\n")
8584
self.__line_start = True

pyjavapoet/field_spec.py

Lines changed: 7 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,17 @@
1515
1616
Modified by Matthew Au-Yeung on 2025-07-29; see changelog.md for more details.
1717
- Similar APIs ported from Java to Python.
18-
19-
Changes and Current API:
20-
- The API is modeled after JavaPoet's FieldSpec, but adapted for Python.
21-
- FieldSpec is immutable; use the builder to create new instances.
22-
- Supports Java modifiers (from Modifier), annotations (AnnotationSpec), type information (TypeName),
23-
and initializer (CodeBlock).
2418
"""
2519

2620
from typing import TYPE_CHECKING, Optional, Union
2721

2822
from pyjavapoet.annotation_spec import AnnotationSpec
2923
from pyjavapoet.code_base import Code
3024
from pyjavapoet.code_block import CodeBlock
31-
from pyjavapoet.code_writer import CodeWriter
25+
from pyjavapoet.code_writer import EMPTY_STRING, CodeWriter
3226
from pyjavapoet.modifier import Modifier
3327
from pyjavapoet.type_name import TypeName
34-
from pyjavapoet.util import deep_copy
28+
from pyjavapoet.util import deep_copy, throw_if_invalid_java_identifier
3529

3630
if TYPE_CHECKING:
3731
from pyjavapoet.code_writer import CodeWriter
@@ -98,69 +92,9 @@ def to_builder(self) -> "Builder":
9892
deep_copy(self.initializer),
9993
)
10094

101-
@staticmethod
102-
def is_valid_field_name(name: str) -> bool:
103-
java_keywords = {
104-
"abstract",
105-
"assert",
106-
"boolean",
107-
"break",
108-
"byte",
109-
"case",
110-
"catch",
111-
"char",
112-
"class",
113-
"const",
114-
"continue",
115-
"default",
116-
"do",
117-
"double",
118-
"else",
119-
"enum",
120-
"extends",
121-
"final",
122-
"finally",
123-
"float",
124-
"for",
125-
"goto",
126-
"if",
127-
"implements",
128-
"import",
129-
"instanceof",
130-
"int",
131-
"interface",
132-
"long",
133-
"native",
134-
"new",
135-
"package",
136-
"private",
137-
"protected",
138-
"public",
139-
"return",
140-
"short",
141-
"static",
142-
"strictfp",
143-
"super",
144-
"switch",
145-
"synchronized",
146-
"this",
147-
"throw",
148-
"throws",
149-
"transient",
150-
"try",
151-
"void",
152-
"volatile",
153-
"while",
154-
"true",
155-
"false",
156-
"null",
157-
}
158-
return name.isidentifier() and name not in java_keywords
159-
16095
@staticmethod
16196
def builder(type_name: Union["TypeName", str, type], name: str) -> "Builder":
162-
if not FieldSpec.is_valid_field_name(name):
163-
raise ValueError(f"Invalid field name: {name}")
97+
throw_if_invalid_java_identifier(name)
16498

16599
if not isinstance(type_name, TypeName):
166100
type_name = TypeName.get(type_name)
@@ -207,6 +141,10 @@ def add_annotation(self, annotation_spec: "AnnotationSpec") -> "FieldSpec.Builde
207141
def add_javadoc(self, format_string: str, *args) -> "FieldSpec.Builder":
208142
self.__javadoc = CodeBlock.add_javadoc(self.__javadoc, format_string, *args)
209143
return self
144+
145+
def add_javadoc_line(self, format_string: str = EMPTY_STRING, *args) -> "FieldSpec.Builder":
146+
self.__javadoc = CodeBlock.add_javadoc_line(self.__javadoc, format_string, *args)
147+
return self
210148

211149
def initializer(self, format_string: str | CodeBlock, *args) -> "FieldSpec.Builder":
212150
if isinstance(format_string, str):

pyjavapoet/java_file.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,6 @@
1515
1616
Modified by Matthew Au-Yeung on 2025-07-29; see changelog.md for more details.
1717
- Similar APIs ported from Java to Python.
18-
19-
Changes and Current API:
20-
- The API is modeled after JavaPoet's JavaFile, but adapted for Python.
21-
- JavaFile is immutable; use the builder to create new instances.
22-
- Supports package declaration, imports, and type declarations.
23-
- The main API:
24-
- JavaFile(package_name, type_spec, file_comment, indent, static_imports)
2518
"""
2619

2720
import sys
@@ -188,16 +181,13 @@ def __init__(
188181
self.__indent = indent
189182
self.__static_imports = static_imports or {}
190183

191-
def add_generated_by(self, creator: str, extra_comment: str = "", *args) -> "JavaFile.Builder":
192-
self.add_file_comment("@generated")
193-
self.add_file_comment(f"Generated by {creator}", *args)
194-
if extra_comment:
195-
self.add_file_comment(extra_comment, *args)
196-
return self
197-
198184
def add_file_comment(self, format_string: str = EMPTY_STRING, *args) -> "JavaFile.Builder":
199185
self.__file_comment = CodeBlock.add_javadoc(self.__file_comment, format_string, *args)
200186
return self
187+
188+
def add_file_comment_line(self, format_string: str = EMPTY_STRING, *args) -> "JavaFile.Builder":
189+
self.__file_comment = CodeBlock.add_javadoc_line(self.__file_comment, format_string, *args)
190+
return self
201191

202192
def indent(self, indent: str) -> "JavaFile.Builder":
203193
self.__indent = indent

0 commit comments

Comments
 (0)