Skip to content

Commit

Permalink
codegen-java: Support not annotating constructor parameters
Browse files Browse the repository at this point in the history
Motivation:
Spring Boot configuration classes neither require nor benefit from annotating constructor parameters with their name.
The same is true for pkl-config-java configuration classes compiled with `-parameter`.

Changes:
- Change CLI parameter `--params-annotation` to accept a `none` value.
  This is recommended in https://clig.dev/#arguments-and-flags and is how the `-F` parameter of the `ssh` command works.
- Change `paramsAnnotation` property in Gradle plugin, CliJavaCodeGeneratorOptions, and JavaCodegenOptions as follows:
  - Change meaning of `null` from "generate org.pkl.java.config.mapper.Named annotations" to "do not generate annotations".
    This is a breaking change (only) affecting users who explicitly set the property to `null` instead of omitting it.
  - Change property default from `null` to:
    `null` if `generateSpringBootConfig` is `true` and `org.pkl.java.config.mapper.Named` otherwise
- add tests
- update docs of this and other codegen options

Result:
Generated code does not contain unnecessary annotations.
  • Loading branch information
translatenix committed Nov 14, 2024
1 parent 9a27616 commit 39d2195
Show file tree
Hide file tree
Showing 13 changed files with 185 additions and 114 deletions.
16 changes: 10 additions & 6 deletions docs/modules/java-binding/pages/codegen.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -145,23 +145,27 @@ Flag that indicates to generate private final fields and public getter methods i
[%collapsible]
====
Default: (flag not set) +
Flag that indicates to generate Javadoc based on doc comments for Pkl modules, classes, and properties.
Flag that indicates to preserve Pkl doc comments by generating Javadoc comments.
====

.--params-annotation
[%collapsible]
====
Default: `org.pkl.config.java.mapper.Named` +
Fully qualified name of the annotation to use on constructor parameters.
Default: `none` if `--generate-spring-boot` is set, `org.pkl.config.java.mapper.Named` otherwise +
Fully qualified name of the annotation type to use for annotating constructor parameters with their name. +
The specified annotation type must have a `value` parameter of type `String` or the generated code may not compile.
If set to `none`, constructor parameters are not annotated.
Whether and how constructor parameters should be annotated depends on the library that instantiates the generated classes.
For Spring Boot applications, and for users of `pkl-config-java` compiling the generated classes with `-parameters`, no annotation is required.
====

.--non-null-annotation
[%collapsible]
====
Default: `org.pkl.config.java.mapper.NonNull` +
Fully qualified named of the annotation class to use for non-null types. +
This annotation is required to have `java.lang.annotation.ElementType.TYPE_USE` as a `@Target`
or it may generate code that does not compile.
Fully qualified name of the annotation type to use for annotating non-null types. +
The specified annotation type must be annotated with `@java.lang.annotation.Target(ElementType.TYPE_USE)`
or the generated code may not compile.
====

Common code generator options:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Flag that indicates to generate config classes for use with Spring Boot.
[%collapsible]
====
Default: (not set) +
Whether to make generated classes implement `java.io.Serializable`.
Flag that indicates to generate classes that implement `java.io.Serializable`.
====

.--rename
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/kotlin-binding/pages/codegen.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ Relative URIs are resolved against the working directory.
[%collapsible]
====
Default: (flag not set) +
Flag that indicates to generate Kdoc based on doc comments for Pkl modules, classes, and properties.
Flag that indicates to preserve Pkl doc comments by generating KDoc comments.
====

Common code generator options:
Expand Down
35 changes: 27 additions & 8 deletions docs/modules/pkl-gradle/pages/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -373,14 +373,26 @@ Example: `generateGetters = true` +
Whether to generate private final fields and public getter methods rather than public final fields.
====

// TODO: fixme (paramsAnnotation, nonNullAnnotation)
.preferJavaxInjectAnnotation: Boolean
.paramsAnnotation: Property<String>
[%collapsible]
====
Default: `false` +
Example: `preferJavaxInjectAnnotation = true` +
Whether to annotate constructor parameters with `@javax.inject.Named` instead of `@org.pkl.config.java.mapper.Named`.
If `true`, the generated code will have a compile dependency on `javax.inject:javax.inject:1`.
Default: `null` if `generateSpringBootConfig` is `true`, `"org.pkl.config.java.mapper.Named"` otherwise+
Example: `paramsAnnotation = "org.project.MyAnnotation"` +
Fully qualified name of the annotation type to use for annotating constructor parameters with their name. +
The specified annotation type must have a `value` parameter of type `String` or the generated code may not compile.
If set to `null`, constructor parameters are not annotated.
Whether and how constructor parameters should be annotated depends on the library that instantiates the generated classes.
For Spring Boot applications, and for users of `pkl-config-java` compiling the generated classes with `-parameters`, no annotation is required.
====
.nonNullAnnotation: Property<String>
[%collapsible]
====
Default: `"org.pkl.config.java.mapper.NonNull"` +
Example: `nonNullAnnotation = "org.project.MyAnnotation"` +
Fully qualified name of the annotation type to use for annotating non-null types. +
The specified annotation type must be annotated with `@java.lang.annotation.Target(ElementType.TYPE_USE)`
or the generated code may not compile.
====

