diff --git a/CHANGELOG.md b/CHANGELOG.md index 0474f78..cc5bbb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -125,5 +125,6 @@ and the following files were added: ## [0.1.1] - 2025-07-29 ### Added Features -- Added begin_statement, add_statement_item, and end_statement to MethodSpec builder +- Added begin_statement_chain, add_chained_item, and end_statement_chain to MethodSpec builder - Includes corresponding changes in CodeBlock +- Switched add_javadoc to add_javadoc_line diff --git a/README.md b/README.md index 99e53f1..c795cb9 100644 --- a/README.md +++ b/README.md @@ -305,18 +305,18 @@ component = AnnotationSpec.builder(ClassName.get("org.springframework.stereotype service = TypeSpec.class_builder("UserService") \ .add_annotation(component) \ .add_modifiers(Modifier.PUBLIC) \ - .add_javadoc("Service class for managing users.\n") \ - .add_javadoc("\n") \ - .add_javadoc("@author PyJavaPoet\n") \ - .add_javadoc("@since 1.0\n") \ + .add_javadoc_line("Service class for managing users.") \ + .add_javadoc_line() \ + .add_javadoc_line("@author PyJavaPoet") \ + .add_javadoc_line("@since 1.0") \ .add_field(FieldSpec.builder(ClassName.get("java.lang", "String"), "name") .add_annotation(nullable) .add_modifiers(Modifier.PRIVATE) .build()) \ .add_method(MethodSpec.method_builder("getName") - .add_javadoc("Gets the user name.\n") - .add_javadoc("\n") - .add_javadoc("@return the user name, or null if not set\n") + .add_javadoc_line("Gets the user name.") + .add_javadoc_line() + .add_javadoc_line("@return the user name, or null if not set") .add_annotation(nullable) .add_modifiers(Modifier.PUBLIC) .returns(ClassName.get("java.lang", "String")) @@ -428,7 +428,53 @@ public class ItemProcessor { } ``` -### 7. Records (Java 14+) +### 7. Statement Chaining + +PyJavaPoet supports fluent method chaining for building statements: + +**Python Code:** +```python +from pyjavapoet import MethodSpec, TypeSpec, JavaFile, Modifier + +# Method with statement chaining +method = MethodSpec.method_builder("buildString") \ + .add_modifiers(Modifier.PUBLIC) \ + .returns("String") \ + .add_statement("StringBuilder $L = new StringBuilder()", "builder") \ + .begin_statement_chain("$L", "builder") \ + .add_chained_item(".append($S)", "Hello") \ + .add_chained_item(".append($S)", " ") \ + .add_chained_item(".append($S)", "World") \ + .end_statement_chain() \ + .add_statement("return $L.toString()", "builder") \ + .build() + +clazz = TypeSpec.class_builder("StringBuilderExample") \ + .add_modifiers(Modifier.PUBLIC) \ + .add_method(method) \ + .build() + +java_file = JavaFile.builder("com.example", clazz).build() +print(java_file) +``` + +**Generated Java Code:** +```java +package com.example; + +public class StringBuilderExample { + public String buildString() { + StringBuilder builder = new StringBuilder(); + builder + .append("Hello") + .append(" ") + .append("World"); + return builder.toString(); + } +} +``` + +### 8. Records (Java 14+) **Python Code:** ```python @@ -456,7 +502,7 @@ public record Point(int x, int y) implements Serializable { } ``` -### 8. Writing Files to Disk +### 9. Writing Files to Disk ```python from pyjavapoet import JavaFile @@ -475,45 +521,522 @@ with open("MyClass.java", "w") as f: java_file.write_to(f) ``` -## Extra Usages +## API Reference + +This section provides detailed documentation for each PyJavaPoet component with examples and their generated output. -### MethodSpec -We provide the following API alternative to .add_statement(...) +### TypeName Classes -**Python Code** -```py -MethodSpec.method_builder("test") \ -.add_statement("StringBuilder $L = new StringBuilder()", "builder") \ -.begin_statement("$L", "builder") \ -.add_statement_item(".append($S)", "hello") \ -.add_statement_item(".append($S)", "world") \ -.end_statement() -.build() +TypeName is the foundation for representing Java types in PyJavaPoet. + +#### ClassName + +Represents class and interface types, including nested classes. + +```python +from pyjavapoet import ClassName + +# Basic class names +string_class = ClassName.get("java.lang", "String") +print(str(string_class)) # Output: java.lang.String + +list_class = ClassName.get("java.util", "List") +print(str(list_class)) # Output: java.util.List + +# Nested classes +nested_class = ClassName.get("com.example", "Outer", "Inner") +print(str(nested_class)) # Output: com.example.Outer.Inner + +# Common predefined types +print(str(ClassName.OBJECT)) # Output: java.lang.Object +print(str(ClassName.STRING)) # Output: java.lang.String ``` -**Generated Java Code** -```java -void test() { - StringBuilder builder = new StringBuilder(); - builder - .append("hello") - .append("world"); -} +#### TypeName + +Base class for all type representations, provides common type constants. + +```python +from pyjavapoet import TypeName + +# Primitive types +print(str(TypeName.get("int"))) # Output: int +print(str(TypeName.get("boolean"))) # Output: boolean +print(str(TypeName.get("void"))) # Output: void + +# Predefined constants +print(str(TypeName.INT)) # Output: int +print(str(TypeName.BOOLEAN)) # Output: boolean +print(str(TypeName.VOID)) # Output: void +print(str(TypeName.STRING)) # Output: java.lang.String +``` + +#### ArrayTypeName + +Represents array types with support for multi-dimensional arrays. + +```python +from pyjavapoet import ArrayTypeName, ClassName + +string_class = ClassName.get("java.lang", "String") + +# Single-dimensional array +string_array = ArrayTypeName.of(string_class) +print(str(string_array)) # Output: String[] + +# Multi-dimensional arrays +int_2d_array = ArrayTypeName.of(ArrayTypeName.of("int")) +print(str(int_2d_array)) # Output: int[][] + +# Primitive arrays +int_array = ArrayTypeName.of("int") +print(str(int_array)) # Output: int[] +``` + +#### ParameterizedTypeName + +Represents generic types with type arguments. + +```python +from pyjavapoet import ParameterizedTypeName, ClassName + +list_class = ClassName.get("java.util", "List") +string_class = ClassName.get("java.lang", "String") +integer_class = ClassName.get("java.lang", "Integer") + +# List +list_of_strings = ParameterizedTypeName.get(list_class, string_class) +print(str(list_of_strings)) # Output: List + +# Alternative syntax +list_of_strings2 = list_class.with_type_arguments(string_class) +print(str(list_of_strings2)) # Output: List + +# Map +map_class = ClassName.get("java.util", "Map") +map_string_int = ParameterizedTypeName.get(map_class, string_class, integer_class) +print(str(map_string_int)) # Output: Map + +# Nested parameterized types: Map> +list_of_strings = ParameterizedTypeName.get(list_class, string_class) +map_nested = ParameterizedTypeName.get(map_class, string_class, list_of_strings) +print(str(map_nested)) # Output: Map> +``` + +#### TypeVariableName + +Represents generic type variables with optional bounds. + +```python +from pyjavapoet import TypeVariableName, ClassName + +# Basic type variable +t_var = TypeVariableName.get("T") +print(str(t_var)) # Output: T + +# Bounded type variable +number_class = ClassName.get("java.lang", "Number") +bounded_t = TypeVariableName.get("T", number_class) +print(str(bounded_t)) # Output: T extends Number + +# Multiple bounds +comparable_class = ClassName.get("java.lang", "Comparable") +serializable_class = ClassName.get("java.io", "Serializable") +multi_bounded = TypeVariableName.get("T", number_class, comparable_class, serializable_class) +print(str(multi_bounded)) # Output: T extends Number & Comparable & Serializable +``` + +#### WildcardTypeName + +Represents wildcard types with upper and lower bounds. + +```python +from pyjavapoet import WildcardTypeName, ClassName + +number_class = ClassName.get("java.lang", "Number") +object_class = ClassName.get("java.lang", "Object") + +# ? extends Number +extends_number = WildcardTypeName.subtypes_of(number_class) +print(str(extends_number)) # Output: ? extends Number + +# ? super Number +super_number = WildcardTypeName.supertypes_of(number_class) +print(str(super_number)) # Output: ? super Number + +# Unbounded wildcard (? extends Object becomes just ?) +unbounded = WildcardTypeName.subtypes_of(object_class) +print(str(unbounded)) # Output: ? +``` + +### Specification Classes + +#### FieldSpec + +Represents field declarations with modifiers, initializers, and annotations. + +```python +from pyjavapoet import FieldSpec, Modifier, ClassName, AnnotationSpec + +# Basic field +basic_field = FieldSpec.builder("int", "count").build() +print(str(basic_field)) # Output: int count; + +# Field with modifiers +private_field = FieldSpec.builder(ClassName.get("java.lang", "String"), "name") \ + .add_modifiers(Modifier.PRIVATE, Modifier.FINAL) \ + .build() +print(str(private_field)) # Output: private final String name; + +# Field with initializer +initialized_field = FieldSpec.builder("int", "counter") \ + .add_modifiers(Modifier.PRIVATE, Modifier.STATIC) \ + .initializer("$L", 0) \ + .build() +print(str(initialized_field)) # Output: private static int counter = 0; + +# Field with annotation +nullable = AnnotationSpec.builder(ClassName.get("javax.annotation", "Nullable")).build() +annotated_field = FieldSpec.builder(ClassName.get("java.lang", "String"), "value") \ + .add_annotation(nullable) \ + .build() +print(str(annotated_field)) # Output: @Nullable\nString value; + +# Generic field +list_class = ClassName.get("java.util", "List") +string_class = ClassName.get("java.lang", "String") +list_field = FieldSpec.builder(list_class.with_type_arguments(string_class), "items") \ + .add_modifiers(Modifier.PRIVATE, Modifier.FINAL) \ + .initializer("new $T<>()", ClassName.get("java.util", "ArrayList")) \ + .build() +print(str(list_field)) # Output: private final List items = new ArrayList<>(); +``` + +#### ParameterSpec + +Represents method/constructor parameters with annotations. + +```python +from pyjavapoet import ParameterSpec, ClassName, AnnotationSpec + +# Basic parameter +basic_param = ParameterSpec.builder("int", "value").build() +print(str(basic_param)) # Output: int value + +# Parameter with annotation +nullable = AnnotationSpec.builder(ClassName.get("javax.annotation", "Nullable")).build() +annotated_param = ParameterSpec.builder(ClassName.get("java.lang", "String"), "name") \ + .add_annotation(nullable) \ + .build() +print(str(annotated_param)) # Output: @Nullable String name + +# Generic parameter +list_class = ClassName.get("java.util", "List") +string_class = ClassName.get("java.lang", "String") +generic_param = ParameterSpec.builder(list_class.with_type_arguments(string_class), "items").build() +print(str(generic_param)) # Output: List items + +# Varargs parameter +varargs_param = ParameterSpec.builder("String...", "args").build() +print(str(varargs_param)) # Output: String... args +``` + +#### MethodSpec + +Represents method and constructor declarations with full support for Java features. + +```python +from pyjavapoet import MethodSpec, Modifier, ClassName, TypeVariableName, ParameterSpec + +# Basic method +basic_method = MethodSpec.method_builder("getName") \ + .add_modifiers(Modifier.PUBLIC) \ + .returns(ClassName.get("java.lang", "String")) \ + .add_statement("return this.name") \ + .build() +print(str(basic_method)) +# Output: +# public String getName() { +# return this.name; +# } + +# Constructor +constructor = MethodSpec.constructor_builder() \ + .add_modifiers(Modifier.PUBLIC) \ + .add_parameter(ClassName.get("java.lang", "String"), "name") \ + .add_statement("this.name = name") \ + .build() +print(str(constructor)) +# Output: +# public (String name) { +# this.name = name; +# } + +# Generic method +t_var = TypeVariableName.get("T") +generic_method = MethodSpec.method_builder("identity") \ + .add_type_variable(t_var) \ + .add_modifiers(Modifier.PUBLIC, Modifier.STATIC) \ + .returns(t_var) \ + .add_parameter(t_var, "input") \ + .add_statement("return input") \ + .build() +print(str(generic_method)) +# Output: +# public static T identity(T input) { +# return input; +# } + +# Method with Javadoc +documented_method = MethodSpec.method_builder("calculate") \ + .add_javadoc_line("Calculates the result.") \ + .add_javadoc_line() \ + .add_javadoc_line("@param input the input value") \ + .add_javadoc_line("@return the calculated result") \ + .add_modifiers(Modifier.PUBLIC) \ + .returns("int") \ + .add_parameter("int", "input") \ + .add_statement("return input * 2") \ + .build() +print(str(documented_method)) +# Output: +# /** +# * Calculates the result. +# * +# * @param input the input value +# * @return the calculated result +# */ +# public int calculate(int input) { +# return input * 2; +# } + +# Abstract method (no body) +abstract_method = MethodSpec.method_builder("process") \ + .add_modifiers(Modifier.PUBLIC, Modifier.ABSTRACT) \ + .returns("void") \ + .add_parameter("Object", "data") \ + .build() +print(str(abstract_method)) +# Output: public abstract void process(Object data); +``` + +#### AnnotationSpec + +Represents Java annotations with members and values. + +```python +from pyjavapoet import AnnotationSpec, ClassName + +# Basic annotation +override = AnnotationSpec.builder(ClassName.get("java.lang", "Override")).build() +print(str(override)) # Output: @Override + +# Annotation with single value +component = AnnotationSpec.builder(ClassName.get("org.springframework.stereotype", "Component")) \ + .add_member("value", "$S", "userService") \ + .build() +print(str(component)) # Output: @Component("userService") + +# Annotation with multiple members +request_mapping = AnnotationSpec.builder(ClassName.get("org.springframework.web.bind.annotation", "RequestMapping")) \ + .add_member("value", "$S", "/api/users") \ + .add_member("method", "$T.GET", ClassName.get("org.springframework.web.bind.annotation", "RequestMethod")) \ + .build() +print(str(request_mapping)) +# Output: @RequestMapping(value = "/api/users", method = RequestMethod.GET) + +# Annotation with array values +suppress_warnings = AnnotationSpec.builder(ClassName.get("java.lang", "SuppressWarnings")) \ + .add_member("value", "{$S, $S}", "unchecked", "rawtypes") \ + .build() +print(str(suppress_warnings)) # Output: @SuppressWarnings({"unchecked", "rawtypes"}) + +# Shorthand for single-member annotations +get_annotation = AnnotationSpec.get(ClassName.get("java.lang", "Override")) +print(str(get_annotation)) # Output: @Override +``` + +#### TypeSpec + +Represents type declarations: classes, interfaces, enums, annotations, and records. + +```python +from pyjavapoet import TypeSpec, Modifier, ClassName, FieldSpec, MethodSpec + +# Basic class +basic_class = TypeSpec.class_builder("BasicClass").build() +print(str(basic_class)) +# Output: +# class BasicClass { +# } + +# Class with inheritance and interfaces +extended_class = TypeSpec.class_builder("MyClass") \ + .add_modifiers(Modifier.PUBLIC) \ + .superclass(ClassName.get("com.example", "BaseClass")) \ + .add_superinterface(ClassName.get("java.io", "Serializable")) \ + .add_superinterface(ClassName.get("java.lang", "Cloneable")) \ + .build() +print(str(extended_class)) +# Output: +# public class MyClass extends BaseClass implements Serializable, Cloneable { +# } + +# Interface +interface = TypeSpec.interface_builder("Drawable") \ + .add_modifiers(Modifier.PUBLIC) \ + .add_method(MethodSpec.method_builder("draw") + .add_modifiers(Modifier.ABSTRACT, Modifier.PUBLIC) + .returns("void") + .build()) \ + .build() +print(str(interface)) +# Output: +# public interface Drawable { +# public abstract void draw(); +# } + +# Enum +color_enum = TypeSpec.enum_builder("Color") \ + .add_modifiers(Modifier.PUBLIC) \ + .add_enum_constant("RED") \ + .add_enum_constant("GREEN") \ + .add_enum_constant("BLUE") \ + .build() +print(str(color_enum)) +# Output: +# public enum Color { +# RED, +# GREEN, +# BLUE +# } + +# Annotation type +annotation_type = TypeSpec.annotation_builder("MyAnnotation") \ + .add_modifiers(Modifier.PUBLIC) \ + .add_method(MethodSpec.method_builder("value") + .add_modifiers(Modifier.ABSTRACT, Modifier.PUBLIC) + .returns(ClassName.get("java.lang", "String")) + .build()) \ + .build() +print(str(annotation_type)) +# Output: +# public @interface MyAnnotation { +# public abstract String value(); +# } + +# Record (Java 14+) +point_record = TypeSpec.record_builder("Point") \ + .add_modifiers(Modifier.PUBLIC) \ + .add_record_component(ParameterSpec.builder("int", "x").build()) \ + .add_record_component(ParameterSpec.builder("int", "y").build()) \ + .build() +print(str(point_record)) +# Output: +# public record Point(int x, int y) { +# } + +# Anonymous class +anonymous = TypeSpec.anonymous_class_builder("") \ + .add_superinterface(ClassName.get("java.lang", "Runnable")) \ + .add_method(MethodSpec.method_builder("run") + .add_modifiers(Modifier.PUBLIC) + .returns("void") + .add_statement("System.out.println($S)", "Running!") + .build()) \ + .build() +print(str(anonymous)) +# Output: +# new Runnable() { +# @Override +# public void run() { +# System.out.println("Running!"); +# } +# } +``` + +#### JavaFile + +Represents a complete Java source file with package, imports, and type declarations. + +```python +from pyjavapoet import JavaFile, TypeSpec, Modifier + +# Basic Java file +simple_class = TypeSpec.class_builder("HelloWorld") \ + .add_modifiers(Modifier.PUBLIC) \ + .build() + +java_file = JavaFile.builder("com.example", simple_class).build() +print(str(java_file)) +# Output: +# package com.example; +# +# public class HelloWorld { +# } + +# Java file with imports and file comment +java_file_with_imports = JavaFile.builder("com.example", simple_class) \ + .add_file_comment("This is a generated file.") \ + .add_file_comment("Do not edit manually.") \ + .add_static_import(ClassName.get("java.lang", "System"), "out") \ + .build() +print(str(java_file_with_imports)) +# Output: +# /** +# * This is a generated file. +# * Do not edit manually. +# */ +# package com.example; +# +# import static java.lang.System.out; +# +# public class HelloWorld { +# } +``` + +### Builder Pattern Methods + +All spec classes follow the builder pattern with these common methods: + +- **`.builder()`** - Creates a new builder instance +- **`.to_builder()`** - Creates a builder from existing spec +- **`.build()`** - Builds the final immutable spec +- **`.add_*(...)`** - Adds elements (modifiers, annotations, etc.) +- **`.returns(type)`** - Sets return type (MethodSpec only) +- **`.add_statement(format, ...args)`** - Adds code statements +- **`.begin_control_flow()`** / **`.end_control_flow()`** - Control structures + +### Placeholder Syntax + +PyJavaPoet uses placeholder syntax for safe code generation: + +- **`$T`** - Type (TypeName, ClassName, etc.) +- **`$S`** - String literal (automatically escaped) +- **`$L`** - Literal (numbers, variables, etc.) +- **`$N`** - Name (field/method names from specs) + +```python +# Examples of placeholder usage +method = MethodSpec.method_builder("example") \ + .add_statement("$T list = new $T<>()", ClassName.get("java.util", "List"), ClassName.get("java.util", "ArrayList")) \ + .add_statement("list.add($S)", "Hello World") \ + .add_statement("int size = $L", 42) \ + .build() ``` ## TODOs -1. Add better api for beginStatement and endStatement in MethodSpec -2. TreeSitter API to synactically validate java file -3. Add kwargs to method spec builder. Currently code block will have an issue of overwriting previous +1. TreeSitter API to synactically validate java file +2. Add kwargs to method spec builder. Currently code block will have an issue of overwriting previous keys if re-specified and so I have removed it. -4. Text wrapping on CodeWriter -5. Code Block update statement to use `$[` and `$]` -6. Name Allocator if we so desire (?) -7. Annotation member has to be valid java identifier -8. Handle primitive types better in ClassName i.e. validation -9. Improve tests with exact output strings and also slim down unneeded tests -10. Pass in TypeSpec for Types as well (for nested classes) ? It might work and we can include a self key too +3. Text wrapping on CodeWriter +4. Code Block update statement to use `$[` and `$]` +5. Name Allocator if we so desire (?) +6. Annotation member has to be valid java identifier +7. Handle primitive types better in ClassName i.e. validation +8. Improve tests with exact output strings and also slim down unneeded tests +9. Pass in TypeSpec for Types as well (for nested classes) ? It might work and we can include a self key too ## License diff --git a/pyjavapoet/annotation_spec.py b/pyjavapoet/annotation_spec.py index 08f7c73..126ae12 100644 --- a/pyjavapoet/annotation_spec.py +++ b/pyjavapoet/annotation_spec.py @@ -26,12 +26,11 @@ from typing import Any, Union -from code_base import Code -from util import deep_copy, throw_if_invalid_java_identifier - +from pyjavapoet.code_base import Code from pyjavapoet.code_block import CodeBlock from pyjavapoet.code_writer import CodeWriter from pyjavapoet.type_name import TypeName +from pyjavapoet.util import deep_copy, throw_if_invalid_java_identifier class AnnotationSpec(Code["AnnotationSpec"]): diff --git a/pyjavapoet/code_block.py b/pyjavapoet/code_block.py index 7a5a779..27c3c76 100644 --- a/pyjavapoet/code_block.py +++ b/pyjavapoet/code_block.py @@ -25,11 +25,10 @@ import re from typing import Any, Optional -from code_base import Code -from util import deep_copy - +from pyjavapoet.code_base import Code from pyjavapoet.code_writer import CodeWriter from pyjavapoet.type_name import TypeName +from pyjavapoet.util import deep_copy class CodeBlock(Code["CodeBlock"]): diff --git a/pyjavapoet/field_spec.py b/pyjavapoet/field_spec.py index 6475b41..9811a50 100644 --- a/pyjavapoet/field_spec.py +++ b/pyjavapoet/field_spec.py @@ -25,13 +25,13 @@ from typing import TYPE_CHECKING, Optional, Union -from code_base import Code -from util import deep_copy - from pyjavapoet.annotation_spec import AnnotationSpec +from pyjavapoet.code_base import Code from pyjavapoet.code_block import CodeBlock +from pyjavapoet.code_writer import CodeWriter from pyjavapoet.modifier import Modifier from pyjavapoet.type_name import TypeName +from pyjavapoet.util import deep_copy if TYPE_CHECKING: from pyjavapoet.code_writer import CodeWriter diff --git a/pyjavapoet/method_spec.py b/pyjavapoet/method_spec.py index f0bcf2b..0e9cab5 100644 --- a/pyjavapoet/method_spec.py +++ b/pyjavapoet/method_spec.py @@ -33,9 +33,8 @@ from enum import Enum, auto from typing import Optional, Union -from code_base import Code - from pyjavapoet.annotation_spec import AnnotationSpec +from pyjavapoet.code_base import Code from pyjavapoet.code_block import CodeBlock from pyjavapoet.code_writer import EMPTY_STRING, CodeWriter from pyjavapoet.modifier import Modifier @@ -92,7 +91,7 @@ def __init__( def emit(self, code_writer: "CodeWriter") -> None: # Emit Javadoc - if self.javadoc is not None: + if self.javadoc: self.javadoc.emit_javadoc(code_writer) code_writer.emit("\n") @@ -279,7 +278,7 @@ def add_type_variable(self, type_variable: "TypeVariableName") -> "MethodSpec.Bu self.__type_variables.append(type_variable) return self - def add_javadoc(self, format_string: str = EMPTY_STRING, *args) -> "MethodSpec.Builder": + def add_javadoc_line(self, format_string: str = EMPTY_STRING, *args) -> "MethodSpec.Builder": self.__javadoc = CodeBlock.add_javadoc(self.__javadoc, format_string, *args) return self @@ -303,7 +302,7 @@ def add_statement(self, format_string: str, *args) -> "MethodSpec.Builder": self.__code_builder.add_statement(format_string, *args) return self - def begin_statement(self, format_string: str, *args) -> "MethodSpec.Builder": + def begin_statement_chain(self, format_string: str, *args) -> "MethodSpec.Builder": if self.__kind == MethodSpec.Kind.COMPACT_CONSTRUCTOR: raise ValueError("Compact constructors cannot have a body") @@ -311,7 +310,7 @@ def begin_statement(self, format_string: str, *args) -> "MethodSpec.Builder": self.__code_builder.begin_statement(format_string, *args) return self - def add_statement_item(self, format_string: str, *args) -> "MethodSpec.Builder": + def add_chained_item(self, format_string: str, *args) -> "MethodSpec.Builder": if self.__kind == MethodSpec.Kind.COMPACT_CONSTRUCTOR: raise ValueError("Compact constructors cannot have a body") @@ -319,7 +318,7 @@ def add_statement_item(self, format_string: str, *args) -> "MethodSpec.Builder": self.__code_builder.add_statement_item(format_string, *args) return self - def end_statement(self) -> "MethodSpec.Builder": + def end_statement_chain(self) -> "MethodSpec.Builder": if self.__kind == MethodSpec.Kind.COMPACT_CONSTRUCTOR: raise ValueError("Compact constructors cannot have a body") diff --git a/pyjavapoet/parameter_spec.py b/pyjavapoet/parameter_spec.py index 289040d..4c4c4df 100644 --- a/pyjavapoet/parameter_spec.py +++ b/pyjavapoet/parameter_spec.py @@ -33,12 +33,11 @@ import re from typing import TYPE_CHECKING, List, Set, Union -from code_base import Code -from util import deep_copy, throw_if_invalid_java_identifier - from pyjavapoet.annotation_spec import AnnotationSpec +from pyjavapoet.code_base import Code from pyjavapoet.modifier import Modifier from pyjavapoet.type_name import ArrayTypeName, TypeName, TypeVariableName +from pyjavapoet.util import deep_copy, throw_if_invalid_java_identifier if TYPE_CHECKING: from pyjavapoet.code_writer import CodeWriter diff --git a/pyjavapoet/type_spec.py b/pyjavapoet/type_spec.py index 3e36571..337dc0b 100644 --- a/pyjavapoet/type_spec.py +++ b/pyjavapoet/type_spec.py @@ -29,11 +29,10 @@ from enum import Enum, auto from typing import Optional, Union -from code_base import Code - from pyjavapoet.annotation_spec import AnnotationSpec +from pyjavapoet.code_base import Code from pyjavapoet.code_block import CodeBlock -from pyjavapoet.code_writer import CodeWriter +from pyjavapoet.code_writer import EMPTY_STRING, CodeWriter from pyjavapoet.field_spec import FieldSpec from pyjavapoet.method_spec import MethodSpec from pyjavapoet.modifier import Modifier @@ -448,7 +447,7 @@ def add_permitted_subclass(self, subclass: Union["TypeName", str, type]) -> "Typ self.__permitted_subclasses.append(subclass) return self - def add_javadoc(self, format_string: str, *args) -> "TypeSpec.Builder": + def add_javadoc_line(self, format_string: str = EMPTY_STRING, *args) -> "TypeSpec.Builder": self.__javadoc = CodeBlock.add_javadoc(self.__javadoc, format_string, *args) return self diff --git a/pyproject.toml b/pyproject.toml index a5e3660..1303553 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,8 +40,8 @@ known-first-party = ["pyjavapoet"] lines-between-types = 0 [tool.setuptools] -package-dir = { "" = "pyjavapoet" } - +package-dir = { "pyjavapoet" = "pyjavapoet" } +packages = ["pyjavapoet"] [tool.setuptools.package-data] # Add resources if required diff --git a/tests/test_code_block.py b/tests/test_code_block.py index c5536fe..10a4217 100644 --- a/tests/test_code_block.py +++ b/tests/test_code_block.py @@ -16,9 +16,8 @@ import unittest -from code_writer import CodeWriter - from pyjavapoet.code_block import CodeBlock +from pyjavapoet.code_writer import CodeWriter from pyjavapoet.type_name import ClassName diff --git a/tests/test_java_file.py b/tests/test_java_file.py index 3c497d8..f241859 100644 --- a/tests/test_java_file.py +++ b/tests/test_java_file.py @@ -681,9 +681,9 @@ def test_file_with_comments(self): """Test file with various types of comments.""" method = ( MethodSpec.method_builder("documented") - .add_javadoc("This is a documented method.") - .add_javadoc("@param none no parameters") - .add_javadoc("@return nothing") + .add_javadoc_line("This is a documented method.") + .add_javadoc_line("@param none no parameters") + .add_javadoc_line("@return nothing") .add_modifiers(Modifier.PUBLIC) .returns("void") .add_statement("// Single line comment") @@ -692,7 +692,10 @@ def test_file_with_comments(self): ) type_spec = ( - TypeSpec.class_builder("Commented").add_javadoc("This is a documented class.\n").add_method(method).build() + TypeSpec.class_builder("Commented") + .add_javadoc_line("This is a documented class.\n") + .add_method(method) + .build() ) java_file = JavaFile.builder("com.example", type_spec).add_generated_by("pyjavapoet").build() diff --git a/tests/test_method_spec.py b/tests/test_method_spec.py index dbd2afe..d9fed10 100644 --- a/tests/test_method_spec.py +++ b/tests/test_method_spec.py @@ -58,10 +58,10 @@ def test_method_with_javadoc(self): """Test method with javadoc.""" method = ( MethodSpec.method_builder("calculate") - .add_javadoc("Calculates the result.") - .add_javadoc() - .add_javadoc("@param $L", "input the input value") - .add_javadoc("@return the calculated result") + .add_javadoc_line("Calculates the result.") + .add_javadoc_line() + .add_javadoc_line("@param $L", "input the input value") + .add_javadoc_line("@return the calculated result") .add_modifiers(Modifier.PUBLIC) .returns("int") .add_parameter("int", "input") @@ -372,10 +372,10 @@ def test_statement_builder(self): method = ( MethodSpec.method_builder("test") .add_statement("StringBuilder $L = new StringBuilder()", "builder") - .begin_statement("$L", "builder") - .add_statement_item(".append($S)", "hello") - .add_statement_item(".append($S)", "world") - .end_statement() + .begin_statement_chain("$L", "builder") + .add_chained_item(".append($S)", "hello") + .add_chained_item(".append($S)", "world") + .end_statement_chain() .build() ) result = str(method) diff --git a/tests/test_type_spec.py b/tests/test_type_spec.py index 7ed93ef..86a4f48 100644 --- a/tests/test_type_spec.py +++ b/tests/test_type_spec.py @@ -370,10 +370,10 @@ def test_class_with_javadoc(self): """Test class with javadoc.""" clazz = ( TypeSpec.class_builder("Documented") - .add_javadoc("This is a documented class.\n") - .add_javadoc("\n") - .add_javadoc("@author PyJavaPoet\n") - .add_javadoc("@since 1.0\n") + .add_javadoc_line("This is a documented class.\n") + .add_javadoc_line("\n") + .add_javadoc_line("@author PyJavaPoet\n") + .add_javadoc_line("@since 1.0\n") .add_modifiers(Modifier.PUBLIC) .build() )