mirror of
https://github.com/apple/pkl.git
synced 2026-07-08 14:05:17 +02:00
Allow renaming Java/Kotlin classes/packages during code generation (#499)
Adds a `rename` field to the Java/Kotlin code generators that allows renaming packages and classes during codegen. * Add `--rename` flag to CLIs * Add `rename` property to Gradle API
This commit is contained in:
@@ -164,13 +164,6 @@ This annotation is required to have `java.lang.annotation.ElementType.TYPE_USE`
|
|||||||
or it may generate code that does not compile.
|
or it may generate code that does not compile.
|
||||||
====
|
====
|
||||||
|
|
||||||
.--implement-serializable
|
|
||||||
[%collapsible]
|
|
||||||
====
|
|
||||||
Default: (flag not set) +
|
|
||||||
Whether to make generated classes implement `java.io.Serializable`.
|
|
||||||
====
|
|
||||||
|
|
||||||
Common code generator options:
|
Common code generator options:
|
||||||
|
|
||||||
include::{partialsdir}/cli-codegen-options.adoc[]
|
include::{partialsdir}/cli-codegen-options.adoc[]
|
||||||
|
|||||||
@@ -21,3 +21,57 @@ Relative paths are resolved against the working directory.
|
|||||||
Default: (not set) +
|
Default: (not set) +
|
||||||
Flag that indicates to generate config classes for use with Spring Boot.
|
Flag that indicates to generate config classes for use with Spring Boot.
|
||||||
====
|
====
|
||||||
|
|
||||||
|
.--implement-serializable
|
||||||
|
[%collapsible]
|
||||||
|
====
|
||||||
|
Default: (not set) +
|
||||||
|
Whether to make generated classes implement `java.io.Serializable`.
|
||||||
|
====
|
||||||
|
|
||||||
|
.--rename
|
||||||
|
[%collapsible]
|
||||||
|
====
|
||||||
|
Default: (none) +
|
||||||
|
Example: `foo.=com.example.foo.` +
|
||||||
|
Allows to change default class and package names (derived from Pkl module names) in the generated code.
|
||||||
|
|
||||||
|
When you need the generated class or package names to be different from the default names derived from the Pkl module names, you can define a rename mapping, where the key is the original Pkl module name prefix, and the value is its replacement.
|
||||||
|
When you do, the generated code's `package` declarations, class names, as well as file locations, will be modified according to this mapping.
|
||||||
|
|
||||||
|
The prefixes are replaced literally, which means that dots at the end are important.
|
||||||
|
If you want to rename packages only, in most cases, you must ensure that you have an ending dot on both sides of a mapping (except for an empty mapping, if you use it), otherwise you may get unexpected results:
|
||||||
|
|
||||||
|
----
|
||||||
|
// Assuming the following arguments:
|
||||||
|
--rename com.foo.=x // Dot on the left only
|
||||||
|
--rename org.bar=y. // Dot on the right only
|
||||||
|
--rename net.baz=z // No dots
|
||||||
|
|
||||||
|
// The following renames will be made:
|
||||||
|
"com.foo.bar" -> "xbar" // Target prefix merged into the suffix
|
||||||
|
"org.bar.baz" -> "y..baz" // Double dot, invalid name
|
||||||
|
"net.baz.qux" -> "z.qux" // Looks okay, but...
|
||||||
|
"net.bazqux" -> "zqux" // ...may cut the name in the middle.
|
||||||
|
----
|
||||||
|
|
||||||
|
When computing the appropriate target name, the longest matching prefix is used:
|
||||||
|
|
||||||
|
----
|
||||||
|
// Assuming the following arguments:
|
||||||
|
--rename com.foo.Main=w.Main
|
||||||
|
--rename com.foo.=x.
|
||||||
|
--rename com.=y.
|
||||||
|
--rename =z.
|
||||||
|
|
||||||
|
// The following renames will be made:
|
||||||
|
com.foo.Main -> w.Main
|
||||||
|
com.foo.bar -> x.bar
|
||||||
|
com.baz.qux -> y.baz.qux
|
||||||
|
org.foo.bar -> z.org.foo.bar
|
||||||
|
----
|
||||||
|
|
||||||
|
Repeat this option to define multiple mappings.
|
||||||
|
Keys can be arbitrary strings, including an empty string.
|
||||||
|
Values must be valid dot-separated fully qualified class name prefixes, possibly terminated by a dot.
|
||||||
|
====
|
||||||
|
|||||||
@@ -36,4 +36,54 @@ Example: `generateSpringBootConfig = true` +
|
|||||||
Whether to generate config classes for use with Spring Boot.
|
Whether to generate config classes for use with Spring Boot.
|
||||||
====
|
====
|
||||||
|
|
||||||
|
.packageMapping: MapProperty<String, String>
|
||||||
|
[%collapsible]
|
||||||
|
====
|
||||||
|
Default: `[:]` +
|
||||||
|
Example: `packageMapping = ["foo.": "com.example.foo.", "bar.Config": "com.example.bar.Config"]` +
|
||||||
|
Allows to change default class and package names (derived from Pkl module names) in the generated code.
|
||||||
|
|
||||||
|
When you need the generated class or package names to be different from the default names derived from the Pkl module names, you can define a rename mapping, where the key is the original Pkl module name prefix, and the value is its replacement.
|
||||||
|
When you do, the generated code's `package` declarations, class names, as well as file locations, will be modified according to this mapping.
|
||||||
|
|
||||||
|
The prefixes are replaced literally, which means that dots at the end are important.
|
||||||
|
If you want to rename packages only, in most cases, you must ensure that you have an ending dot on both sides of a mapping (except for an empty mapping, if you use it), otherwise you may get unexpected results:
|
||||||
|
|
||||||
|
....
|
||||||
|
// Assuming the following mapping configuration:
|
||||||
|
packageMapping = [
|
||||||
|
"com.foo.": "x", // Dot on the left only
|
||||||
|
"org.bar": "y.", // Dot on the right only
|
||||||
|
"net.baz": "z" // No dots
|
||||||
|
]
|
||||||
|
|
||||||
|
// The following renames will be made:
|
||||||
|
"com.foo.bar" -> "xbar" // Target prefix merged into the suffix
|
||||||
|
"org.bar.baz" -> "y..baz" // Double dot, invalid name
|
||||||
|
"net.baz.qux" -> "z.qux" // Looks okay, but...
|
||||||
|
"net.bazqux" -> "zqux" // ...may cut the name in the middle.
|
||||||
|
....
|
||||||
|
|
||||||
|
When computing the appropriate target name, the longest matching prefix is used:
|
||||||
|
|
||||||
|
....
|
||||||
|
// Assuming the following mapping configuration:
|
||||||
|
packageMapping = [
|
||||||
|
"com.foo.Main": "w.Main",
|
||||||
|
"com.foo.": "x.",
|
||||||
|
"com.": "y.",
|
||||||
|
"": "z."
|
||||||
|
]
|
||||||
|
|
||||||
|
// The following renames will be made:
|
||||||
|
com.foo.Main -> w.Main
|
||||||
|
com.foo.bar -> x.bar
|
||||||
|
com.baz.qux -> y.baz.qux
|
||||||
|
org.foo.bar -> z.org.foo.bar
|
||||||
|
....
|
||||||
|
|
||||||
|
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)
|
// TODO: fixme (implementSerializable)
|
||||||
|
|||||||
+12
-2
@@ -54,7 +54,16 @@ data class CliJavaCodeGeneratorOptions(
|
|||||||
val nonNullAnnotation: String? = null,
|
val nonNullAnnotation: String? = null,
|
||||||
|
|
||||||
/** Whether to make generated classes implement [java.io.Serializable] */
|
/** Whether to make generated classes implement [java.io.Serializable] */
|
||||||
val implementSerializable: Boolean = false
|
val implementSerializable: Boolean = false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A rename mapping for class names.
|
||||||
|
*
|
||||||
|
* When you need to have Java class or package names different from the default names derived from
|
||||||
|
* Pkl module names, you can define a rename mapping, where the key is a prefix of the original
|
||||||
|
* Pkl module name, and the value is the desired replacement.
|
||||||
|
*/
|
||||||
|
val renames: Map<String, String> = emptyMap()
|
||||||
) {
|
) {
|
||||||
fun toJavaCodegenOptions() =
|
fun toJavaCodegenOptions() =
|
||||||
JavaCodegenOptions(
|
JavaCodegenOptions(
|
||||||
@@ -64,6 +73,7 @@ data class CliJavaCodeGeneratorOptions(
|
|||||||
generateSpringBootConfig,
|
generateSpringBootConfig,
|
||||||
paramsAnnotation,
|
paramsAnnotation,
|
||||||
nonNullAnnotation,
|
nonNullAnnotation,
|
||||||
implementSerializable
|
implementSerializable,
|
||||||
|
renames
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,10 @@ import kotlin.apply
|
|||||||
import kotlin.let
|
import kotlin.let
|
||||||
import kotlin.takeIf
|
import kotlin.takeIf
|
||||||
import kotlin.to
|
import kotlin.to
|
||||||
|
import org.pkl.commons.NameMapper
|
||||||
import org.pkl.core.*
|
import org.pkl.core.*
|
||||||
import org.pkl.core.util.CodeGeneratorUtils
|
import org.pkl.core.util.CodeGeneratorUtils
|
||||||
|
import org.pkl.core.util.IoUtils
|
||||||
|
|
||||||
class JavaCodeGeneratorException(message: String) : RuntimeException(message)
|
class JavaCodeGeneratorException(message: String) : RuntimeException(message)
|
||||||
|
|
||||||
@@ -68,7 +70,15 @@ data class JavaCodegenOptions(
|
|||||||
val nonNullAnnotation: String? = null,
|
val nonNullAnnotation: String? = null,
|
||||||
|
|
||||||
/** Whether to make generated classes implement [java.io.Serializable] */
|
/** Whether to make generated classes implement [java.io.Serializable] */
|
||||||
val implementSerializable: Boolean = false
|
val implementSerializable: Boolean = false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapping from Pkl module name prefixes to their replacements.
|
||||||
|
*
|
||||||
|
* Can be used when the class or package name in the generated source code should be different
|
||||||
|
* from the corresponding name derived from the Pkl module declaration .
|
||||||
|
*/
|
||||||
|
val renames: Map<String, String> = emptyMap()
|
||||||
)
|
)
|
||||||
|
|
||||||
/** Entrypoint for the Java code generator API. */
|
/** Entrypoint for the Java code generator API. */
|
||||||
@@ -123,7 +133,8 @@ class JavaCodeGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val propertyFileName: String
|
private val propertyFileName: String
|
||||||
get() = "resources/META-INF/org/pkl/config/java/mapper/classes/${schema.moduleName}.properties"
|
get() =
|
||||||
|
"resources/META-INF/org/pkl/config/java/mapper/classes/${IoUtils.encodePath(schema.moduleName)}.properties"
|
||||||
|
|
||||||
private val propertiesFile: String
|
private val propertiesFile: String
|
||||||
get() {
|
get() {
|
||||||
@@ -150,8 +161,16 @@ class JavaCodeGenerator(
|
|||||||
return AnnotationSpec.builder(className).build()
|
return AnnotationSpec.builder(className).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
val javaFileName: String
|
private val javaFileName: String
|
||||||
get() = relativeOutputPathFor(schema.moduleName)
|
get() {
|
||||||
|
val (packageName, className) = nameMapper.map(schema.moduleName)
|
||||||
|
val dirPath = packageName.replace('.', '/')
|
||||||
|
return if (dirPath.isEmpty()) {
|
||||||
|
"java/$className.java"
|
||||||
|
} else {
|
||||||
|
"java/$dirPath/$className.java"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val javaFile: String
|
val javaFile: String
|
||||||
get() {
|
get() {
|
||||||
@@ -183,9 +202,7 @@ class JavaCodeGenerator(
|
|||||||
moduleClass.addMethod(appendPropertyMethod().addModifiers(modifier).build())
|
moduleClass.addMethod(appendPropertyMethod().addModifiers(modifier).build())
|
||||||
}
|
}
|
||||||
|
|
||||||
val moduleName = schema.moduleName
|
val (packageName, _) = nameMapper.map(schema.moduleName)
|
||||||
val index = moduleName.lastIndexOf(".")
|
|
||||||
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
|
||||||
|
|
||||||
return JavaFile.builder(packageName, moduleClass.build())
|
return JavaFile.builder(packageName, moduleClass.build())
|
||||||
.indent(codegenOptions.indent)
|
.indent(codegenOptions.indent)
|
||||||
@@ -193,20 +210,7 @@ class JavaCodeGenerator(
|
|||||||
.toString()
|
.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun relativeOutputPathFor(moduleName: String): String {
|
|
||||||
val moduleNameParts = moduleName.split(".")
|
|
||||||
val dirPath = moduleNameParts.dropLast(1).joinToString("/")
|
|
||||||
val fileName = moduleNameParts.last().replaceFirstChar { it.titlecaseChar() }
|
|
||||||
return if (dirPath.isEmpty()) {
|
|
||||||
"java/$fileName.java"
|
|
||||||
} else {
|
|
||||||
"java/$dirPath/$fileName.java"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("NAME_SHADOWING")
|
|
||||||
private fun generateTypeSpec(pClass: PClass, schema: ModuleSchema): TypeSpec.Builder {
|
private fun generateTypeSpec(pClass: PClass, schema: ModuleSchema): TypeSpec.Builder {
|
||||||
|
|
||||||
val isModuleClass = pClass == schema.moduleClass
|
val isModuleClass = pClass == schema.moduleClass
|
||||||
val javaPoetClassName = pClass.toJavaPoetName()
|
val javaPoetClassName = pClass.toJavaPoetName()
|
||||||
val superclass =
|
val superclass =
|
||||||
@@ -687,9 +691,7 @@ class JavaCodeGenerator(
|
|||||||
.endControlFlow()
|
.endControlFlow()
|
||||||
|
|
||||||
private fun PClass.toJavaPoetName(): ClassName {
|
private fun PClass.toJavaPoetName(): ClassName {
|
||||||
val index = moduleName.lastIndexOf(".")
|
val (packageName, moduleClassName) = nameMapper.map(moduleName)
|
||||||
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
|
||||||
val moduleClassName = moduleName.substring(index + 1).replaceFirstChar { it.titlecaseChar() }
|
|
||||||
return if (isModuleClass) {
|
return if (isModuleClass) {
|
||||||
ClassName.get(packageName, moduleClassName)
|
ClassName.get(packageName, moduleClassName)
|
||||||
} else {
|
} else {
|
||||||
@@ -699,9 +701,7 @@ class JavaCodeGenerator(
|
|||||||
|
|
||||||
// generated type is a nested enum class
|
// generated type is a nested enum class
|
||||||
private fun TypeAlias.toJavaPoetName(): ClassName {
|
private fun TypeAlias.toJavaPoetName(): ClassName {
|
||||||
val index = moduleName.lastIndexOf(".")
|
val (packageName, moduleClassName) = nameMapper.map(moduleName)
|
||||||
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
|
||||||
val moduleClassName = moduleName.substring(index + 1).replaceFirstChar { it.titlecaseChar() }
|
|
||||||
return ClassName.get(packageName, moduleClassName, simpleName)
|
return ClassName.get(packageName, moduleClassName, simpleName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -872,6 +872,8 @@ class JavaCodeGenerator(
|
|||||||
} else key
|
} else key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val nameMapper = NameMapper(codegenOptions.renames)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val javaReservedWords =
|
internal val javaReservedWords =
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package org.pkl.codegen.java
|
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.default
|
||||||
import com.github.ajalt.clikt.parameters.options.flag
|
import com.github.ajalt.clikt.parameters.options.flag
|
||||||
import com.github.ajalt.clikt.parameters.options.option
|
import com.github.ajalt.clikt.parameters.options.option
|
||||||
@@ -108,6 +109,21 @@ class PklJavaCodegenCommand :
|
|||||||
)
|
)
|
||||||
.flag()
|
.flag()
|
||||||
|
|
||||||
|
private val renames: Map<String, String> by
|
||||||
|
option(
|
||||||
|
names = arrayOf("--rename"),
|
||||||
|
metavar = "<old_name=new_name>",
|
||||||
|
help =
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
classes or just their packages.
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
.associate()
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
val options =
|
val options =
|
||||||
CliJavaCodeGeneratorOptions(
|
CliJavaCodeGeneratorOptions(
|
||||||
@@ -119,7 +135,8 @@ class PklJavaCodegenCommand :
|
|||||||
generateSpringBootConfig = generateSpringboot,
|
generateSpringBootConfig = generateSpringboot,
|
||||||
paramsAnnotation = paramsAnnotation,
|
paramsAnnotation = paramsAnnotation,
|
||||||
nonNullAnnotation = nonNullAnnotation,
|
nonNullAnnotation = nonNullAnnotation,
|
||||||
implementSerializable = implementSerializable
|
implementSerializable = implementSerializable,
|
||||||
|
renames = renames
|
||||||
)
|
)
|
||||||
CliJavaCodeGenerator(options).run()
|
CliJavaCodeGenerator(options).run()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,6 +173,115 @@ class CliJavaCodeGeneratorTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `custom package names`(@TempDir tempDir: Path) {
|
||||||
|
val module1 =
|
||||||
|
PklModule(
|
||||||
|
"org.foo.Module1",
|
||||||
|
"""
|
||||||
|
module org.foo.Module1
|
||||||
|
|
||||||
|
class Person {
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
val module2 =
|
||||||
|
PklModule(
|
||||||
|
"org.bar.Module2",
|
||||||
|
"""
|
||||||
|
module org.bar.Module2
|
||||||
|
|
||||||
|
import "../../org/foo/Module1.pkl"
|
||||||
|
|
||||||
|
class Group {
|
||||||
|
owner: Module1.Person
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
val module3 =
|
||||||
|
PklModule(
|
||||||
|
"org.baz.Module3",
|
||||||
|
"""
|
||||||
|
module org.baz.Module3
|
||||||
|
|
||||||
|
import "../../org/bar/Module2.pkl"
|
||||||
|
|
||||||
|
class Supergroup {
|
||||||
|
owner: Module2.Group
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
val module1PklFile = module1.writeToDisk(tempDir.resolve("org/foo/Module1.pkl"))
|
||||||
|
val module2PklFile = module2.writeToDisk(tempDir.resolve("org/bar/Module2.pkl"))
|
||||||
|
val module3PklFile = module3.writeToDisk(tempDir.resolve("org/baz/Module3.pkl"))
|
||||||
|
val outputDir = tempDir.resolve("output")
|
||||||
|
|
||||||
|
val generator =
|
||||||
|
CliJavaCodeGenerator(
|
||||||
|
CliJavaCodeGeneratorOptions(
|
||||||
|
CliBaseOptions(listOf(module1PklFile, module2PklFile, module3PklFile).map { it.toUri() }),
|
||||||
|
outputDir,
|
||||||
|
renames = mapOf("org.foo" to "com.foo.x", "org.baz" to "com.baz.a.b")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
generator.run()
|
||||||
|
|
||||||
|
val module1JavaFile = outputDir.resolve("java/com/foo/x/Module1.java")
|
||||||
|
module1JavaFile.readString().let {
|
||||||
|
assertContains("package com.foo.x;", it)
|
||||||
|
|
||||||
|
assertContains("public final class Module1 {", it)
|
||||||
|
|
||||||
|
assertContains(
|
||||||
|
"""
|
||||||
|
| public static final class Person {
|
||||||
|
| public final @NonNull String name;
|
||||||
|
""",
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val module2JavaFile = outputDir.resolve("java/org/bar/Module2.java")
|
||||||
|
module2JavaFile.readString().let {
|
||||||
|
assertContains("package org.bar;", it)
|
||||||
|
|
||||||
|
assertContains("import com.foo.x.Module1;", it)
|
||||||
|
|
||||||
|
assertContains("public final class Module2 {", it)
|
||||||
|
|
||||||
|
assertContains(
|
||||||
|
"""
|
||||||
|
| public static final class Group {
|
||||||
|
| public final Module1. @NonNull Person owner;
|
||||||
|
""",
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val module3JavaFile = outputDir.resolve("java/com/baz/a/b/Module3.java")
|
||||||
|
module3JavaFile.readString().let {
|
||||||
|
assertContains("package com.baz.a.b;", it)
|
||||||
|
|
||||||
|
assertContains("import org.bar.Module2;", it)
|
||||||
|
|
||||||
|
assertContains("public final class Module3 {", it)
|
||||||
|
|
||||||
|
assertContains(
|
||||||
|
"""
|
||||||
|
| public static final class Supergroup {
|
||||||
|
| public final Module2. @NonNull Group owner;
|
||||||
|
""",
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun assertContains(part: String, code: String) {
|
private fun assertContains(part: String, code: String) {
|
||||||
val trimmedPart = part.trim().trimMargin()
|
val trimmedPart = part.trim().trimMargin()
|
||||||
if (!code.contains(trimmedPart)) {
|
if (!code.contains(trimmedPart)) {
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ object InMemoryJavaCompiler {
|
|||||||
val fileManager =
|
val fileManager =
|
||||||
InMemoryFileManager(compiler.getStandardFileManager(diagnosticsCollector, null, null))
|
InMemoryFileManager(compiler.getStandardFileManager(diagnosticsCollector, null, null))
|
||||||
val sourceObjects =
|
val sourceObjects =
|
||||||
sourceFiles.map { (filename, contents) -> ReadableSourceFileObject(filename, contents) }
|
sourceFiles
|
||||||
|
.filter { (filename, _) -> filename.endsWith(".java") }
|
||||||
|
.map { (filename, contents) -> ReadableSourceFileObject(filename, contents) }
|
||||||
val task = compiler.getTask(null, fileManager, diagnosticsCollector, null, null, sourceObjects)
|
val task = compiler.getTask(null, fileManager, diagnosticsCollector, null, null, sourceObjects)
|
||||||
val result = task.call()
|
val result = task.call()
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import org.pkl.core.util.IoUtils
|
|||||||
|
|
||||||
class JavaCodeGeneratorTest {
|
class JavaCodeGeneratorTest {
|
||||||
companion object {
|
companion object {
|
||||||
|
const val MAPPER_PREFIX = "resources/META-INF/org/pkl/config/java/mapper/classes"
|
||||||
|
|
||||||
private val simpleClass by lazy {
|
private val simpleClass by lazy {
|
||||||
compileJavaCode(
|
compileJavaCode(
|
||||||
generateJavaCode(
|
generateJavaCode(
|
||||||
@@ -101,7 +103,8 @@ class JavaCodeGeneratorTest {
|
|||||||
generateJavadoc: Boolean = false,
|
generateJavadoc: Boolean = false,
|
||||||
generateSpringBootConfig: Boolean = false,
|
generateSpringBootConfig: Boolean = false,
|
||||||
nonNullAnnotation: String? = null,
|
nonNullAnnotation: String? = null,
|
||||||
implementSerializable: Boolean = false
|
implementSerializable: Boolean = false,
|
||||||
|
renames: Map<String, String> = emptyMap()
|
||||||
): String {
|
): String {
|
||||||
val module = Evaluator.preconfigured().evaluateSchema(text(pklCode))
|
val module = Evaluator.preconfigured().evaluateSchema(text(pklCode))
|
||||||
val generator =
|
val generator =
|
||||||
@@ -113,6 +116,7 @@ class JavaCodeGeneratorTest {
|
|||||||
generateSpringBootConfig = generateSpringBootConfig,
|
generateSpringBootConfig = generateSpringBootConfig,
|
||||||
nonNullAnnotation = nonNullAnnotation,
|
nonNullAnnotation = nonNullAnnotation,
|
||||||
implementSerializable = implementSerializable,
|
implementSerializable = implementSerializable,
|
||||||
|
renames = renames
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return generator.javaFile
|
return generator.javaFile
|
||||||
@@ -127,6 +131,8 @@ class JavaCodeGeneratorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TempDir lateinit var tempDir: Path
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEquals() {
|
fun testEquals() {
|
||||||
val ctor = simpleClass.constructors.first()
|
val ctor = simpleClass.constructors.first()
|
||||||
@@ -1420,7 +1426,7 @@ class JavaCodeGeneratorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `import module`(@TempDir tempDir: Path) {
|
fun `import module`() {
|
||||||
val library =
|
val library =
|
||||||
PklModule(
|
PklModule(
|
||||||
"library",
|
"library",
|
||||||
@@ -1449,7 +1455,7 @@ class JavaCodeGeneratorTest {
|
|||||||
.trimIndent()
|
.trimIndent()
|
||||||
)
|
)
|
||||||
|
|
||||||
val javaSourceFiles = generateJavaFiles(tempDir, library, client)
|
val javaSourceFiles = generateFiles(library, client)
|
||||||
val javaClientCode =
|
val javaClientCode =
|
||||||
javaSourceFiles.entries.find { (fileName, _) -> fileName.endsWith("Client.java") }!!.value
|
javaSourceFiles.entries.find { (fileName, _) -> fileName.endsWith("Client.java") }!!.value
|
||||||
|
|
||||||
@@ -1467,7 +1473,7 @@ class JavaCodeGeneratorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `extend module`(@TempDir tempDir: Path) {
|
fun `extend module`() {
|
||||||
val base =
|
val base =
|
||||||
PklModule(
|
PklModule(
|
||||||
"base",
|
"base",
|
||||||
@@ -1496,7 +1502,7 @@ class JavaCodeGeneratorTest {
|
|||||||
.trimIndent()
|
.trimIndent()
|
||||||
)
|
)
|
||||||
|
|
||||||
val javaSourceFiles = generateJavaFiles(tempDir, base, derived)
|
val javaSourceFiles = generateFiles(base, derived)
|
||||||
val javaDerivedCode =
|
val javaDerivedCode =
|
||||||
javaSourceFiles.entries.find { (filename, _) -> filename.endsWith("Derived.java") }!!.value
|
javaSourceFiles.entries.find { (filename, _) -> filename.endsWith("Derived.java") }!!.value
|
||||||
|
|
||||||
@@ -1520,7 +1526,7 @@ class JavaCodeGeneratorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `extend module that only contains type aliases`(@TempDir tempDir: Path) {
|
fun `extend module that only contains type aliases`() {
|
||||||
val base =
|
val base =
|
||||||
PklModule(
|
PklModule(
|
||||||
"base",
|
"base",
|
||||||
@@ -1545,7 +1551,7 @@ class JavaCodeGeneratorTest {
|
|||||||
.trimIndent()
|
.trimIndent()
|
||||||
)
|
)
|
||||||
|
|
||||||
val javaSourceFiles = generateJavaFiles(tempDir, base, derived)
|
val javaSourceFiles = generateFiles(base, derived)
|
||||||
val javaDerivedCode =
|
val javaDerivedCode =
|
||||||
javaSourceFiles.entries.find { (filename, _) -> filename.endsWith("Derived.java") }!!.value
|
javaSourceFiles.entries.find { (filename, _) -> filename.endsWith("Derived.java") }!!.value
|
||||||
|
|
||||||
@@ -1561,7 +1567,7 @@ class JavaCodeGeneratorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `generated properties files`(@TempDir tempDir: Path) {
|
fun `generated properties files`() {
|
||||||
val pklModule =
|
val pklModule =
|
||||||
PklModule(
|
PklModule(
|
||||||
"Mod.pkl",
|
"Mod.pkl",
|
||||||
@@ -1582,7 +1588,7 @@ class JavaCodeGeneratorTest {
|
|||||||
"""
|
"""
|
||||||
.trimIndent()
|
.trimIndent()
|
||||||
)
|
)
|
||||||
val generated = generateFiles(tempDir, pklModule)
|
val generated = generateFiles(pklModule)
|
||||||
val expectedPropertyFile =
|
val expectedPropertyFile =
|
||||||
"resources/META-INF/org/pkl/config/java/mapper/classes/org.pkl.Mod.properties"
|
"resources/META-INF/org/pkl/config/java/mapper/classes/org.pkl.Mod.properties"
|
||||||
assertThat(generated).containsKey(expectedPropertyFile)
|
assertThat(generated).containsKey(expectedPropertyFile)
|
||||||
@@ -1596,7 +1602,7 @@ class JavaCodeGeneratorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `generated properties files with normalized java name`(@TempDir tempDir: Path) {
|
fun `generated properties files with normalized java name`() {
|
||||||
val pklModule =
|
val pklModule =
|
||||||
PklModule(
|
PklModule(
|
||||||
"mod.pkl",
|
"mod.pkl",
|
||||||
@@ -1617,7 +1623,7 @@ class JavaCodeGeneratorTest {
|
|||||||
"""
|
"""
|
||||||
.trimIndent()
|
.trimIndent()
|
||||||
)
|
)
|
||||||
val generated = generateFiles(tempDir, pklModule)
|
val generated = generateFiles(pklModule)
|
||||||
val expectedPropertyFile =
|
val expectedPropertyFile =
|
||||||
"resources/META-INF/org/pkl/config/java/mapper/classes/my.mod.properties"
|
"resources/META-INF/org/pkl/config/java/mapper/classes/my.mod.properties"
|
||||||
assertThat(generated).containsKey(expectedPropertyFile)
|
assertThat(generated).containsKey(expectedPropertyFile)
|
||||||
@@ -1824,24 +1830,235 @@ class JavaCodeGeneratorTest {
|
|||||||
assertCompilesSuccessfully(javaCode)
|
assertCompilesSuccessfully(javaCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateFiles(tempDir: Path, vararg pklModules: PklModule): Map<String, String> {
|
@Test
|
||||||
|
fun `override names in a standalone module`() {
|
||||||
|
val files =
|
||||||
|
JavaCodegenOptions(
|
||||||
|
renames = mapOf("a.b.c." to "x.y.z.", "d.e.f.AnotherModule" to "u.v.w.RenamedModule")
|
||||||
|
)
|
||||||
|
.generateFiles(
|
||||||
|
"MyModule.pkl" to
|
||||||
|
"""
|
||||||
|
module a.b.c.MyModule
|
||||||
|
|
||||||
|
foo: String = "abc"
|
||||||
|
"""
|
||||||
|
.trimIndent(),
|
||||||
|
"AnotherModule.pkl" to
|
||||||
|
"""
|
||||||
|
module d.e.f.AnotherModule
|
||||||
|
|
||||||
|
bar: Int = 123
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
.toMutableMap()
|
||||||
|
|
||||||
|
files.validateContents(
|
||||||
|
"java/x/y/z/MyModule.java" to listOf("package x.y.z;", "public final class MyModule {"),
|
||||||
|
"$MAPPER_PREFIX/a.b.c.MyModule.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.a.b.c.MyModule\\#ModuleClass=x.y.z.MyModule"),
|
||||||
|
// ---
|
||||||
|
"java/u/v/w/RenamedModule.java" to
|
||||||
|
listOf("package u.v.w;", "public final class RenamedModule {"),
|
||||||
|
"$MAPPER_PREFIX/d.e.f.AnotherModule.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.d.e.f.AnotherModule\\#ModuleClass=u.v.w.RenamedModule"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `override names based on the longest prefix`() {
|
||||||
|
val files =
|
||||||
|
JavaCodegenOptions(
|
||||||
|
renames = mapOf("com.foo.bar." to "x.", "com.foo." to "y.", "com." to "z.", "" to "w.")
|
||||||
|
)
|
||||||
|
.generateFiles(
|
||||||
|
"com/foo/bar/Module1" to
|
||||||
|
"""
|
||||||
|
module com.foo.bar.Module1
|
||||||
|
|
||||||
|
bar: String
|
||||||
|
"""
|
||||||
|
.trimIndent(),
|
||||||
|
"com/Module2" to
|
||||||
|
"""
|
||||||
|
module com.Module2
|
||||||
|
|
||||||
|
com: String
|
||||||
|
"""
|
||||||
|
.trimIndent(),
|
||||||
|
"org/baz/Module3" to
|
||||||
|
"""
|
||||||
|
module org.baz.Module3
|
||||||
|
|
||||||
|
baz: String
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
files.validateContents(
|
||||||
|
"java/x/Module1.java" to listOf("package x;", "public final class Module1 {"),
|
||||||
|
"$MAPPER_PREFIX/com.foo.bar.Module1.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.com.foo.bar.Module1\\#ModuleClass=x.Module1"),
|
||||||
|
// ---
|
||||||
|
"java/z/Module2.java" to listOf("package z;", "public final class Module2 {"),
|
||||||
|
"$MAPPER_PREFIX/com.Module2.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.com.Module2\\#ModuleClass=z.Module2"),
|
||||||
|
// ---
|
||||||
|
"java/w/org/baz/Module3.java" to listOf("package w.org.baz;", "public final class Module3 {"),
|
||||||
|
"$MAPPER_PREFIX/org.baz.Module3.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.org.baz.Module3\\#ModuleClass=w.org.baz.Module3"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `override names in multiple modules using each other`() {
|
||||||
|
val files =
|
||||||
|
JavaCodegenOptions(
|
||||||
|
renames =
|
||||||
|
mapOf(
|
||||||
|
"org.foo." to "com.foo.x.",
|
||||||
|
"org.bar.Module2" to "org.bar.RenamedModule",
|
||||||
|
"org.baz." to "com.baz.a.b."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.generateFiles(
|
||||||
|
"org/foo/Module1" to
|
||||||
|
"""
|
||||||
|
module org.foo.Module1
|
||||||
|
|
||||||
|
class Person {
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
.trimIndent(),
|
||||||
|
"org/bar/Module2" to
|
||||||
|
"""
|
||||||
|
module org.bar.Module2
|
||||||
|
|
||||||
|
import "../../org/foo/Module1.pkl"
|
||||||
|
|
||||||
|
class Group {
|
||||||
|
owner: Module1.Person
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
.trimIndent(),
|
||||||
|
"org/baz/Module3" to
|
||||||
|
"""
|
||||||
|
module org.baz.Module3
|
||||||
|
|
||||||
|
import "../../org/bar/Module2.pkl"
|
||||||
|
|
||||||
|
class Supergroup {
|
||||||
|
owner: Module2.Group
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
files.validateContents(
|
||||||
|
"java/com/foo/x/Module1.java" to listOf("package com.foo.x;", "public final class Module1 {"),
|
||||||
|
"$MAPPER_PREFIX/org.foo.Module1.properties" to
|
||||||
|
listOf(
|
||||||
|
"org.pkl.config.java.mapper.org.foo.Module1\\#ModuleClass=com.foo.x.Module1",
|
||||||
|
"org.pkl.config.java.mapper.org.foo.Module1\\#Person=com.foo.x.Module1${'$'}Person",
|
||||||
|
),
|
||||||
|
// ---
|
||||||
|
"java/org/bar/RenamedModule.java" to
|
||||||
|
listOf(
|
||||||
|
"package org.bar;",
|
||||||
|
"import com.foo.x.Module1;",
|
||||||
|
"public final class RenamedModule {",
|
||||||
|
"public final Module1. @NonNull Person owner;"
|
||||||
|
),
|
||||||
|
"$MAPPER_PREFIX/org.bar.Module2.properties" to
|
||||||
|
listOf(
|
||||||
|
"org.pkl.config.java.mapper.org.bar.Module2\\#ModuleClass=org.bar.RenamedModule",
|
||||||
|
"org.pkl.config.java.mapper.org.bar.Module2\\#Group=org.bar.RenamedModule${'$'}Group",
|
||||||
|
),
|
||||||
|
// ---
|
||||||
|
"java/com/baz/a/b/Module3.java" to
|
||||||
|
listOf(
|
||||||
|
"package com.baz.a.b;",
|
||||||
|
"import org.bar.RenamedModule;",
|
||||||
|
"public final class Module3 {",
|
||||||
|
"public final RenamedModule. @NonNull Group owner;"
|
||||||
|
),
|
||||||
|
"$MAPPER_PREFIX/org.baz.Module3.properties" to
|
||||||
|
listOf(
|
||||||
|
"org.pkl.config.java.mapper.org.baz.Module3\\#ModuleClass=com.baz.a.b.Module3",
|
||||||
|
"org.pkl.config.java.mapper.org.baz.Module3\\#Supergroup=com.baz.a.b.Module3${'$'}Supergroup",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `do not capitalize names of renamed classes`() {
|
||||||
|
val files =
|
||||||
|
JavaCodegenOptions(
|
||||||
|
renames = mapOf("a.b.c.MyModule" to "x.y.z.renamed_module", "d.e.f." to "u.v.w.")
|
||||||
|
)
|
||||||
|
.generateFiles(
|
||||||
|
"MyModule.pkl" to
|
||||||
|
"""
|
||||||
|
module a.b.c.MyModule
|
||||||
|
|
||||||
|
foo: String = "abc"
|
||||||
|
"""
|
||||||
|
.trimIndent(),
|
||||||
|
"lower_module.pkl" to
|
||||||
|
"""
|
||||||
|
module d.e.f.lower_module
|
||||||
|
|
||||||
|
bar: Int = 123
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
files.validateContents(
|
||||||
|
"java/x/y/z/renamed_module.java" to
|
||||||
|
listOf("package x.y.z;", "public final class renamed_module {"),
|
||||||
|
"$MAPPER_PREFIX/a.b.c.MyModule.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.a.b.c.MyModule\\#ModuleClass=x.y.z.renamed_module"),
|
||||||
|
// ---
|
||||||
|
"java/u/v/w/Lower_module.java" to
|
||||||
|
listOf("package u.v.w;", "public final class Lower_module {"),
|
||||||
|
"$MAPPER_PREFIX/d.e.f.lower_module.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.d.e.f.lower_module\\#ModuleClass=u.v.w.Lower_module"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Map<String, String>.validateContents(
|
||||||
|
vararg assertions: kotlin.Pair<String, List<String>>
|
||||||
|
) {
|
||||||
|
val files = toMutableMap()
|
||||||
|
|
||||||
|
for ((fileName, lines) in assertions) {
|
||||||
|
assertThat(files).containsKey(fileName)
|
||||||
|
assertThat(files.remove(fileName)).describedAs("Contents of $fileName").contains(lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(files).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JavaCodegenOptions.generateFiles(vararg pklModules: PklModule): Map<String, String> {
|
||||||
val pklFiles = pklModules.map { it.writeToDisk(tempDir.resolve("pkl/${it.name}.pkl")) }
|
val pklFiles = pklModules.map { it.writeToDisk(tempDir.resolve("pkl/${it.name}.pkl")) }
|
||||||
val evaluator = Evaluator.preconfigured()
|
val evaluator = Evaluator.preconfigured()
|
||||||
return pklFiles.fold(mapOf()) { acc, pklFile ->
|
return pklFiles.fold(mapOf()) { acc, pklFile ->
|
||||||
val pklSchema = evaluator.evaluateSchema(path(pklFile))
|
val pklSchema = evaluator.evaluateSchema(path(pklFile))
|
||||||
acc + JavaCodeGenerator(pklSchema, JavaCodegenOptions()).output
|
val generator = JavaCodeGenerator(pklSchema, this)
|
||||||
|
acc + generator.output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateJavaFiles(tempDir: Path, vararg pklModules: PklModule): Map<String, String> {
|
private fun JavaCodegenOptions.generateFiles(
|
||||||
val pklFiles = pklModules.map { it.writeToDisk(tempDir.resolve("pkl/${it.name}.pkl")) }
|
vararg pklModules: kotlin.Pair<String, String>
|
||||||
val evaluator = Evaluator.preconfigured()
|
): Map<String, String> =
|
||||||
return pklFiles.fold(mapOf()) { acc, pklFile ->
|
generateFiles(*pklModules.map { (name, text) -> PklModule(name, text) }.toTypedArray())
|
||||||
val pklSchema = evaluator.evaluateSchema(path(pklFile))
|
|
||||||
val generator = JavaCodeGenerator(pklSchema, JavaCodegenOptions())
|
private fun generateFiles(vararg pklModules: PklModule): Map<String, String> =
|
||||||
acc + arrayOf(generator.javaFileName to generator.javaFile)
|
JavaCodegenOptions().generateFiles(*pklModules)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun instantiateOtherAndPropertyTypes(): kotlin.Pair<Any, Any> {
|
private fun instantiateOtherAndPropertyTypes(): kotlin.Pair<Any, Any> {
|
||||||
val otherCtor = propertyTypesClasses.getValue("my.Mod\$Other").constructors.first()
|
val otherCtor = propertyTypesClasses.getValue("my.Mod\$Other").constructors.first()
|
||||||
|
|||||||
+17
-2
@@ -36,8 +36,23 @@ data class CliKotlinCodeGeneratorOptions(
|
|||||||
val generateSpringBootConfig: Boolean = false,
|
val generateSpringBootConfig: Boolean = false,
|
||||||
|
|
||||||
/** Whether to make generated classes implement [java.io.Serializable] */
|
/** Whether to make generated classes implement [java.io.Serializable] */
|
||||||
val implementSerializable: Boolean = false
|
val implementSerializable: Boolean = false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A rename mapping for class names.
|
||||||
|
*
|
||||||
|
* When you need to have Kotlin class or package names different from the default names derived
|
||||||
|
* from Pkl module names, you can define a rename mapping, where the key is a prefix of the
|
||||||
|
* original Pkl module name, and the value is the desired replacement.
|
||||||
|
*/
|
||||||
|
val renames: Map<String, String> = emptyMap()
|
||||||
) {
|
) {
|
||||||
fun toKotlinCodegenOptions(): KotlinCodegenOptions =
|
fun toKotlinCodegenOptions(): KotlinCodegenOptions =
|
||||||
KotlinCodegenOptions(indent, generateKdoc, generateSpringBootConfig, implementSerializable)
|
KotlinCodegenOptions(
|
||||||
|
indent,
|
||||||
|
generateKdoc,
|
||||||
|
generateSpringBootConfig,
|
||||||
|
implementSerializable,
|
||||||
|
renames
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
|||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import org.pkl.commons.NameMapper
|
||||||
import org.pkl.core.*
|
import org.pkl.core.*
|
||||||
import org.pkl.core.util.CodeGeneratorUtils
|
import org.pkl.core.util.CodeGeneratorUtils
|
||||||
import org.pkl.core.util.IoUtils
|
import org.pkl.core.util.IoUtils
|
||||||
@@ -35,7 +36,15 @@ data class KotlinCodegenOptions(
|
|||||||
val generateSpringBootConfig: Boolean = false,
|
val generateSpringBootConfig: Boolean = false,
|
||||||
|
|
||||||
/** Whether to make generated classes implement [java.io.Serializable] */
|
/** Whether to make generated classes implement [java.io.Serializable] */
|
||||||
val implementSerializable: Boolean = false
|
val implementSerializable: Boolean = false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapping from Pkl module name prefixes to their replacements.
|
||||||
|
*
|
||||||
|
* Can be used when the class or package name in the generated source code should be different
|
||||||
|
* from the corresponding name derived from the Pkl module declaration .
|
||||||
|
*/
|
||||||
|
val renames: Map<String, String> = emptyMap(),
|
||||||
)
|
)
|
||||||
|
|
||||||
class KotlinCodeGeneratorException(message: String) : RuntimeException(message)
|
class KotlinCodeGeneratorException(message: String) : RuntimeException(message)
|
||||||
@@ -106,10 +115,17 @@ class KotlinCodeGenerator(
|
|||||||
.toString()
|
.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
val kotlinFileName: String
|
private val kotlinFileName: String
|
||||||
get() = buildString {
|
get() = buildString {
|
||||||
|
val (packageName, className) = nameMapper.map(moduleSchema.moduleName)
|
||||||
|
val dirPath = packageName.split('.').joinToString("/", transform = IoUtils::encodePath)
|
||||||
|
val fileName = IoUtils.encodePath(className)
|
||||||
append("kot")
|
append("kot")
|
||||||
append("lin/${relativeOutputPathFor(moduleSchema.moduleName)}")
|
append("lin/")
|
||||||
|
if (dirPath.isNotEmpty()) {
|
||||||
|
append("$dirPath/")
|
||||||
|
}
|
||||||
|
append("$fileName.kt")
|
||||||
}
|
}
|
||||||
|
|
||||||
val kotlinFile: String
|
val kotlinFile: String
|
||||||
@@ -169,9 +185,8 @@ class KotlinCodeGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val moduleName = moduleSchema.moduleName
|
val moduleName = moduleSchema.moduleName
|
||||||
val index = moduleName.lastIndexOf(".")
|
|
||||||
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
val (packageName, moduleTypeName) = nameMapper.map(moduleName)
|
||||||
val moduleTypeName = moduleName.substring(index + 1).replaceFirstChar { it.titlecaseChar() }
|
|
||||||
|
|
||||||
val fileSpec = FileSpec.builder(packageName, moduleTypeName).indent(options.indent)
|
val fileSpec = FileSpec.builder(packageName, moduleTypeName).indent(options.indent)
|
||||||
|
|
||||||
@@ -195,17 +210,6 @@ class KotlinCodeGenerator(
|
|||||||
return fileSpec.build().toString()
|
return fileSpec.build().toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun relativeOutputPathFor(moduleName: String): String {
|
|
||||||
val nameParts = moduleName.split(".").map(IoUtils::encodePath)
|
|
||||||
val dirPath = nameParts.dropLast(1).joinToString("/")
|
|
||||||
val fileName = nameParts.last().replaceFirstChar { it.titlecaseChar() }
|
|
||||||
return if (dirPath.isEmpty()) {
|
|
||||||
"$fileName.kt"
|
|
||||||
} else {
|
|
||||||
"$dirPath/$fileName.kt"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateObjectSpec(pClass: PClass): TypeSpec.Builder {
|
private fun generateObjectSpec(pClass: PClass): TypeSpec.Builder {
|
||||||
val builder = TypeSpec.objectBuilder(pClass.toKotlinPoetName())
|
val builder = TypeSpec.objectBuilder(pClass.toKotlinPoetName())
|
||||||
val docComment = pClass.docComment
|
val docComment = pClass.docComment
|
||||||
@@ -636,9 +640,7 @@ class KotlinCodeGenerator(
|
|||||||
.endControlFlow()
|
.endControlFlow()
|
||||||
|
|
||||||
private fun PClass.toKotlinPoetName(): ClassName {
|
private fun PClass.toKotlinPoetName(): ClassName {
|
||||||
val index = moduleName.lastIndexOf(".")
|
val (packageName, moduleTypeName) = nameMapper.map(moduleName)
|
||||||
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
|
||||||
val moduleTypeName = moduleName.substring(index + 1).replaceFirstChar { it.titlecaseChar() }
|
|
||||||
return if (isModuleClass) {
|
return if (isModuleClass) {
|
||||||
ClassName(packageName, moduleTypeName)
|
ClassName(packageName, moduleTypeName)
|
||||||
} else {
|
} else {
|
||||||
@@ -647,8 +649,7 @@ class KotlinCodeGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun TypeAlias.toKotlinPoetName(): ClassName {
|
private fun TypeAlias.toKotlinPoetName(): ClassName {
|
||||||
val index = moduleName.lastIndexOf(".")
|
val (packageName, moduleTypeName) = nameMapper.map(moduleName)
|
||||||
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
aliasedType is PType.Alias -> {
|
aliasedType is PType.Alias -> {
|
||||||
@@ -663,7 +664,6 @@ class KotlinCodeGenerator(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Kotlin type generated for [this] is a nested enum class
|
// Kotlin type generated for [this] is a nested enum class
|
||||||
val moduleTypeName = moduleName.substring(index + 1).replaceFirstChar { it.titlecaseChar() }
|
|
||||||
ClassName(packageName, moduleTypeName, simpleName)
|
ClassName(packageName, moduleTypeName, simpleName)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@@ -786,4 +786,6 @@ class KotlinCodeGenerator(
|
|||||||
|
|
||||||
private fun List<PType>.toKotlinPoet(): Array<TypeName> =
|
private fun List<PType>.toKotlinPoet(): Array<TypeName> =
|
||||||
map { it.toKotlinPoetName() }.toTypedArray()
|
map { it.toKotlinPoetName() }.toTypedArray()
|
||||||
|
|
||||||
|
private val nameMapper = NameMapper(options.renames)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package org.pkl.codegen.kotlin
|
package org.pkl.codegen.kotlin
|
||||||
|
|
||||||
|
import com.github.ajalt.clikt.parameters.options.associate
|
||||||
import com.github.ajalt.clikt.parameters.options.default
|
import com.github.ajalt.clikt.parameters.options.default
|
||||||
import com.github.ajalt.clikt.parameters.options.flag
|
import com.github.ajalt.clikt.parameters.options.flag
|
||||||
import com.github.ajalt.clikt.parameters.options.option
|
import com.github.ajalt.clikt.parameters.options.option
|
||||||
@@ -81,6 +82,21 @@ class PklKotlinCodegenCommand :
|
|||||||
)
|
)
|
||||||
.flag()
|
.flag()
|
||||||
|
|
||||||
|
private val renames: Map<String, String> by
|
||||||
|
option(
|
||||||
|
names = arrayOf("--rename"),
|
||||||
|
metavar = "<old_name=new_name>",
|
||||||
|
help =
|
||||||
|
"""
|
||||||
|
Replace a prefix in the names of the generated Kotlin 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
|
||||||
|
classes or just their packages.
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
.associate()
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
val options =
|
val options =
|
||||||
CliKotlinCodeGeneratorOptions(
|
CliKotlinCodeGeneratorOptions(
|
||||||
@@ -89,7 +105,8 @@ class PklKotlinCodegenCommand :
|
|||||||
indent = indent,
|
indent = indent,
|
||||||
generateKdoc = generateKdoc,
|
generateKdoc = generateKdoc,
|
||||||
generateSpringBootConfig = generateSpringboot,
|
generateSpringBootConfig = generateSpringboot,
|
||||||
implementSerializable = implementSerializable
|
implementSerializable = implementSerializable,
|
||||||
|
renames = renames
|
||||||
)
|
)
|
||||||
CliKotlinCodeGenerator(options).run()
|
CliKotlinCodeGenerator(options).run()
|
||||||
}
|
}
|
||||||
|
|||||||
+113
@@ -152,6 +152,119 @@ class CliKotlinCodeGeneratorTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `custom package names`(@TempDir tempDir: Path) {
|
||||||
|
val module1 =
|
||||||
|
PklModule(
|
||||||
|
"org.foo.Module1",
|
||||||
|
"""
|
||||||
|
module org.foo.Module1
|
||||||
|
|
||||||
|
class Person {
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
val module2 =
|
||||||
|
PklModule(
|
||||||
|
"org.bar.Module2",
|
||||||
|
"""
|
||||||
|
module org.bar.Module2
|
||||||
|
|
||||||
|
import "../../org/foo/Module1.pkl"
|
||||||
|
|
||||||
|
class Group {
|
||||||
|
owner: Module1.Person
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
val module3 =
|
||||||
|
PklModule(
|
||||||
|
"org.baz.Module3",
|
||||||
|
"""
|
||||||
|
module org.baz.Module3
|
||||||
|
|
||||||
|
import "../../org/bar/Module2.pkl"
|
||||||
|
|
||||||
|
class Supergroup {
|
||||||
|
owner: Module2.Group
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
val module1PklFile = module1.writeToDisk(tempDir.resolve("org/foo/Module1.pkl"))
|
||||||
|
val module2PklFile = module2.writeToDisk(tempDir.resolve("org/bar/Module2.pkl"))
|
||||||
|
val module3PklFile = module3.writeToDisk(tempDir.resolve("org/baz/Module3.pkl"))
|
||||||
|
val outputDir = tempDir.resolve("output")
|
||||||
|
|
||||||
|
val generator =
|
||||||
|
CliKotlinCodeGenerator(
|
||||||
|
CliKotlinCodeGeneratorOptions(
|
||||||
|
CliBaseOptions(listOf(module1PklFile, module2PklFile, module3PklFile).map { it.toUri() }),
|
||||||
|
outputDir,
|
||||||
|
renames = mapOf("org.foo" to "com.foo.x", "org.baz" to "com.baz.a.b")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
generator.run()
|
||||||
|
|
||||||
|
val module1KotlinFile = outputDir.resolve("kotlin/com/foo/x/Module1.kt")
|
||||||
|
module1KotlinFile.readString().let {
|
||||||
|
assertContains("package com.foo.x", it)
|
||||||
|
|
||||||
|
assertContains("object Module1 {", it)
|
||||||
|
|
||||||
|
assertContains(
|
||||||
|
"""
|
||||||
|
| data class Person(
|
||||||
|
| val name: String
|
||||||
|
| )
|
||||||
|
""",
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val module2KotlinFile = outputDir.resolve("kotlin/org/bar/Module2.kt")
|
||||||
|
module2KotlinFile.readString().let {
|
||||||
|
assertContains("package org.bar", it)
|
||||||
|
|
||||||
|
assertContains("import com.foo.x.Module1", it)
|
||||||
|
|
||||||
|
assertContains("object Module2 {", it)
|
||||||
|
|
||||||
|
assertContains(
|
||||||
|
"""
|
||||||
|
| data class Group(
|
||||||
|
| val owner: Module1.Person,
|
||||||
|
| val name: String
|
||||||
|
| )
|
||||||
|
""",
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val module3KotlinFile = outputDir.resolve("kotlin/com/baz/a/b/Module3.kt")
|
||||||
|
module3KotlinFile.readString().let {
|
||||||
|
assertContains("package com.baz.a.b", it)
|
||||||
|
|
||||||
|
assertContains("import org.bar.Module2", it)
|
||||||
|
|
||||||
|
assertContains("object Module3 {", it)
|
||||||
|
|
||||||
|
assertContains(
|
||||||
|
"""
|
||||||
|
| data class Supergroup(
|
||||||
|
| val owner: Module2.Group
|
||||||
|
| )
|
||||||
|
""",
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun assertContains(part: String, code: String) {
|
private fun assertContains(part: String, code: String) {
|
||||||
val trimmedPart = part.trim().trimMargin()
|
val trimmedPart = part.trim().trimMargin()
|
||||||
if (!code.contains(trimmedPart)) {
|
if (!code.contains(trimmedPart)) {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ object InMemoryKotlinCompiler {
|
|||||||
|
|
||||||
val (importLines, remainder) =
|
val (importLines, remainder) =
|
||||||
sourceFiles.entries
|
sourceFiles.entries
|
||||||
|
.filter { (filename, _) -> filename.endsWith(".kt") }
|
||||||
.flatMap { (_, text) -> text.lines() }
|
.flatMap { (_, text) -> text.lines() }
|
||||||
.partition { it.startsWith("import") }
|
.partition { it.startsWith("import") }
|
||||||
val importBlock = importLines.sorted().distinct()
|
val importBlock = importLines.sorted().distinct()
|
||||||
|
|||||||
+246
-32
@@ -32,6 +32,8 @@ import org.pkl.core.util.IoUtils
|
|||||||
|
|
||||||
class KotlinCodeGeneratorTest {
|
class KotlinCodeGeneratorTest {
|
||||||
companion object {
|
companion object {
|
||||||
|
const val MAPPER_PREFIX = "resources/META-INF/org/pkl/config/java/mapper/classes"
|
||||||
|
|
||||||
// according to:
|
// according to:
|
||||||
// https://github.com/JetBrains/kotlin/blob/master/core/descriptors/
|
// https://github.com/JetBrains/kotlin/blob/master/core/descriptors/
|
||||||
// src/org/jetbrains/kotlin/renderer/KeywordStringsGenerated.java
|
// src/org/jetbrains/kotlin/renderer/KeywordStringsGenerated.java
|
||||||
@@ -153,9 +155,13 @@ class KotlinCodeGeneratorTest {
|
|||||||
private fun compileKotlinCode(kotlinCode: String): Map<String, KClass<*>> =
|
private fun compileKotlinCode(kotlinCode: String): Map<String, KClass<*>> =
|
||||||
InMemoryKotlinCompiler.compile(mapOf("my/Mod.kt" to kotlinCode))
|
InMemoryKotlinCompiler.compile(mapOf("my/Mod.kt" to kotlinCode))
|
||||||
|
|
||||||
private fun assertCompilesSuccessfully(sourceText: String) = compileKotlinCode(sourceText)
|
private fun assertCompilesSuccessfully(sourceText: String) {
|
||||||
|
assertThatCode { compileKotlinCode(sourceText) }.doesNotThrowAnyException()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TempDir lateinit var tempDir: Path
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEquals() {
|
fun testEquals() {
|
||||||
val ctor = simpleClass.constructors.first()
|
val ctor = simpleClass.constructors.first()
|
||||||
@@ -1206,7 +1212,7 @@ class KotlinCodeGeneratorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `import module`(@TempDir tempDir: Path) {
|
fun `import module`() {
|
||||||
val library =
|
val library =
|
||||||
PklModule(
|
PklModule(
|
||||||
"library",
|
"library",
|
||||||
@@ -1235,7 +1241,7 @@ class KotlinCodeGeneratorTest {
|
|||||||
.trimIndent()
|
.trimIndent()
|
||||||
)
|
)
|
||||||
|
|
||||||
val kotlinSourceFiles = generateKotlinFiles(tempDir, library, client)
|
val kotlinSourceFiles = generateFiles(library, client)
|
||||||
val kotlinClientCode =
|
val kotlinClientCode =
|
||||||
kotlinSourceFiles.entries.find { (fileName, _) -> fileName.endsWith("Client.kt") }!!.value
|
kotlinSourceFiles.entries.find { (fileName, _) -> fileName.endsWith("Client.kt") }!!.value
|
||||||
|
|
||||||
@@ -1253,7 +1259,7 @@ class KotlinCodeGeneratorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `extend module`(@TempDir tempDir: Path) {
|
fun `extend module`() {
|
||||||
val base =
|
val base =
|
||||||
PklModule(
|
PklModule(
|
||||||
"base",
|
"base",
|
||||||
@@ -1282,7 +1288,7 @@ class KotlinCodeGeneratorTest {
|
|||||||
.trimIndent()
|
.trimIndent()
|
||||||
)
|
)
|
||||||
|
|
||||||
val kotlinSourceFiles = generateKotlinFiles(tempDir, base, derived)
|
val kotlinSourceFiles = generateFiles(base, derived)
|
||||||
val kotlinDerivedCode =
|
val kotlinDerivedCode =
|
||||||
kotlinSourceFiles.entries.find { (filename, _) -> filename.endsWith("Derived.kt") }!!.value
|
kotlinSourceFiles.entries.find { (filename, _) -> filename.endsWith("Derived.kt") }!!.value
|
||||||
|
|
||||||
@@ -1317,7 +1323,7 @@ class KotlinCodeGeneratorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `extend module that only contains type aliases`(@TempDir tempDir: Path) {
|
fun `extend module that only contains type aliases`() {
|
||||||
val moduleOne =
|
val moduleOne =
|
||||||
PklModule(
|
PklModule(
|
||||||
"base",
|
"base",
|
||||||
@@ -1342,7 +1348,7 @@ class KotlinCodeGeneratorTest {
|
|||||||
.trimIndent()
|
.trimIndent()
|
||||||
)
|
)
|
||||||
|
|
||||||
val kotlinSourceFiles = generateKotlinFiles(tempDir, moduleOne, moduleTwo)
|
val kotlinSourceFiles = generateFiles(moduleOne, moduleTwo)
|
||||||
val kotlinDerivedCode =
|
val kotlinDerivedCode =
|
||||||
kotlinSourceFiles.entries.find { (filename, _) -> filename.endsWith("Derived.kt") }!!.value
|
kotlinSourceFiles.entries.find { (filename, _) -> filename.endsWith("Derived.kt") }!!.value
|
||||||
|
|
||||||
@@ -1359,7 +1365,7 @@ class KotlinCodeGeneratorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `generated properties files`(@TempDir tempDir: Path) {
|
fun `generated properties files`() {
|
||||||
val pklModule =
|
val pklModule =
|
||||||
PklModule(
|
PklModule(
|
||||||
"Mod.pkl",
|
"Mod.pkl",
|
||||||
@@ -1380,17 +1386,17 @@ class KotlinCodeGeneratorTest {
|
|||||||
"""
|
"""
|
||||||
.trimIndent()
|
.trimIndent()
|
||||||
)
|
)
|
||||||
val generated = generateFiles(tempDir, pklModule)
|
val generated = generateFiles(pklModule)
|
||||||
val expectedPropertyFile =
|
val expectedPropertyFile =
|
||||||
"resources/META-INF/org/pkl/config/java/mapper/classes/org.pkl.Mod.properties"
|
"resources/META-INF/org/pkl/config/java/mapper/classes/org.pkl.Mod.properties"
|
||||||
assertThat(generated).containsKey(expectedPropertyFile)
|
assertThat(generated).containsKey(expectedPropertyFile)
|
||||||
val propertyFileContents = generated[expectedPropertyFile]!!
|
val propertyFileContents = generated[expectedPropertyFile]!!
|
||||||
assertThat(propertyFileContents)
|
assertThat(propertyFileContents)
|
||||||
.contains("org.pkl.config.java.mapper.org.pkl.Mod\\#ModuleClass=org.pkl.Mod")
|
.contains(
|
||||||
assertThat(propertyFileContents)
|
"org.pkl.config.java.mapper.org.pkl.Mod\\#ModuleClass=org.pkl.Mod",
|
||||||
.contains("org.pkl.config.java.mapper.org.pkl.Mod\\#Foo=org.pkl.Mod\$Foo")
|
"org.pkl.config.java.mapper.org.pkl.Mod\\#Foo=org.pkl.Mod\$Foo",
|
||||||
assertThat(propertyFileContents)
|
"org.pkl.config.java.mapper.org.pkl.Mod\\#Bar=org.pkl.Mod\$Bar"
|
||||||
.contains("org.pkl.config.java.mapper.org.pkl.Mod\\#Bar=org.pkl.Mod\$Bar")
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1502,45 +1508,253 @@ class KotlinCodeGeneratorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `encoded file paths`(@TempDir path: Path) {
|
fun `encoded file paths`() {
|
||||||
val kotlinCode =
|
val kotlinCode =
|
||||||
generateKotlinFiles(
|
generateFiles(
|
||||||
path,
|
|
||||||
PklModule(
|
PklModule(
|
||||||
"FooBar.pkl",
|
"FooBar.pkl",
|
||||||
"""
|
"""
|
||||||
module `Foo*Bar`
|
module `Foo*Bar`
|
||||||
|
|
||||||
someProp: String
|
someProp: String
|
||||||
"""
|
"""
|
||||||
.trimIndent()
|
.trimIndent()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assertThat(kotlinCode).containsKey("kotlin/Foo(2a)Bar.kt")
|
assertThat(kotlinCode).containsKey("kotlin/Foo(2a)Bar.kt")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateFiles(tempDir: Path, vararg pklModules: PklModule): Map<String, String> {
|
@Test
|
||||||
val pklFiles = pklModules.map { it.writeToDisk(tempDir.resolve("pkl/${it.name}.pkl")) }
|
fun `override names in a standalone module`() {
|
||||||
val evaluator = Evaluator.preconfigured()
|
val files =
|
||||||
return pklFiles.fold(mapOf()) { acc, pklFile ->
|
KotlinCodegenOptions(
|
||||||
val pklSchema = evaluator.evaluateSchema(ModuleSource.path(pklFile))
|
renames = mapOf("a.b.c" to "x.y.z", "d.e.f.AnotherModule" to "u.v.w.RenamedModule")
|
||||||
acc + KotlinCodeGenerator(pklSchema, KotlinCodegenOptions()).output
|
)
|
||||||
}
|
.generateFiles(
|
||||||
|
"MyModule.pkl" to
|
||||||
|
"""
|
||||||
|
module a.b.c.MyModule
|
||||||
|
|
||||||
|
foo: String = "abc"
|
||||||
|
"""
|
||||||
|
.trimIndent(),
|
||||||
|
"AnotherModule.pkl" to
|
||||||
|
"""
|
||||||
|
module d.e.f.AnotherModule
|
||||||
|
|
||||||
|
bar: Int = 123
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
.toMutableMap()
|
||||||
|
|
||||||
|
files.validateContents(
|
||||||
|
"kotlin/x/y/z/MyModule.kt" to listOf("package x.y.z", "data class MyModule("),
|
||||||
|
"$MAPPER_PREFIX/a.b.c.MyModule.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.a.b.c.MyModule\\#ModuleClass=x.y.z.MyModule"),
|
||||||
|
// ---
|
||||||
|
"kotlin/u/v/w/RenamedModule.kt" to listOf("package u.v.w", "data class RenamedModule("),
|
||||||
|
"$MAPPER_PREFIX/d.e.f.AnotherModule.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.d.e.f.AnotherModule\\#ModuleClass=u.v.w.RenamedModule"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateKotlinFiles(
|
@Test
|
||||||
tempDir: Path,
|
fun `override names based on the longest prefix`() {
|
||||||
|
val files =
|
||||||
|
KotlinCodegenOptions(
|
||||||
|
renames = mapOf("com.foo.bar." to "x.", "com.foo." to "y.", "com." to "z.", "" to "w.")
|
||||||
|
)
|
||||||
|
.generateFiles(
|
||||||
|
"com/foo/bar/Module1" to
|
||||||
|
"""
|
||||||
|
module com.foo.bar.Module1
|
||||||
|
|
||||||
|
bar: String
|
||||||
|
"""
|
||||||
|
.trimIndent(),
|
||||||
|
"com/Module2" to
|
||||||
|
"""
|
||||||
|
module com.Module2
|
||||||
|
|
||||||
|
com: String
|
||||||
|
"""
|
||||||
|
.trimIndent(),
|
||||||
|
"org/baz/Module3" to
|
||||||
|
"""
|
||||||
|
module org.baz.Module3
|
||||||
|
|
||||||
|
baz: String
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
.toMutableMap()
|
||||||
|
|
||||||
|
files.validateContents(
|
||||||
|
"kotlin/x/Module1.kt" to listOf("package x", "data class Module1("),
|
||||||
|
"$MAPPER_PREFIX/com.foo.bar.Module1.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.com.foo.bar.Module1\\#ModuleClass=x.Module1"),
|
||||||
|
// ---
|
||||||
|
"kotlin/z/Module2.kt" to listOf("package z", "data class Module2("),
|
||||||
|
"$MAPPER_PREFIX/com.Module2.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.com.Module2\\#ModuleClass=z.Module2"),
|
||||||
|
// ---
|
||||||
|
"kotlin/w/org/baz/Module3.kt" to listOf("package w.org.baz", "data class Module3("),
|
||||||
|
"$MAPPER_PREFIX/org.baz.Module3.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.org.baz.Module3\\#ModuleClass=w.org.baz.Module3")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `override names in multiple modules using each other`() {
|
||||||
|
val files =
|
||||||
|
KotlinCodegenOptions(
|
||||||
|
renames =
|
||||||
|
mapOf(
|
||||||
|
"org.foo" to "com.foo.x",
|
||||||
|
"org.bar.Module2" to "org.bar.RenamedModule",
|
||||||
|
"org.baz" to "com.baz.a.b"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.generateFiles(
|
||||||
|
"org/foo/Module1" to
|
||||||
|
"""
|
||||||
|
module org.foo.Module1
|
||||||
|
|
||||||
|
class Person {
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
.trimIndent(),
|
||||||
|
"org/bar/Module2" to
|
||||||
|
"""
|
||||||
|
module org.bar.Module2
|
||||||
|
|
||||||
|
import "../../org/foo/Module1.pkl"
|
||||||
|
|
||||||
|
class Group {
|
||||||
|
owner: Module1.Person
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
.trimIndent(),
|
||||||
|
"org/baz/Module3" to
|
||||||
|
"""
|
||||||
|
module org.baz.Module3
|
||||||
|
|
||||||
|
import "../../org/bar/Module2.pkl"
|
||||||
|
|
||||||
|
class Supergroup {
|
||||||
|
owner: Module2.Group
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
files.validateContents(
|
||||||
|
"kotlin/com/foo/x/Module1.kt" to
|
||||||
|
listOf("package com.foo.x", "object Module1 {", "data class Person("),
|
||||||
|
"$MAPPER_PREFIX/org.foo.Module1.properties" to
|
||||||
|
listOf(
|
||||||
|
"org.pkl.config.java.mapper.org.foo.Module1\\#ModuleClass=com.foo.x.Module1",
|
||||||
|
"org.pkl.config.java.mapper.org.foo.Module1\\#Person=com.foo.x.Module1${'$'}Person",
|
||||||
|
),
|
||||||
|
// ---
|
||||||
|
"kotlin/org/bar/RenamedModule.kt" to
|
||||||
|
listOf(
|
||||||
|
"package org.bar",
|
||||||
|
"import com.foo.x.Module1",
|
||||||
|
"object RenamedModule {",
|
||||||
|
"val owner: Module1.Person"
|
||||||
|
),
|
||||||
|
"$MAPPER_PREFIX/org.bar.Module2.properties" to
|
||||||
|
listOf(
|
||||||
|
"org.pkl.config.java.mapper.org.bar.Module2\\#ModuleClass=org.bar.RenamedModule",
|
||||||
|
"org.pkl.config.java.mapper.org.bar.Module2\\#Group=org.bar.RenamedModule${'$'}Group",
|
||||||
|
),
|
||||||
|
// ---
|
||||||
|
"kotlin/com/baz/a/b/Module3.kt" to
|
||||||
|
listOf(
|
||||||
|
"package com.baz.a.b",
|
||||||
|
"import org.bar.RenamedModule",
|
||||||
|
"object Module3 {",
|
||||||
|
"val owner: RenamedModule.Group"
|
||||||
|
),
|
||||||
|
"$MAPPER_PREFIX/org.baz.Module3.properties" to
|
||||||
|
listOf(
|
||||||
|
"org.pkl.config.java.mapper.org.baz.Module3\\#ModuleClass=com.baz.a.b.Module3",
|
||||||
|
"org.pkl.config.java.mapper.org.baz.Module3\\#Supergroup=com.baz.a.b.Module3${'$'}Supergroup",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `do not capitalize names of renamed classes`() {
|
||||||
|
val files =
|
||||||
|
KotlinCodegenOptions(
|
||||||
|
renames = mapOf("a.b.c.MyModule" to "x.y.z.renamed_module", "d.e.f." to "u.v.w.")
|
||||||
|
)
|
||||||
|
.generateFiles(
|
||||||
|
"MyModule.pkl" to
|
||||||
|
"""
|
||||||
|
module a.b.c.MyModule
|
||||||
|
|
||||||
|
foo: String = "abc"
|
||||||
|
"""
|
||||||
|
.trimIndent(),
|
||||||
|
"lower_module.pkl" to
|
||||||
|
"""
|
||||||
|
module d.e.f.lower_module
|
||||||
|
|
||||||
|
bar: Int = 123
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
files.validateContents(
|
||||||
|
"kotlin/x/y/z/renamed_module.kt" to listOf("package x.y.z", "data class renamed_module("),
|
||||||
|
"$MAPPER_PREFIX/a.b.c.MyModule.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.a.b.c.MyModule\\#ModuleClass=x.y.z.renamed_module"),
|
||||||
|
// ---
|
||||||
|
"kotlin/u/v/w/Lower_module.kt" to listOf("package u.v.w", "data class Lower_module("),
|
||||||
|
"$MAPPER_PREFIX/d.e.f.lower_module.properties" to
|
||||||
|
listOf("org.pkl.config.java.mapper.d.e.f.lower_module\\#ModuleClass=u.v.w.Lower_module"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Map<String, String>.validateContents(
|
||||||
|
vararg assertions: kotlin.Pair<String, List<String>>
|
||||||
|
) {
|
||||||
|
val files = toMutableMap()
|
||||||
|
|
||||||
|
for ((fileName, lines) in assertions) {
|
||||||
|
assertThat(files).containsKey(fileName)
|
||||||
|
assertThat(files.remove(fileName)).describedAs("Contents of $fileName").contains(lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(files).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KotlinCodegenOptions.generateFiles(
|
||||||
vararg pklModules: PklModule
|
vararg pklModules: PklModule
|
||||||
): Map<String, String> {
|
): Map<String, String> {
|
||||||
val pklFiles = pklModules.map { it.writeToDisk(tempDir.resolve("pkl/${it.name}.pkl")) }
|
val pklFiles = pklModules.map { it.writeToDisk(tempDir.resolve("pkl/${it.name}.pkl")) }
|
||||||
val evaluator = Evaluator.preconfigured()
|
val evaluator = Evaluator.preconfigured()
|
||||||
return pklFiles.fold(mapOf()) { acc, pklFile ->
|
return pklFiles.fold(mapOf()) { acc, pklFile ->
|
||||||
val pklSchema = evaluator.evaluateSchema(ModuleSource.path(pklFile))
|
val pklSchema = evaluator.evaluateSchema(ModuleSource.path(pklFile))
|
||||||
val generator = KotlinCodeGenerator(pklSchema, KotlinCodegenOptions())
|
val generator = KotlinCodeGenerator(pklSchema, this)
|
||||||
acc + arrayOf(generator.kotlinFileName to generator.kotlinFile)
|
acc + generator.output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KotlinCodegenOptions.generateFiles(
|
||||||
|
vararg pklModules: kotlin.Pair<String, String>
|
||||||
|
): Map<String, String> =
|
||||||
|
generateFiles(*pklModules.map { (name, text) -> PklModule(name, text) }.toTypedArray())
|
||||||
|
|
||||||
|
private fun generateFiles(vararg pklModules: PklModule): Map<String, String> =
|
||||||
|
KotlinCodegenOptions().generateFiles(*pklModules)
|
||||||
|
|
||||||
private fun instantiateOtherAndPropertyTypes(): kotlin.Pair<Any, Any> {
|
private fun instantiateOtherAndPropertyTypes(): kotlin.Pair<Any, Any> {
|
||||||
val otherCtor = propertyTypesClasses.getValue("Other").constructors.first()
|
val otherCtor = propertyTypesClasses.getValue("Other").constructors.first()
|
||||||
val other = otherCtor.call("pigeon")
|
val other = otherCtor.call("pigeon")
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pkl.commons
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper class for translating names of Pkl modules to different names of classes and/or objects
|
||||||
|
* in the target language of a code generation execution.
|
||||||
|
*
|
||||||
|
* The `mapping` parameter is expected to contain valid prefixes of Pkl module names, with an
|
||||||
|
* optional dot at the end, and values should be valid class names in the language for which code
|
||||||
|
* generation is performed.
|
||||||
|
*
|
||||||
|
* If the rename patterns do not explicitly rename the class, the class name is capitalized.
|
||||||
|
*
|
||||||
|
* When computing the appropriate target name, the longest matching prefix is used.
|
||||||
|
*
|
||||||
|
* Prefix replacements are literal, and therefore dots are important. When renaming packages, in
|
||||||
|
* most cases, you must ensure that you have an ending dot on both sides of a mapping (except for
|
||||||
|
* the empty mapping, if you use it), otherwise you may get unexpected results:
|
||||||
|
* ```kotlin
|
||||||
|
* val mapper = NameMapper(
|
||||||
|
* mapOf(
|
||||||
|
* "com.foo." to "x", // Dot on the left only
|
||||||
|
* "org.bar" to "y.", // Dot on the right only
|
||||||
|
* "net.baz" to "z" // No dots
|
||||||
|
* )
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* assertThat(mapper.map("com.foo.bar")).isEqualTo("" to "xbar") // Target prefix merged into the suffix
|
||||||
|
* assertThat(mapper.map("org.bar.baz")).isEqualTo("y." to "Baz") // Double dot, invalid package name
|
||||||
|
* assertThat(mapper.map("net.baz.qux")).isEqualTo("z" to "Qux") // Looks okay, but...
|
||||||
|
* assertThat(mapper.map("net.bazqux")).isEqualTo("" to "zqux") // ...may cut the package name in the middle.
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
class NameMapper(mapping: Map<String, String>) {
|
||||||
|
private val sortedMapping = mapping.toList().sortedBy { -it.first.length }
|
||||||
|
|
||||||
|
private fun doMap(sourceName: String): Pair<String, Boolean> {
|
||||||
|
for ((sourcePrefix, targetPrefix) in sortedMapping) {
|
||||||
|
if (sourceName.startsWith(sourcePrefix)) {
|
||||||
|
val rest = sourceName.substring(sourcePrefix.length)
|
||||||
|
val mapped = targetPrefix + rest
|
||||||
|
val wasClassRenamed =
|
||||||
|
!targetPrefix.endsWith('.') && (sourcePrefix.length - 1) >= sourceName.lastIndexOf('.')
|
||||||
|
return mapped to wasClassRenamed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sourceName to false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun map(sourceName: String): Pair<String, String> {
|
||||||
|
val (mappedName, wasClassRenamed) = doMap(sourceName)
|
||||||
|
val packageName = mappedName.substringBeforeLast(".", "")
|
||||||
|
val mappedClassName = mappedName.substringAfterLast(".")
|
||||||
|
val className =
|
||||||
|
if (wasClassRenamed) mappedClassName
|
||||||
|
else mappedClassName.replaceFirstChar { it.titlecaseChar() }
|
||||||
|
return packageName to className
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pkl.commons
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class NameMapperTest {
|
||||||
|
@Test
|
||||||
|
fun `empty prefixes everything`() {
|
||||||
|
val mapper = NameMapper(mapOf("" to "bar."))
|
||||||
|
assertThat(mapper.map("foo.bar.Baz")).isEqualTo("bar.foo.bar" to "Baz")
|
||||||
|
assertThat(mapper.map("Baz")).isEqualTo("bar" to "Baz")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `longest prefix wins`() {
|
||||||
|
val mapper = NameMapper(mapOf("bar." to "com.bar.", "bar.baz." to "foo.bar."))
|
||||||
|
assertThat(mapper.map("bar.baz.Buzzy")).isEqualTo("foo.bar" to "Buzzy")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `implicit uppercase classname`() {
|
||||||
|
val mapper = NameMapper(mapOf("foo." to "bar."))
|
||||||
|
assertThat(mapper.map("foo.bar.baz")).isEqualTo("bar.bar" to "Baz")
|
||||||
|
assertThat(mapper.map("foo.bar")).isEqualTo("bar" to "Bar")
|
||||||
|
assertThat(mapper.map("baz")).isEqualTo("" to "Baz")
|
||||||
|
assertThat(mapper.map("baz")).isEqualTo("" to "Baz")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `no implicit uppercased classname if explicitly renamed`() {
|
||||||
|
val mapper =
|
||||||
|
NameMapper(
|
||||||
|
mapOf(
|
||||||
|
"foo.bar" to "bar.bar",
|
||||||
|
"foo.c" to "foo.z",
|
||||||
|
"com.foo." to "x",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertThat(mapper.map("foo.bar")).isEqualTo("bar" to "bar")
|
||||||
|
assertThat(mapper.map("foo.bar")).isEqualTo("bar" to "bar")
|
||||||
|
assertThat(mapper.map("foo.cow")).isEqualTo("foo" to "zow")
|
||||||
|
assertThat(mapper.map("com.foo.bar")).isEqualTo("" to "xbar")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,11 @@ dependencies {
|
|||||||
//
|
//
|
||||||
// To debug shaded code in IntelliJ, temporarily remove the conditional.
|
// To debug shaded code in IntelliJ, temporarily remove the conditional.
|
||||||
if (System.getProperty("idea.sync.active") == null) {
|
if (System.getProperty("idea.sync.active") == null) {
|
||||||
runtimeOnly(project(":pkl-tools", "fatJar"))
|
runtimeOnly(projects.pklTools) {
|
||||||
|
attributes {
|
||||||
|
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.SHADOWED))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testImplementation(projects.pklCommonsTest)
|
testImplementation(projects.pklCommonsTest)
|
||||||
|
|||||||
@@ -412,6 +412,7 @@ public class PklPlugin implements Plugin<Project> {
|
|||||||
task.getOutputDir().set(spec.getOutputDir());
|
task.getOutputDir().set(spec.getOutputDir());
|
||||||
task.getGenerateSpringBootConfig().set(spec.getGenerateSpringBootConfig());
|
task.getGenerateSpringBootConfig().set(spec.getGenerateSpringBootConfig());
|
||||||
task.getImplementSerializable().set(spec.getImplementSerializable());
|
task.getImplementSerializable().set(spec.getImplementSerializable());
|
||||||
|
task.getPackageMapping().set(spec.getPackageMapping());
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends BasePklTask, S extends BasePklSpec> void configureBaseTask(T task, S spec) {
|
private <T extends BasePklTask, S extends BasePklSpec> void configureBaseTask(T task, S spec) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package org.pkl.gradle.spec;
|
package org.pkl.gradle.spec;
|
||||||
|
|
||||||
import org.gradle.api.file.DirectoryProperty;
|
import org.gradle.api.file.DirectoryProperty;
|
||||||
|
import org.gradle.api.provider.MapProperty;
|
||||||
import org.gradle.api.provider.Property;
|
import org.gradle.api.provider.Property;
|
||||||
import org.gradle.api.tasks.SourceSet;
|
import org.gradle.api.tasks.SourceSet;
|
||||||
|
|
||||||
@@ -30,4 +31,6 @@ public interface CodeGenSpec extends ModulesSpec {
|
|||||||
Property<Boolean> getGenerateSpringBootConfig();
|
Property<Boolean> getGenerateSpringBootConfig();
|
||||||
|
|
||||||
Property<Boolean> getImplementSerializable();
|
Property<Boolean> getImplementSerializable();
|
||||||
|
|
||||||
|
MapProperty<String, String> getPackageMapping();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package org.pkl.gradle.task;
|
package org.pkl.gradle.task;
|
||||||
|
|
||||||
import org.gradle.api.file.DirectoryProperty;
|
import org.gradle.api.file.DirectoryProperty;
|
||||||
|
import org.gradle.api.provider.MapProperty;
|
||||||
import org.gradle.api.provider.Property;
|
import org.gradle.api.provider.Property;
|
||||||
import org.gradle.api.tasks.Input;
|
import org.gradle.api.tasks.Input;
|
||||||
import org.gradle.api.tasks.OutputDirectory;
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
@@ -32,4 +33,7 @@ public abstract class CodeGenTask extends ModulesTask {
|
|||||||
|
|
||||||
@Input
|
@Input
|
||||||
public abstract Property<Boolean> getImplementSerializable();
|
public abstract Property<Boolean> getImplementSerializable();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public abstract MapProperty<String, String> getPackageMapping();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ public abstract class JavaCodeGenTask extends CodeGenTask {
|
|||||||
getGenerateSpringBootConfig().get(),
|
getGenerateSpringBootConfig().get(),
|
||||||
getParamsAnnotation().getOrNull(),
|
getParamsAnnotation().getOrNull(),
|
||||||
getNonNullAnnotation().getOrNull(),
|
getNonNullAnnotation().getOrNull(),
|
||||||
getImplementSerializable().get()))
|
getImplementSerializable().get(),
|
||||||
|
getPackageMapping().get()))
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ public abstract class KotlinCodeGenTask extends CodeGenTask {
|
|||||||
getIndent().get(),
|
getIndent().get(),
|
||||||
getGenerateKdoc().get(),
|
getGenerateKdoc().get(),
|
||||||
getGenerateSpringBootConfig().get(),
|
getGenerateSpringBootConfig().get(),
|
||||||
getImplementSerializable().get()))
|
getImplementSerializable().get(),
|
||||||
|
getPackageMapping().get()))
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class JavaCodeGeneratorsTest : AbstractTest() {
|
|||||||
|
|
||||||
runTask("configClasses")
|
runTask("configClasses")
|
||||||
|
|
||||||
val baseDir = testProjectDir.resolve("build/generated/java/org")
|
val baseDir = testProjectDir.resolve("build/generated/java/foo/bar")
|
||||||
val moduleFile = baseDir.resolve("Mod.java")
|
val moduleFile = baseDir.resolve("Mod.java")
|
||||||
|
|
||||||
assertThat(baseDir.listDirectoryEntries().count()).isEqualTo(1)
|
assertThat(baseDir.listDirectoryEntries().count()).isEqualTo(1)
|
||||||
@@ -36,7 +36,7 @@ class JavaCodeGeneratorsTest : AbstractTest() {
|
|||||||
| public static final class Person {
|
| public static final class Person {
|
||||||
| public final @Nonnull String name;
|
| public final @Nonnull String name;
|
||||||
|
|
|
|
||||||
| public final @Nonnull List<@Nonnull Address> addresses;
|
| public final @Nonnull List<Address> addresses;
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,28 +53,14 @@ class JavaCodeGeneratorsTest : AbstractTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun `compile generated code`() {
|
fun `compile generated code`() {
|
||||||
writeBuildFile()
|
writeBuildFile()
|
||||||
writeFile("mod.pkl", """
|
writePklFile()
|
||||||
module org.mod
|
|
||||||
|
|
||||||
class Person {
|
|
||||||
name: String
|
|
||||||
addresses: List<Address?>
|
|
||||||
}
|
|
||||||
|
|
||||||
class Address {
|
|
||||||
street: String
|
|
||||||
zip: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
other: Any = 42
|
|
||||||
""".trimIndent())
|
|
||||||
|
|
||||||
runTask("compileJava")
|
runTask("compileJava")
|
||||||
|
|
||||||
val classesDir = testProjectDir.resolve("build/classes/java/main")
|
val classesDir = testProjectDir.resolve("build/classes/java/main")
|
||||||
val moduleClassFile = classesDir.resolve("org/Mod.class")
|
val moduleClassFile = classesDir.resolve("foo/bar/Mod.class")
|
||||||
val personClassFile = classesDir.resolve("org/Mod\$Person.class")
|
val personClassFile = classesDir.resolve("foo/bar/Mod\$Person.class")
|
||||||
val addressClassFile = classesDir.resolve("org/Mod\$Address.class")
|
val addressClassFile = classesDir.resolve("foo/bar/Mod\$Address.class")
|
||||||
assertThat(moduleClassFile).exists()
|
assertThat(moduleClassFile).exists()
|
||||||
assertThat(personClassFile).exists()
|
assertThat(personClassFile).exists()
|
||||||
assertThat(addressClassFile).exists()
|
assertThat(addressClassFile).exists()
|
||||||
@@ -127,6 +113,9 @@ class JavaCodeGeneratorsTest : AbstractTest() {
|
|||||||
paramsAnnotation = "javax.inject.Named"
|
paramsAnnotation = "javax.inject.Named"
|
||||||
nonNullAnnotation = "javax.annotation.Nonnull"
|
nonNullAnnotation = "javax.annotation.Nonnull"
|
||||||
settingsModule = "pkl:settings"
|
settingsModule = "pkl:settings"
|
||||||
|
packageMapping = [
|
||||||
|
'org': 'foo.bar'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,18 +123,6 @@ class JavaCodeGeneratorsTest : AbstractTest() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeGradlePropertiesFile() {
|
|
||||||
writeFile("gradle.properties", """
|
|
||||||
systemProp.http.proxyHost=proxy.config.pcp.local
|
|
||||||
systemProp.http.proxyPort=3128
|
|
||||||
systemProp.http.nonProxyHosts=localhost|*.apple.com
|
|
||||||
|
|
||||||
systemProp.https.proxyHost=proxy.config.pcp.local
|
|
||||||
systemProp.https.proxyPort=3128
|
|
||||||
systemProp.https.nonProxyHosts=localhost|*.apple.com
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writePklFile() {
|
private fun writePklFile() {
|
||||||
writeFile(
|
writeFile(
|
||||||
"mod.pkl", """
|
"mod.pkl", """
|
||||||
@@ -153,7 +130,7 @@ class JavaCodeGeneratorsTest : AbstractTest() {
|
|||||||
|
|
||||||
class Person {
|
class Person {
|
||||||
name: String
|
name: String
|
||||||
addresses: List<Address>
|
addresses: List<Address?>
|
||||||
}
|
}
|
||||||
|
|
||||||
class Address {
|
class Address {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class KotlinCodeGeneratorsTest : AbstractTest() {
|
|||||||
|
|
||||||
runTask("configClasses")
|
runTask("configClasses")
|
||||||
|
|
||||||
val baseDir = testProjectDir.resolve("build/generated/kotlin/org")
|
val baseDir = testProjectDir.resolve("build/generated/kotlin/foo/bar")
|
||||||
val kotlinFile = baseDir.resolve("Mod.kt")
|
val kotlinFile = baseDir.resolve("Mod.kt")
|
||||||
|
|
||||||
assertThat(baseDir.listDirectoryEntries().count()).isEqualTo(1)
|
assertThat(baseDir.listDirectoryEntries().count()).isEqualTo(1)
|
||||||
@@ -58,9 +58,9 @@ class KotlinCodeGeneratorsTest : AbstractTest() {
|
|||||||
runTask("compileKotlin")
|
runTask("compileKotlin")
|
||||||
|
|
||||||
val classesDir = testProjectDir.resolve("build/classes/kotlin/main")
|
val classesDir = testProjectDir.resolve("build/classes/kotlin/main")
|
||||||
val moduleClassFile = classesDir.resolve("org/Mod.class")
|
val moduleClassFile = classesDir.resolve("foo/bar/Mod.class")
|
||||||
val personClassFile = classesDir.resolve("org/Mod\$Person.class")
|
val personClassFile = classesDir.resolve("foo/bar/Mod\$Person.class")
|
||||||
val addressClassFile = classesDir.resolve("org/Mod\$Address.class")
|
val addressClassFile = classesDir.resolve("foo/bar/Mod\$Address.class")
|
||||||
assertThat(moduleClassFile).exists()
|
assertThat(moduleClassFile).exists()
|
||||||
assertThat(personClassFile).exists()
|
assertThat(personClassFile).exists()
|
||||||
assertThat(addressClassFile).exists()
|
assertThat(addressClassFile).exists()
|
||||||
@@ -125,6 +125,9 @@ class KotlinCodeGeneratorsTest : AbstractTest() {
|
|||||||
sourceModules = ["mod.pkl"]
|
sourceModules = ["mod.pkl"]
|
||||||
outputDir = file("build/generated")
|
outputDir = file("build/generated")
|
||||||
settingsModule = "pkl:settings"
|
settingsModule = "pkl:settings"
|
||||||
|
packageMapping = [
|
||||||
|
'org.': 'foo.bar.'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user