Common code generation properties:
Expand Down Expand Up @@ -443,8 +455,15 @@ see link:{uri-codegen-kotlin-example}[codegen-kotlin] in the _pkl/pkl-examples_

=== Configuration Options

// TODO: fixme (generateKdoc)
(None)
=== Configuration Options

.generateKdoc: Property<Boolean>
[%collapsible]
====
Default: `false` +
Example: `generateKdoc = true` +
Whether to preserve Pkl doc comments by generating KDoc comments.
====

Common code generation properties:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ Example: `generateSpringBootConfig = true` +
Whether to generate config classes for use with Spring Boot.
====

.implementSerializable: Property<Boolean>
[%collapsible]
====
Default: `false` +
Example: `implementSerializable = true` +
Whether to generate classes that implement `java.io.Serializable`.
====

.renames: MapProperty<String, String>
[%collapsible]
====
Expand Down Expand Up @@ -86,4 +94,3 @@ Keys in this mapping can be arbitrary strings, including an empty string.
Values must be valid dot-separated fully qualifed class name prefixes, possibly terminated by a dot.
====

// TODO: fixme (implementSerializable)
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,35 @@ data class CliJavaCodeGeneratorOptions(
*/
val generateGetters: Boolean = false,

/** Whether to generate Javadoc based on doc comments for Pkl modules, classes, and properties. */
/** Whether to preserve Pkl doc comments by generating Javadoc comments. */
val generateJavadoc: Boolean = false,

/** Whether to generate config classes for use with Spring Boot. */
val generateSpringBootConfig: Boolean = false,

/**
* Fully qualified name of the annotation to use on constructor parameters. If this options is not
* set, [org.pkl.config.java.mapper.Named] will be used.
* Fully qualified name of the annotation type to use for annotating constructor parameters with
* their name.
*
* The specified annotation type must have a `value` parameter of type [java.lang.String] or the
* generated code may not compile.
*
* If set to `null`, constructor parameters are not annotated. The default value is `null` if
* [generateSpringBootConfig] is `true` and `"org.pkl.config.java.mapper.Named"` otherwise.
*/
val paramsAnnotation: String? = null,
val paramsAnnotation: String? =
if (generateSpringBootConfig) null else "org.pkl.config.java.mapper.Named",

/**
* Fully qualified name of the annotation to use on non-null properties. If this option is not
* set, [org.pkl.config.java.mapper.NonNull] will be used.
* Fully qualified name of the annotation type to use for annotating non-null types.
*
* The specified annotation type must have a [java.lang.annotation.Target] of
* [java.lang.annotation.ElementType.TYPE_USE] or the generated code may not compile. If set to
* `null`, [org.pkl.config.java.mapper.NonNull] will be used.
*/
val nonNullAnnotation: String? = null,

/** Whether to make generated classes implement [java.io.Serializable] */
/** Whether to generate classes that implement [java.io.Serializable]. */
val implementSerializable: Boolean = false,

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,25 +54,35 @@ data class JavaCodeGeneratorOptions(
*/
val generateGetters: Boolean = false,

/** Whether to generate Javadoc based on doc comments for Pkl modules, classes, and properties. */
/** Whether to preserve Pkl doc comments by generating Javadoc comments. */
val generateJavadoc: Boolean = false,

/** Whether to generate config classes for use with Spring Boot. */
val generateSpringBootConfig: Boolean = false,

/**
* Fully qualified name of the annotation to use on constructor parameters. If this options is not
* set, [org.pkl.config.java.mapper.Named] will be used.
* Fully qualified name of the annotation type to use for annotating constructor parameters with
* their name.
*
* The specified annotation type must have a `value` parameter of type [java.lang.String] or the
* generated code may not compile.
*
* If set to `null`, constructor parameters are not annotated. The default value is `null` if
* [generateSpringBootConfig] is `true` and `"org.pkl.config.java.mapper.Named"` otherwise.
*/
val paramsAnnotation: String? = null,
val paramsAnnotation: String? =
if (generateSpringBootConfig) null else "org.pkl.config.java.mapper.Named",

/**
* Fully qualified name of the annotation to use on non-null properties. If this option is not
* set, [org.pkl.config.java.mapper.NonNull] will be used.
* Fully qualified name of the annotation type to use for annotating non-null types.
*
* The specified annotation type must have a [java.lang.annotation.Target] of
* [java.lang.annotation.ElementType.TYPE_USE] or the generated code may not compile. If set to
* `null`, [org.pkl.config.java.mapper.NonNull] will be used.
*/
val nonNullAnnotation: String? = null,

/** Whether to make generated classes implement [java.io.Serializable] */
/** Whether to generate classes that implement [java.io.Serializable]. */
val implementSerializable: Boolean = false,

/**
Expand Down Expand Up @@ -232,15 +242,15 @@ class JavaCodeGenerator(
propJavaName: String,
property: PClass.Property
) {
builder.addParameter(
ParameterSpec.builder(property.type.toJavaPoetName(), propJavaName)
.addAnnotation(
AnnotationSpec.builder(namedAnnotationName)
.addMember("value", "\$S", property.simpleName)
.build()
)
.build()
)
val paramBuilder = ParameterSpec.builder(property.type.toJavaPoetName(), propJavaName)
if (paramsAnnotationName != null) {
paramBuilder.addAnnotation(
AnnotationSpec.builder(paramsAnnotationName)
.addMember("value", "\$S", property.simpleName)
.build()
)
}
builder.addParameter(paramBuilder.build())
}

fun generateConstructor(isInstantiable: Boolean): MethodSpec {
Expand Down Expand Up @@ -670,12 +680,8 @@ class JavaCodeGenerator(
return builder
}

private val namedAnnotationName =
if (codegenOptions.paramsAnnotation != null) {
toClassName(codegenOptions.paramsAnnotation)
} else {
ClassName.get("org.pkl.config.java.mapper", "Named")
}
private val paramsAnnotationName: ClassName? =
codegenOptions.paramsAnnotation?.let { toClassName(it) }

private fun appendPropertyMethod() =
MethodSpec.methodBuilder("appendProperty")
Expand Down
41 changes: 21 additions & 20 deletions pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@

package org.pkl.codegen.java

import com.github.ajalt.clikt.parameters.options.associate
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.*
import com.github.ajalt.clikt.parameters.types.path
import java.nio.file.Path
import org.pkl.commons.cli.CliBaseOptions
Expand Down Expand Up @@ -71,41 +68,45 @@ class PklJavaCodegenCommand :
private val generateJavadoc: Boolean by
option(
names = arrayOf("--generate-javadoc"),
help =
"Whether to generate Javadoc based on doc comments " +
"for Pkl modules, classes, and properties."
help = "Whether to preserve Pkl doc comments by generating Javadoc comments."
)
.flag()

private val generateSpringboot: Boolean by
private val generateSpringBoot: Boolean by
option(
names = arrayOf("--generate-spring-boot"),
help = "Whether to generate config classes for use with Spring boot."
help = "Whether to generate config classes for use with Spring Boot."
)
.flag()

private val paramsAnnotation: String? by
private val paramsAnnotation: String by
option(
names = arrayOf("--params-annotation"),
help = "Fully qualified name of the annotation to use on constructor parameters."
)
names = arrayOf("--params-annotation"),
help =
"Fully qualified name of the annotation type to use for annotating constructor parameters with their name."
)
.defaultLazy(
"`none` if `--generate-spring-boot` is set, `org.pkl.config.java.mapper.Named` otherwise"
) {
if (generateSpringBoot) "none" else "org.pkl.config.java.mapper.Named"
}

private val nonNullAnnotation: String? by
option(
names = arrayOf("--non-null-annotation"),
help =
"""
Fully qualified named of the annotation class to use for non-null types.
This annotation is required to have `java.lang.annotation.ElementType.TYPE_USE` as a `@Target`
or it may generate code that does not compile.
Fully qualified name of the annotation type to use for annotating non-null types.
The specified annotation type must be annotated with `@java.lang.annotation.Target(ElementType.TYPE_USE)`
or the generated code may not compile.
"""
.trimIndent()
)

private val implementSerializable: Boolean by
option(
names = arrayOf("--implement-serializable"),
help = "Whether to make generated classes implement java.io.Serializable."
help = "Whether to generate classes that implement java.io.Serializable."
)
.flag()

Expand All @@ -117,7 +118,7 @@ class PklJavaCodegenCommand :
"""
Replace a prefix in the names of the generated Java classes (repeatable).
By default, the names of generated classes are derived from the Pkl module names.
With this option, you can override the modify the default names, renaming entire
With this option, you can override or modify the default names, renaming entire
classes or just their packages.
"""
.trimIndent()
Expand All @@ -132,8 +133,8 @@ class PklJavaCodegenCommand :
indent = indent,
generateGetters = generateGetters,
generateJavadoc = generateJavadoc,
generateSpringBootConfig = generateSpringboot,
paramsAnnotation = paramsAnnotation,
generateSpringBootConfig = generateSpringBoot,
paramsAnnotation = if (paramsAnnotation == "none") null else paramsAnnotation,
nonNullAnnotation = nonNullAnnotation,
implementSerializable = implementSerializable,
renames = renames
Expand Down
Loading

0 comments on commit 39d2195

Please sign in to comment.