mirror of
https://github.com/apple/pkl.git
synced 2026-01-17 00:47:07 +01:00
Initial commit
This commit is contained in:
39
pkl-codegen-java/gradle.lockfile
Normal file
39
pkl-codegen-java/gradle.lockfile
Normal file
@@ -0,0 +1,39 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ajalt.clikt:clikt-jvm:3.5.1=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.ajalt.clikt:clikt:3.5.1=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.squareup:javapoet:1.13.0=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
com.tunnelvisionlabs:antlr4-runtime:4.9.0=default,runtimeClasspath,testRuntimeClasspath
|
||||
io.leangen.geantyref:geantyref:1.3.14=testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.12.21=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.24.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.sdk:graal-sdk:22.3.1=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.graalvm.truffle:truffle-api:22.3.1=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.7.10=kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-script-runtime:1.7.10=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-scripting-common:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
|
||||
org.jetbrains.kotlin:kotlin-scripting-jvm:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.10=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.7.10=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains:annotations:13.0=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:5.9.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.junit.jupiter:junit-jupiter-engine:5.9.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.junit.jupiter:junit-jupiter-params:5.9.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:1.9.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.junit.platform:junit-platform-engine:1.9.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.junit:junit-bom:5.9.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.opentest4j:opentest4j:1.2.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.organicdesign:Paguro:3.10.3=default,runtimeClasspath,testRuntimeClasspath
|
||||
org.snakeyaml:snakeyaml-engine:2.5=default,runtimeClasspath,testRuntimeClasspath
|
||||
empty=annotationProcessor,archives,compile,compileOnly,compileOnlyDependenciesMetadata,intransitiveDependenciesMetadata,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,kotlinScriptDef,kotlinScriptDefExtensions,runtime,runtimeOnlyDependenciesMetadata,sourcesJar,testAnnotationProcessor,testApiDependenciesMetadata,testCompile,testCompileOnly,testCompileOnlyDependenciesMetadata,testIntransitiveDependenciesMetadata,testKotlinScriptDef,testKotlinScriptDefExtensions,testRuntime
|
||||
44
pkl-codegen-java/pkl-codegen-java.gradle.kts
Normal file
44
pkl-codegen-java/pkl-codegen-java.gradle.kts
Normal file
@@ -0,0 +1,44 @@
|
||||
plugins {
|
||||
pklAllProjects
|
||||
pklKotlinLibrary
|
||||
pklPublishLibrary
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// CliJavaCodeGeneratorOptions exposes CliBaseOptions
|
||||
api(project(":pkl-commons-cli"))
|
||||
|
||||
implementation(project(":pkl-commons"))
|
||||
implementation(project(":pkl-core"))
|
||||
implementation(libs.javaPoet)
|
||||
|
||||
testImplementation(project(":pkl-config-java"))
|
||||
testImplementation(project(":pkl-commons-test"))
|
||||
}
|
||||
|
||||
// with `org.gradle.parallel=true` and without the line below, `test` strangely runs into:
|
||||
// java.lang.NoClassDefFoundError: Lorg/pkl/config/java/ConfigEvaluator;
|
||||
// perhaps somehow related to InMemoryJavaCompiler?
|
||||
tasks.test {
|
||||
mustRunAfter(":pkl-config-java:testFatJar")
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
manifest {
|
||||
attributes += mapOf("Main-Class" to "org.pkl.codegen.java.Main")
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
named<MavenPublication>("library") {
|
||||
pom {
|
||||
url.set("https://github.com/apple/pkl/tree/main/pkl-codegen-java")
|
||||
description.set("""
|
||||
Java source code generator that generates corresponding Java classes for Pkl classes,
|
||||
simplifying consumption of Pkl configuration as statically typed Java objects.
|
||||
""".trimIndent())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.codegen.java
|
||||
|
||||
import java.io.IOException
|
||||
import org.pkl.commons.cli.CliCommand
|
||||
import org.pkl.commons.cli.CliException
|
||||
import org.pkl.commons.createParentDirectories
|
||||
import org.pkl.commons.writeString
|
||||
import org.pkl.core.ModuleSource
|
||||
import org.pkl.core.module.ModuleKeyFactories
|
||||
|
||||
/** API for the Java code generator CLI. */
|
||||
class CliJavaCodeGenerator(private val options: CliJavaCodeGeneratorOptions) :
|
||||
CliCommand(options.base) {
|
||||
|
||||
override fun doRun() {
|
||||
val builder = evaluatorBuilder()
|
||||
try {
|
||||
builder.build().use { evaluator ->
|
||||
for (moduleUri in options.base.normalizedSourceModules) {
|
||||
val schema = evaluator.evaluateSchema(ModuleSource.uri(moduleUri))
|
||||
val codeGenerator = JavaCodeGenerator(schema, options.toJavaCodegenOptions())
|
||||
try {
|
||||
for ((fileName, fileContents) in codeGenerator.output) {
|
||||
val outputFile = options.outputDir.resolve(fileName)
|
||||
try {
|
||||
outputFile.createParentDirectories().writeString(fileContents)
|
||||
} catch (e: IOException) {
|
||||
throw CliException("I/O error writing file `$outputFile`.\nCause: ${e.message}")
|
||||
}
|
||||
}
|
||||
} catch (e: JavaCodeGeneratorException) {
|
||||
throw CliException(e.message!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
ModuleKeyFactories.closeQuietly(builder.moduleKeyFactories)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 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.codegen.java
|
||||
|
||||
import java.nio.file.Path
|
||||
import org.pkl.commons.cli.CliBaseOptions
|
||||
|
||||
/** Configuration options for [CliJavaCodeGenerator]. */
|
||||
data class CliJavaCodeGeneratorOptions(
|
||||
/** Base options shared between CLI commands. */
|
||||
val base: CliBaseOptions,
|
||||
|
||||
/** The directory where generated source code is placed. */
|
||||
val outputDir: Path,
|
||||
|
||||
/** The characters to use for indenting generated source code. */
|
||||
val indent: String = " ",
|
||||
|
||||
/**
|
||||
* Whether to generate public getter methods and private/protected fields instead of public
|
||||
* fields.
|
||||
*/
|
||||
val generateGetters: Boolean = false,
|
||||
|
||||
/** Whether to generate Javadoc based on doc comments for Pkl modules, classes, and properties. */
|
||||
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.
|
||||
*/
|
||||
val paramsAnnotation: String? = null,
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
val nonNullAnnotation: String? = null,
|
||||
|
||||
/** Whether to make generated classes implement [java.io.Serializable] */
|
||||
val implementSerializable: Boolean = false
|
||||
) {
|
||||
fun toJavaCodegenOptions() =
|
||||
JavaCodegenOptions(
|
||||
indent,
|
||||
generateGetters,
|
||||
generateJavadoc,
|
||||
generateSpringBootConfig,
|
||||
paramsAnnotation,
|
||||
nonNullAnnotation,
|
||||
implementSerializable
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,933 @@
|
||||
/**
|
||||
* 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.codegen.java
|
||||
|
||||
import com.squareup.javapoet.*
|
||||
import java.io.StringWriter
|
||||
import java.lang.Deprecated
|
||||
import java.net.URI
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
import javax.lang.model.element.Modifier
|
||||
import kotlin.AssertionError
|
||||
import kotlin.Boolean
|
||||
import kotlin.Int
|
||||
import kotlin.Long
|
||||
import kotlin.RuntimeException
|
||||
import kotlin.String
|
||||
import kotlin.Suppress
|
||||
import kotlin.Unit
|
||||
import kotlin.apply
|
||||
import kotlin.let
|
||||
import kotlin.takeIf
|
||||
import kotlin.to
|
||||
import org.pkl.core.*
|
||||
import org.pkl.core.util.CodeGeneratorUtils
|
||||
|
||||
class JavaCodeGeneratorException(message: String) : RuntimeException(message)
|
||||
|
||||
data class JavaCodegenOptions(
|
||||
/** The characters to use for indenting generated Java code. */
|
||||
val indent: String = " ",
|
||||
|
||||
/**
|
||||
* Whether to generate public getter methods and protected final fields instead of public final
|
||||
* fields.
|
||||
*/
|
||||
val generateGetters: Boolean = false,
|
||||
|
||||
/** Whether to generate Javadoc based on doc comments for Pkl modules, classes, and properties. */
|
||||
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.
|
||||
*/
|
||||
val paramsAnnotation: String? = null,
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
val nonNullAnnotation: String? = null,
|
||||
|
||||
/** Whether to make generated classes implement [java.io.Serializable] */
|
||||
val implementSerializable: Boolean = false
|
||||
)
|
||||
|
||||
/** Entrypoint for the Java code generator API. */
|
||||
class JavaCodeGenerator(
|
||||
private val schema: ModuleSchema,
|
||||
private val codegenOptions: JavaCodegenOptions
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private val STRING = ClassName.get(String::class.java)
|
||||
private val DURATION = ClassName.get(Duration::class.java)
|
||||
private val DURATION_UNIT = ClassName.get(DurationUnit::class.java)
|
||||
private val DATA_SIZE = ClassName.get(DataSize::class.java)
|
||||
private val DATASIZE_UNIT = ClassName.get(DataSizeUnit::class.java)
|
||||
private val PAIR = ClassName.get(Pair::class.java)
|
||||
private val COLLECTION = ClassName.get(Collection::class.java)
|
||||
private val LIST = ClassName.get(List::class.java)
|
||||
private val SET = ClassName.get(Set::class.java)
|
||||
private val MAP = ClassName.get(Map::class.java)
|
||||
private val PMODULE = ClassName.get(PModule::class.java)
|
||||
private val PCLASS = ClassName.get(PClass::class.java)
|
||||
private val PATTERN = ClassName.get(Pattern::class.java)
|
||||
private val URI = ClassName.get(URI::class.java)
|
||||
private val VERSION = ClassName.get(Version::class.java)
|
||||
|
||||
private const val PROPERTY_PREFIX: String = "org.pkl.config.java.mapper."
|
||||
|
||||
private fun toClassName(fqn: String): ClassName {
|
||||
val index = fqn.lastIndexOf(".")
|
||||
if (index == -1) {
|
||||
throw JavaCodeGeneratorException(
|
||||
"""
|
||||
Annotation `$fqn` is not a valid Java class.
|
||||
The name of the annotation should be the canonical Java name of the class, for example, `com.example.Foo`.
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
val packageName = fqn.substring(0, index)
|
||||
val classParts = fqn.substring(index + 1).split('$')
|
||||
return if (classParts.size == 1) {
|
||||
ClassName.get(packageName, classParts.first())
|
||||
} else {
|
||||
ClassName.get(packageName, classParts.first(), *classParts.drop(1).toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val output: Map<String, String>
|
||||
get() {
|
||||
return mapOf(javaFileName to javaFile, propertyFileName to propertiesFile)
|
||||
}
|
||||
|
||||
private val propertyFileName: String
|
||||
get() = "resources/META-INF/org/pkl/config/java/mapper/classes/${schema.moduleName}.properties"
|
||||
|
||||
private val propertiesFile: String
|
||||
get() {
|
||||
val props = Properties()
|
||||
props["$PROPERTY_PREFIX${schema.moduleClass.qualifiedName}"] =
|
||||
schema.moduleClass.toJavaPoetName().reflectionName()
|
||||
for (pClass in schema.classes.values) {
|
||||
props["$PROPERTY_PREFIX${pClass.qualifiedName}"] = pClass.toJavaPoetName().reflectionName()
|
||||
}
|
||||
return StringWriter()
|
||||
.apply { props.store(this, "Java mappings for Pkl module `${schema.moduleName}`") }
|
||||
.toString()
|
||||
}
|
||||
|
||||
private val nonNullAnnotation: AnnotationSpec
|
||||
get() {
|
||||
val annotation = codegenOptions.nonNullAnnotation
|
||||
val className =
|
||||
if (annotation == null) {
|
||||
ClassName.get("org.pkl.config.java.mapper", "NonNull")
|
||||
} else {
|
||||
toClassName(annotation)
|
||||
}
|
||||
return AnnotationSpec.builder(className).build()
|
||||
}
|
||||
|
||||
val javaFileName: String
|
||||
get() = relativeOutputPathFor(schema.moduleName)
|
||||
|
||||
val javaFile: String
|
||||
get() {
|
||||
if (schema.moduleUri.scheme == "pkl") {
|
||||
throw JavaCodeGeneratorException(
|
||||
"Cannot generate Java code for a Pkl standard library module (`${schema.moduleUri}`)."
|
||||
)
|
||||
}
|
||||
|
||||
val pModuleClass = schema.moduleClass
|
||||
val moduleClass = generateTypeSpec(pModuleClass, schema)
|
||||
|
||||
for (pClass in schema.classes.values) {
|
||||
moduleClass.addType(generateTypeSpec(pClass, schema).build())
|
||||
}
|
||||
|
||||
for (typeAlias in schema.typeAliases.values) {
|
||||
val stringLiterals = mutableSetOf<String>()
|
||||
if (CodeGeneratorUtils.isRepresentableAsEnum(typeAlias.aliasedType, stringLiterals)) {
|
||||
moduleClass.addType(generateEnumTypeSpec(typeAlias, stringLiterals).build())
|
||||
}
|
||||
}
|
||||
// generate static append method for module classes w/o parent class; reuse in subclasses and
|
||||
// nested classes
|
||||
if (pModuleClass.superclass!!.info == PClassInfo.Module) {
|
||||
val modifier =
|
||||
if (pModuleClass.isOpen || pModuleClass.isAbstract) Modifier.PROTECTED
|
||||
else Modifier.PRIVATE
|
||||
moduleClass.addMethod(appendPropertyMethod().addModifiers(modifier).build())
|
||||
}
|
||||
|
||||
val moduleName = schema.moduleName
|
||||
val index = moduleName.lastIndexOf(".")
|
||||
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
||||
|
||||
return JavaFile.builder(packageName, moduleClass.build())
|
||||
.indent(codegenOptions.indent)
|
||||
.build()
|
||||
.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 {
|
||||
|
||||
val isModuleClass = pClass == schema.moduleClass
|
||||
val javaPoetClassName = pClass.toJavaPoetName()
|
||||
val superclass =
|
||||
pClass.superclass?.takeIf { it.info != PClassInfo.Typed && it.info != PClassInfo.Module }
|
||||
val superProperties =
|
||||
superclass?.let { renameIfReservedWord(it.allProperties) }?.filterValues { !it.isHidden }
|
||||
?: mapOf()
|
||||
val properties = renameIfReservedWord(pClass.properties).filterValues { !it.isHidden }
|
||||
val allProperties = superProperties + properties
|
||||
|
||||
fun addCtorParameter(
|
||||
builder: MethodSpec.Builder,
|
||||
propJavaName: String,
|
||||
property: PClass.Property
|
||||
) {
|
||||
builder.addParameter(
|
||||
ParameterSpec.builder(property.type.toJavaPoetName(), propJavaName)
|
||||
.addAnnotation(
|
||||
AnnotationSpec.builder(namedAnnotationName)
|
||||
.addMember("value", "\$S", property.simpleName)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
fun generateConstructor(): MethodSpec {
|
||||
val builder =
|
||||
MethodSpec.constructorBuilder()
|
||||
// choose most restrictive access modifier possible
|
||||
.addModifiers(
|
||||
when {
|
||||
pClass.isAbstract -> Modifier.PROTECTED
|
||||
allProperties.isNotEmpty() -> Modifier.PUBLIC // if `false`, has no state
|
||||
pClass.isOpen -> Modifier.PROTECTED
|
||||
else -> Modifier.PRIVATE
|
||||
}
|
||||
)
|
||||
|
||||
if (superProperties.isNotEmpty()) {
|
||||
for ((name, property) in superProperties) {
|
||||
if (properties.containsKey(name)) continue
|
||||
addCtorParameter(builder, name, property)
|
||||
}
|
||||
// $W inserts space or newline (automatic line wrapping)
|
||||
val callArgsStr = superProperties.keys.joinToString(",\$W")
|
||||
// use kotlin interpolation rather than javapoet $L interpolation
|
||||
// as otherwise the $W won't get processed
|
||||
builder.addStatement("super($callArgsStr)")
|
||||
}
|
||||
|
||||
for ((name, property) in properties) {
|
||||
addCtorParameter(builder, name, property)
|
||||
builder.addStatement("this.\$N = \$N", name, name)
|
||||
}
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
fun generateEqualsMethod(): MethodSpec {
|
||||
val builder =
|
||||
MethodSpec.methodBuilder("equals")
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addAnnotation(Override::class.java)
|
||||
.addParameter(Object::class.java, "obj")
|
||||
.returns(Boolean::class.java)
|
||||
.addStatement("if (this == obj) return true")
|
||||
.addStatement("if (obj == null) return false")
|
||||
// generating this.getClass() instead of class literal avoids a SpotBugs warning
|
||||
.addStatement("if (this.getClass() != obj.getClass()) return false")
|
||||
.addStatement("\$T other = (\$T) obj", javaPoetClassName, javaPoetClassName)
|
||||
|
||||
for ((propertyName, property) in allProperties) {
|
||||
val accessor =
|
||||
if ((property.type as? PType.Class)?.pClass?.info == PClassInfo.Regex) "\$N.pattern()"
|
||||
else "\$N"
|
||||
builder.addStatement(
|
||||
"if (!\$T.equals(this.$accessor, other.$accessor)) return false",
|
||||
Objects::class.java,
|
||||
propertyName,
|
||||
propertyName
|
||||
)
|
||||
}
|
||||
|
||||
builder.addStatement("return true")
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
fun generateHashCodeMethod(): MethodSpec {
|
||||
val builder =
|
||||
MethodSpec.methodBuilder("hashCode")
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addAnnotation(Override::class.java)
|
||||
.returns(Int::class.java)
|
||||
.addStatement("int result = 1")
|
||||
|
||||
for (propertyName in allProperties.keys) {
|
||||
builder.addStatement(
|
||||
"result = 31 * result + \$T.hashCode(this.\$N)",
|
||||
Objects::class.java,
|
||||
propertyName
|
||||
)
|
||||
}
|
||||
|
||||
builder.addStatement("return result")
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
fun generateToStringMethod(): MethodSpec {
|
||||
val builder =
|
||||
MethodSpec.methodBuilder("toString")
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addAnnotation(Override::class.java)
|
||||
.returns(String::class.java)
|
||||
|
||||
var builderSize = 50
|
||||
val appendBuilder = CodeBlock.builder()
|
||||
for (propertyName in allProperties.keys) {
|
||||
builderSize += 50
|
||||
appendBuilder.addStatement(
|
||||
"appendProperty(builder, \$S, this.\$N)",
|
||||
propertyName,
|
||||
propertyName
|
||||
)
|
||||
}
|
||||
|
||||
builder
|
||||
.addStatement(
|
||||
"\$T builder = new \$T(\$L)",
|
||||
StringBuilder::class.java,
|
||||
StringBuilder::class.java,
|
||||
builderSize
|
||||
)
|
||||
.addStatement("builder.append(\$T.class.getSimpleName()).append(\" {\")", javaPoetClassName)
|
||||
.addCode(appendBuilder.build())
|
||||
// not using $S here because it generates `"\n" + "{"`
|
||||
// with a line break in the generated code after `+`
|
||||
.addStatement("builder.append(\"\\n}\")")
|
||||
.addStatement("return builder.toString()")
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
// do the minimum work necessary to avoid (most) java compile errors
|
||||
// generating idiomatic Javadoc would require parsing doc comments, converting member links,
|
||||
// etc.
|
||||
fun renderAsJavadoc(docComment: String): String {
|
||||
val escaped = docComment.replace("*/", "*/")
|
||||
return if (escaped[escaped.length - 1] != '\n') escaped + '\n' else escaped
|
||||
}
|
||||
|
||||
fun generateDeprecation(
|
||||
annotations: Collection<PObject>,
|
||||
hasJavadoc: Boolean,
|
||||
addAnnotation: (Class<*>) -> Unit,
|
||||
addJavadoc: (String) -> Unit
|
||||
) {
|
||||
annotations
|
||||
.firstOrNull { it.classInfo == PClassInfo.Deprecated }
|
||||
?.let { deprecation ->
|
||||
addAnnotation(Deprecated::class.java)
|
||||
if (codegenOptions.generateJavadoc) {
|
||||
(deprecation["message"] as String?)?.let {
|
||||
if (hasJavadoc) {
|
||||
addJavadoc("\n")
|
||||
}
|
||||
addJavadoc(renderAsJavadoc("@deprecated $it"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun generateField(propertyName: String, property: PClass.Property): FieldSpec {
|
||||
val builder = FieldSpec.builder(property.type.toJavaPoetName(), propertyName)
|
||||
|
||||
val docComment = property.docComment
|
||||
val hasJavadoc =
|
||||
docComment != null && codegenOptions.generateJavadoc && !codegenOptions.generateGetters
|
||||
if (hasJavadoc) {
|
||||
builder.addJavadoc(renderAsJavadoc(docComment!!))
|
||||
}
|
||||
|
||||
if (codegenOptions.generateGetters) {
|
||||
builder.addModifiers(
|
||||
if (pClass.isAbstract || pClass.isOpen) Modifier.PROTECTED else Modifier.PRIVATE
|
||||
)
|
||||
} else {
|
||||
generateDeprecation(
|
||||
property.annotations,
|
||||
hasJavadoc,
|
||||
{ builder.addAnnotation(it) },
|
||||
{ builder.addJavadoc(it) }
|
||||
)
|
||||
builder.addModifiers(Modifier.PUBLIC)
|
||||
}
|
||||
builder.addModifiers(Modifier.FINAL)
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
fun generateGetter(
|
||||
propertyName: String,
|
||||
property: PClass.Property,
|
||||
isOverridden: Boolean
|
||||
): MethodSpec {
|
||||
val propertyType = property.type
|
||||
val isBooleanProperty =
|
||||
propertyType is PType.Class && propertyType.pClass.info == PClassInfo.Boolean
|
||||
val methodName =
|
||||
(if (isBooleanProperty) "is" else "get") +
|
||||
// can use original name here (property.name rather than propertyName)
|
||||
// because getter name cannot possibly conflict with reserved words
|
||||
property.simpleName.replaceFirstChar { it.titlecaseChar() }
|
||||
|
||||
val builder =
|
||||
MethodSpec.methodBuilder(methodName)
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.returns(propertyType.toJavaPoetName())
|
||||
.addStatement("return \$N", propertyName)
|
||||
if (isOverridden) {
|
||||
builder.addAnnotation(Override::class.java)
|
||||
}
|
||||
|
||||
val docComment = property.docComment
|
||||
val hasJavadoc = docComment != null && codegenOptions.generateJavadoc
|
||||
if (hasJavadoc) {
|
||||
builder.addJavadoc(renderAsJavadoc(docComment!!))
|
||||
}
|
||||
|
||||
generateDeprecation(
|
||||
property.annotations,
|
||||
hasJavadoc,
|
||||
{ builder.addAnnotation(it) },
|
||||
{ builder.addJavadoc(it) }
|
||||
)
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
fun generateWithMethod(propertyName: String, property: PClass.Property): MethodSpec {
|
||||
val methodName = "with" + property.simpleName.replaceFirstChar { it.titlecaseChar() }
|
||||
|
||||
val methodBuilder =
|
||||
MethodSpec.methodBuilder(methodName)
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addParameter(property.type.toJavaPoetName(), propertyName)
|
||||
.returns(javaPoetClassName)
|
||||
|
||||
generateDeprecation(
|
||||
property.annotations,
|
||||
false,
|
||||
{ methodBuilder.addAnnotation(it) },
|
||||
{ methodBuilder.addJavadoc(it) }
|
||||
)
|
||||
|
||||
val codeBuilder = CodeBlock.builder()
|
||||
codeBuilder.add("return new \$T(", javaPoetClassName)
|
||||
var firstProperty = true
|
||||
for (name in superProperties.keys) {
|
||||
if (name in properties) continue
|
||||
if (firstProperty) {
|
||||
firstProperty = false
|
||||
codeBuilder.add("\$N", name)
|
||||
} else {
|
||||
codeBuilder.add(", \$N", name)
|
||||
}
|
||||
}
|
||||
for (name in properties.keys) {
|
||||
if (firstProperty) {
|
||||
firstProperty = false
|
||||
codeBuilder.add("\$N", name)
|
||||
} else {
|
||||
codeBuilder.add(", \$N", name)
|
||||
}
|
||||
}
|
||||
codeBuilder.add(");\n")
|
||||
|
||||
methodBuilder.addCode(codeBuilder.build())
|
||||
return methodBuilder.build()
|
||||
}
|
||||
|
||||
fun generateSpringBootAnnotations(builder: TypeSpec.Builder) {
|
||||
builder.addAnnotation(
|
||||
ClassName.get("org.springframework.boot.context.properties", "ConstructorBinding")
|
||||
)
|
||||
|
||||
if (isModuleClass) {
|
||||
builder.addAnnotation(
|
||||
ClassName.get("org.springframework.boot.context.properties", "ConfigurationProperties")
|
||||
)
|
||||
} else {
|
||||
// not very efficient to repeat computing module property base types for every class
|
||||
val modulePropertiesWithMatchingType =
|
||||
schema.moduleClass.allProperties.values.filter { property ->
|
||||
var propertyType = property.type
|
||||
while (propertyType is PType.Constrained || propertyType is PType.Nullable) {
|
||||
if (propertyType is PType.Constrained) {
|
||||
propertyType = propertyType.baseType
|
||||
} else if (propertyType is PType.Nullable) {
|
||||
propertyType = propertyType.baseType
|
||||
}
|
||||
}
|
||||
propertyType is PType.Class && propertyType.pClass == pClass
|
||||
}
|
||||
if (modulePropertiesWithMatchingType.size == 1) {
|
||||
// exactly one module property has this type -> make it available for direct injection
|
||||
// (potential improvement: make type available for direct injection if it occurs exactly
|
||||
// once in property tree)
|
||||
builder.addAnnotation(
|
||||
AnnotationSpec.builder(
|
||||
ClassName.get(
|
||||
"org.springframework.boot.context.properties",
|
||||
"ConfigurationProperties"
|
||||
)
|
||||
)
|
||||
// use "value" instead of "prefix" to entice JavaPoet to generate a single-line
|
||||
// annotation
|
||||
// that can easily be filtered out by JavaCodeGeneratorTest.`spring boot config`
|
||||
.addMember("value", "\$S", modulePropertiesWithMatchingType.first().simpleName)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
fun generateClass(): TypeSpec.Builder {
|
||||
val builder =
|
||||
TypeSpec.classBuilder(javaPoetClassName.simpleName()).addModifiers(Modifier.PUBLIC)
|
||||
|
||||
if (codegenOptions.implementSerializable && !isModuleClass) {
|
||||
builder.addSuperinterface(java.io.Serializable::class.java)
|
||||
builder.addField(generateSerialVersionUIDField())
|
||||
}
|
||||
|
||||
val docComment = pClass.docComment
|
||||
val hasJavadoc = docComment != null && codegenOptions.generateJavadoc
|
||||
if (hasJavadoc) {
|
||||
builder.addJavadoc(renderAsJavadoc(docComment!!))
|
||||
}
|
||||
|
||||
generateDeprecation(
|
||||
pClass.annotations,
|
||||
hasJavadoc,
|
||||
{ builder.addAnnotation(it) },
|
||||
{ builder.addJavadoc(it) }
|
||||
)
|
||||
|
||||
if (!isModuleClass) {
|
||||
builder.addModifiers(Modifier.STATIC)
|
||||
}
|
||||
|
||||
if (pClass.isAbstract) {
|
||||
builder.addModifiers(Modifier.ABSTRACT)
|
||||
} else if (!pClass.isOpen) {
|
||||
builder.addModifiers(Modifier.FINAL)
|
||||
}
|
||||
|
||||
if (codegenOptions.generateSpringBootConfig) {
|
||||
generateSpringBootAnnotations(builder)
|
||||
}
|
||||
|
||||
builder.addMethod(generateConstructor())
|
||||
|
||||
superclass?.let { builder.superclass(it.toJavaPoetName()) }
|
||||
|
||||
// generate fields, plus getter methods and either setters or `with` methods in alternating
|
||||
// order
|
||||
// `with` methods also need to be generated for superclass properties so that return type is
|
||||
// self type
|
||||
for ((name, property) in allProperties) {
|
||||
if (name in properties) {
|
||||
builder.addField(generateField(name, property))
|
||||
if (codegenOptions.generateGetters) {
|
||||
val isOverridden = name in superProperties
|
||||
builder.addMethod(generateGetter(name, property, isOverridden))
|
||||
}
|
||||
}
|
||||
if (!pClass.isAbstract) {
|
||||
builder.addMethod(generateWithMethod(name, property))
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.isNotEmpty()) {
|
||||
builder
|
||||
.addMethod(generateEqualsMethod())
|
||||
.addMethod(generateHashCodeMethod())
|
||||
.addMethod(generateToStringMethod())
|
||||
}
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
return generateClass()
|
||||
}
|
||||
|
||||
private fun generateSerialVersionUIDField(): FieldSpec {
|
||||
return FieldSpec.builder(Long::class.java, "serialVersionUID", Modifier.PRIVATE)
|
||||
.addModifiers(Modifier.STATIC, Modifier.FINAL)
|
||||
.initializer("0L")
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun generateEnumTypeSpec(
|
||||
typeAlias: TypeAlias,
|
||||
stringLiterals: Set<String>
|
||||
): TypeSpec.Builder {
|
||||
val enumConstantToPklNames =
|
||||
stringLiterals
|
||||
.groupingBy { literal ->
|
||||
CodeGeneratorUtils.toEnumConstantName(literal)
|
||||
?: throw JavaCodeGeneratorException(
|
||||
"Cannot generate Java enum class for Pkl type alias `${typeAlias.displayName}` " +
|
||||
"because string literal type \"$literal\" cannot be converted to a valid enum constant name."
|
||||
)
|
||||
}
|
||||
.reduce { enumConstantName, firstLiteral, secondLiteral ->
|
||||
throw JavaCodeGeneratorException(
|
||||
"Cannot generate Java enum class for Pkl type alias `${typeAlias.displayName}` " +
|
||||
"because string literal types \"$firstLiteral\" and \"$secondLiteral\" " +
|
||||
"would both be converted to enum constant name `$enumConstantName`."
|
||||
)
|
||||
}
|
||||
|
||||
val builder =
|
||||
TypeSpec.enumBuilder(typeAlias.simpleName)
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addField(String::class.java, "value", Modifier.PRIVATE)
|
||||
.addMethod(
|
||||
MethodSpec.constructorBuilder()
|
||||
.addModifiers(Modifier.PRIVATE)
|
||||
.addParameter(String::class.java, "value")
|
||||
.addStatement("this.value = value")
|
||||
.build()
|
||||
)
|
||||
.addMethod(
|
||||
MethodSpec.methodBuilder("toString")
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addAnnotation(Override::class.java)
|
||||
.returns(String::class.java)
|
||||
.addStatement("return this.value")
|
||||
.build()
|
||||
)
|
||||
|
||||
for ((enumConstantName, pklName) in enumConstantToPklNames) {
|
||||
builder.addEnumConstant(
|
||||
enumConstantName,
|
||||
TypeSpec.anonymousClassBuilder("\$S", pklName).build()
|
||||
)
|
||||
}
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
private val namedAnnotationName =
|
||||
if (codegenOptions.paramsAnnotation != null) {
|
||||
toClassName(codegenOptions.paramsAnnotation)
|
||||
} else {
|
||||
ClassName.get("org.pkl.config.java.mapper", "Named")
|
||||
}
|
||||
|
||||
private fun appendPropertyMethod() =
|
||||
MethodSpec.methodBuilder("appendProperty")
|
||||
.addModifiers(Modifier.STATIC)
|
||||
.addParameter(StringBuilder::class.java, "builder")
|
||||
.addParameter(String::class.java, "name")
|
||||
.addParameter(Object::class.java, "value")
|
||||
.addStatement("builder.append(\"\\n \").append(name).append(\" = \")")
|
||||
.addStatement(
|
||||
"\$T lines = \$T.toString(value).split(\"\\n\")",
|
||||
ArrayTypeName.of(String::class.java),
|
||||
Objects::class.java
|
||||
)
|
||||
.addStatement("builder.append(lines[0])")
|
||||
.beginControlFlow("for (int i = 1; i < lines.length; i++)")
|
||||
.addStatement("builder.append(\"\\n \").append(lines[i])")
|
||||
.endControlFlow()
|
||||
|
||||
private fun PClass.toJavaPoetName(): ClassName {
|
||||
val index = moduleName.lastIndexOf(".")
|
||||
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
||||
val moduleClassName = moduleName.substring(index + 1).replaceFirstChar { it.titlecaseChar() }
|
||||
return if (isModuleClass) {
|
||||
ClassName.get(packageName, moduleClassName)
|
||||
} else {
|
||||
ClassName.get(packageName, moduleClassName, simpleName)
|
||||
}
|
||||
}
|
||||
|
||||
// generated type is a nested enum class
|
||||
private fun TypeAlias.toJavaPoetName(): ClassName {
|
||||
val index = moduleName.lastIndexOf(".")
|
||||
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)
|
||||
}
|
||||
|
||||
/** Generate `List<? extends Foo>` if `Foo` is `abstract` or `open`, to allow subclassing. */
|
||||
private fun PType.toJavaPoetTypeArgumentName(): TypeName {
|
||||
val baseName = toJavaPoetName(nullable = false, boxed = true)
|
||||
return if (this is PType.Class && (pClass.isAbstract || pClass.isOpen)) {
|
||||
WildcardTypeName.subtypeOf(baseName)
|
||||
} else {
|
||||
baseName
|
||||
}
|
||||
}
|
||||
|
||||
private fun PType.toJavaPoetName(nullable: Boolean = false, boxed: Boolean = false): TypeName =
|
||||
when (this) {
|
||||
PType.UNKNOWN -> TypeName.OBJECT.nullableIf(nullable)
|
||||
PType.NOTHING -> TypeName.VOID
|
||||
is PType.StringLiteral -> STRING.nullableIf(nullable)
|
||||
is PType.Class -> {
|
||||
// if in doubt, spell it out
|
||||
when (val classInfo = pClass.info) {
|
||||
PClassInfo.Any -> TypeName.OBJECT
|
||||
PClassInfo.Typed,
|
||||
PClassInfo.Dynamic -> TypeName.OBJECT.nullableIf(nullable)
|
||||
PClassInfo.Boolean -> TypeName.BOOLEAN.boxIf(boxed).nullableIf(nullable)
|
||||
PClassInfo.String -> STRING.nullableIf(nullable)
|
||||
// seems more useful to generate `double` than `java.lang.Number`
|
||||
PClassInfo.Number -> TypeName.DOUBLE.boxIf(boxed).nullableIf(nullable)
|
||||
PClassInfo.Int -> TypeName.LONG.boxIf(boxed).nullableIf(nullable)
|
||||
PClassInfo.Float -> TypeName.DOUBLE.boxIf(boxed).nullableIf(nullable)
|
||||
PClassInfo.Duration -> DURATION.nullableIf(nullable)
|
||||
PClassInfo.DataSize -> DATA_SIZE.nullableIf(nullable)
|
||||
PClassInfo.Pair ->
|
||||
ParameterizedTypeName.get(
|
||||
PAIR,
|
||||
if (typeArguments.isEmpty()) {
|
||||
TypeName.OBJECT
|
||||
} else {
|
||||
typeArguments[0].toJavaPoetTypeArgumentName()
|
||||
},
|
||||
if (typeArguments.isEmpty()) {
|
||||
TypeName.OBJECT
|
||||
} else {
|
||||
typeArguments[1].toJavaPoetTypeArgumentName()
|
||||
}
|
||||
)
|
||||
.nullableIf(nullable)
|
||||
PClassInfo.Collection ->
|
||||
ParameterizedTypeName.get(
|
||||
COLLECTION,
|
||||
if (typeArguments.isEmpty()) {
|
||||
TypeName.OBJECT
|
||||
} else {
|
||||
typeArguments[0].toJavaPoetTypeArgumentName()
|
||||
}
|
||||
)
|
||||
.nullableIf(nullable)
|
||||
PClassInfo.List,
|
||||
PClassInfo.Listing -> {
|
||||
ParameterizedTypeName.get(
|
||||
LIST,
|
||||
if (typeArguments.isEmpty()) {
|
||||
TypeName.OBJECT
|
||||
} else {
|
||||
typeArguments[0].toJavaPoetTypeArgumentName()
|
||||
}
|
||||
)
|
||||
.nullableIf(nullable)
|
||||
}
|
||||
PClassInfo.Set ->
|
||||
ParameterizedTypeName.get(
|
||||
SET,
|
||||
if (typeArguments.isEmpty()) {
|
||||
TypeName.OBJECT
|
||||
} else {
|
||||
typeArguments[0].toJavaPoetTypeArgumentName()
|
||||
}
|
||||
)
|
||||
.nullableIf(nullable)
|
||||
PClassInfo.Map,
|
||||
PClassInfo.Mapping ->
|
||||
ParameterizedTypeName.get(
|
||||
MAP,
|
||||
if (typeArguments.isEmpty()) {
|
||||
TypeName.OBJECT
|
||||
} else {
|
||||
typeArguments[0].toJavaPoetTypeArgumentName()
|
||||
},
|
||||
if (typeArguments.isEmpty()) {
|
||||
TypeName.OBJECT
|
||||
} else {
|
||||
typeArguments[1].toJavaPoetTypeArgumentName()
|
||||
}
|
||||
)
|
||||
.nullableIf(nullable)
|
||||
PClassInfo.Module -> PMODULE.nullableIf(nullable)
|
||||
PClassInfo.Class -> PCLASS.nullableIf(nullable)
|
||||
PClassInfo.Regex -> PATTERN.nullableIf(nullable)
|
||||
PClassInfo.Version -> VERSION.nullableIf(nullable)
|
||||
else ->
|
||||
when {
|
||||
!classInfo.isStandardLibraryClass -> pClass.toJavaPoetName().nullableIf(nullable)
|
||||
else ->
|
||||
throw JavaCodeGeneratorException(
|
||||
"Standard library class `${pClass.qualifiedName}` is not supported by Java code generator. " +
|
||||
"If you think this is an omission, please let us know."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
is PType.Nullable -> baseType.toJavaPoetName(nullable = true, boxed = true)
|
||||
is PType.Constrained -> baseType.toJavaPoetName(nullable = nullable, boxed = boxed)
|
||||
is PType.Alias ->
|
||||
when (typeAlias.qualifiedName) {
|
||||
"pkl.base#NonNull" -> TypeName.OBJECT.nullableIf(nullable)
|
||||
"pkl.base#Int8" -> TypeName.BYTE.boxIf(boxed).nullableIf(nullable)
|
||||
"pkl.base#Int16",
|
||||
"pkl.base#UInt8" -> TypeName.SHORT.boxIf(boxed).nullableIf(nullable)
|
||||
"pkl.base#Int32",
|
||||
"pkl.base#UInt16" -> TypeName.INT.boxIf(boxed).nullableIf(nullable)
|
||||
"pkl.base#UInt",
|
||||
"pkl.base#UInt32" -> TypeName.LONG.boxIf(boxed).nullableIf(nullable)
|
||||
"pkl.base#DurationUnit" -> DURATION_UNIT.nullableIf(nullable)
|
||||
"pkl.base#DataSizeUnit" -> DATASIZE_UNIT.nullableIf(nullable)
|
||||
"pkl.base#Uri" -> URI.nullableIf(nullable)
|
||||
else -> {
|
||||
if (CodeGeneratorUtils.isRepresentableAsEnum(aliasedType, null)) {
|
||||
if (typeAlias.isStandardLibraryMember) {
|
||||
throw JavaCodeGeneratorException(
|
||||
"Standard library typealias `${typeAlias.qualifiedName}` is not supported by Java code generator. " +
|
||||
"If you think this is an omission, please let us know."
|
||||
)
|
||||
} else {
|
||||
// reference generated enum class
|
||||
typeAlias.toJavaPoetName().nullableIf(nullable)
|
||||
}
|
||||
} else {
|
||||
// inline type alias
|
||||
aliasedType.toJavaPoetName(nullable)
|
||||
}
|
||||
}
|
||||
}
|
||||
is PType.Function ->
|
||||
throw JavaCodeGeneratorException(
|
||||
"Pkl function types are not supported by the Java code generator."
|
||||
)
|
||||
is PType.Union ->
|
||||
if (CodeGeneratorUtils.isRepresentableAsString(this)) STRING.nullableIf(nullable)
|
||||
else
|
||||
throw JavaCodeGeneratorException(
|
||||
"Pkl union types are not supported by the Java code generator."
|
||||
)
|
||||
else ->
|
||||
// should never encounter PType.TypeVariableNode because it can only occur in stdlib classes
|
||||
throw AssertionError("Encountered unexpected PType subclass: $this")
|
||||
}
|
||||
|
||||
private fun TypeName.nullableIf(isNullable: Boolean): TypeName =
|
||||
if (isPrimitive && isNullable) box()
|
||||
else if (isPrimitive || isNullable) this else annotated(nonNullAnnotation)
|
||||
|
||||
private fun TypeName.boxIf(shouldBox: Boolean): TypeName = if (shouldBox) box() else this
|
||||
|
||||
private fun <T> renameIfReservedWord(map: Map<String, T>): Map<String, T> {
|
||||
return map.mapKeys { (key, _) ->
|
||||
if (key in javaReservedWords) {
|
||||
generateSequence("_$key") { "_$it" }.first { it !in map.keys }
|
||||
} else key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val javaReservedWords =
|
||||
setOf(
|
||||
"_", // java 9+
|
||||
"abstract",
|
||||
"assert",
|
||||
"boolean",
|
||||
"break",
|
||||
"byte",
|
||||
"case",
|
||||
"catch",
|
||||
"char",
|
||||
"class",
|
||||
"const",
|
||||
"continue",
|
||||
"default",
|
||||
"double",
|
||||
"do",
|
||||
"else",
|
||||
"enum",
|
||||
"extends",
|
||||
"false",
|
||||
"final",
|
||||
"finally",
|
||||
"float",
|
||||
"for",
|
||||
"goto",
|
||||
"if",
|
||||
"implements",
|
||||
"import",
|
||||
"instanceof",
|
||||
"int",
|
||||
"interface",
|
||||
"long",
|
||||
"native",
|
||||
"new",
|
||||
"null",
|
||||
"package",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"return",
|
||||
"short",
|
||||
"static",
|
||||
"strictfp",
|
||||
"super",
|
||||
"switch",
|
||||
"synchronized",
|
||||
"this",
|
||||
"throw",
|
||||
"throws",
|
||||
"transient",
|
||||
"true",
|
||||
"try",
|
||||
"void",
|
||||
"volatile",
|
||||
"while"
|
||||
)
|
||||
126
pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/Main.kt
Normal file
126
pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/Main.kt
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@file:JvmName("Main")
|
||||
|
||||
package org.pkl.codegen.java
|
||||
|
||||
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.types.path
|
||||
import java.nio.file.Path
|
||||
import org.pkl.commons.cli.CliBaseOptions
|
||||
import org.pkl.commons.cli.cliMain
|
||||
import org.pkl.commons.cli.commands.ModulesCommand
|
||||
import org.pkl.commons.toPath
|
||||
import org.pkl.core.Release
|
||||
|
||||
/** Main method for the Java code generator CLI. */
|
||||
internal fun main(args: Array<String>) {
|
||||
cliMain { PklJavaCodegenCommand().main(args) }
|
||||
}
|
||||
|
||||
class PklJavaCodegenCommand :
|
||||
ModulesCommand(
|
||||
name = "pkl-codegen-java",
|
||||
helpLink = Release.current().documentation().homepage(),
|
||||
) {
|
||||
|
||||
private val defaults = CliJavaCodeGeneratorOptions(CliBaseOptions(), "".toPath())
|
||||
|
||||
private val outputDir: Path by
|
||||
option(
|
||||
names = arrayOf("-o", "--output-dir"),
|
||||
metavar = "<path>",
|
||||
help = "The directory where generated source code is placed."
|
||||
)
|
||||
.path()
|
||||
.default(defaults.outputDir)
|
||||
|
||||
private val indent: String by
|
||||
option(
|
||||
names = arrayOf("--indent"),
|
||||
metavar = "<chars>",
|
||||
help = "The characters to use for indenting generated source code."
|
||||
)
|
||||
.default(defaults.indent)
|
||||
|
||||
private val generateGetters: Boolean by
|
||||
option(
|
||||
names = arrayOf("--generate-getters"),
|
||||
help =
|
||||
"Whether to generate public getter methods and " +
|
||||
"private final fields instead of public final fields."
|
||||
)
|
||||
.flag()
|
||||
|
||||
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."
|
||||
)
|
||||
.flag()
|
||||
|
||||
private val generateSpringboot: Boolean by
|
||||
option(
|
||||
names = arrayOf("--generate-spring-boot"),
|
||||
help = "Whether to generate config classes for use with Spring boot."
|
||||
)
|
||||
.flag()
|
||||
|
||||
private val paramsAnnotation: String? by
|
||||
option(
|
||||
names = arrayOf("--params-annotation"),
|
||||
help = "Fully qualified name of the annotation to use on constructor parameters."
|
||||
)
|
||||
|
||||
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.
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
private val implementSerializable: Boolean by
|
||||
option(
|
||||
names = arrayOf("--implement-serializable"),
|
||||
help = "Whether to make generated classes implement java.io.Serializable."
|
||||
)
|
||||
.flag()
|
||||
|
||||
override fun run() {
|
||||
val options =
|
||||
CliJavaCodeGeneratorOptions(
|
||||
base = baseOptions.baseOptions(modules, projectOptions),
|
||||
outputDir = outputDir,
|
||||
indent = indent,
|
||||
generateGetters = generateGetters,
|
||||
generateJavadoc = generateJavadoc,
|
||||
generateSpringBootConfig = generateSpringboot,
|
||||
paramsAnnotation = paramsAnnotation,
|
||||
nonNullAnnotation = nonNullAnnotation,
|
||||
implementSerializable = implementSerializable
|
||||
)
|
||||
CliJavaCodeGenerator(options).run()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* 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.codegen.java
|
||||
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.listDirectoryEntries
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import org.pkl.commons.cli.CliBaseOptions
|
||||
import org.pkl.commons.readString
|
||||
|
||||
class CliJavaCodeGeneratorTest {
|
||||
|
||||
private val dollar = "$"
|
||||
|
||||
@Test
|
||||
fun `module inheritance`(@TempDir tempDir: Path) {
|
||||
val module1 =
|
||||
PklModule(
|
||||
"org.mod1",
|
||||
"""
|
||||
open module org.mod1
|
||||
|
||||
pigeon: Person
|
||||
|
||||
class Person {
|
||||
name: String
|
||||
age: Int
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
val module2 =
|
||||
PklModule(
|
||||
"org.mod2",
|
||||
"""
|
||||
module org.mod2
|
||||
|
||||
extends "mod1.pkl"
|
||||
|
||||
parrot: Person
|
||||
"""
|
||||
)
|
||||
|
||||
val module1File = module1.writeToDisk(tempDir.resolve("org/mod1.pkl"))
|
||||
val module2File = module2.writeToDisk(tempDir.resolve("org/mod2.pkl"))
|
||||
val outputDir = tempDir.resolve("output")
|
||||
|
||||
val generator =
|
||||
CliJavaCodeGenerator(
|
||||
CliJavaCodeGeneratorOptions(
|
||||
CliBaseOptions(listOf(module1File.toUri(), module2File.toUri())),
|
||||
outputDir
|
||||
)
|
||||
)
|
||||
|
||||
generator.run()
|
||||
|
||||
val javaDir = outputDir.resolve("java")
|
||||
|
||||
val moduleJavaFiles = javaDir.resolve("org").listDirectoryEntries()
|
||||
assertThat(moduleJavaFiles.map { it.fileName.toString() })
|
||||
.containsExactlyInAnyOrder("Mod1.java", "Mod2.java")
|
||||
|
||||
val module1JavaFile = javaDir.resolve("org/Mod1.java")
|
||||
assertContains(
|
||||
"""
|
||||
|public class Mod1 {
|
||||
| public final @NonNull Person pigeon;
|
||||
""",
|
||||
module1JavaFile.readString()
|
||||
)
|
||||
|
||||
val module2JavaFile = javaDir.resolve("org/Mod2.java")
|
||||
assertContains(
|
||||
"""
|
||||
|public final class Mod2 extends Mod1 {
|
||||
| public final Mod1. @NonNull Person parrot;
|
||||
""",
|
||||
module2JavaFile.readString()
|
||||
)
|
||||
val resourcesDir = outputDir.resolve("resources/META-INF/org/pkl/config/java/mapper/classes/")
|
||||
|
||||
val module1PropertiesFile = resourcesDir.resolve("org.mod1.properties")
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
org.pkl.config.java.mapper.org.mod1\#Person=org.Mod1${dollar}Person
|
||||
org.pkl.config.java.mapper.org.mod1\#ModuleClass=org.Mod1
|
||||
"""
|
||||
.trimIndent(),
|
||||
module1PropertiesFile.readString()
|
||||
)
|
||||
|
||||
val module2PropertiesFile = resourcesDir.resolve("org.mod2.properties")
|
||||
|
||||
assertContains(
|
||||
"""
|
||||
org.pkl.config.java.mapper.org.mod2\#ModuleClass=org.Mod2
|
||||
"""
|
||||
.trimIndent(),
|
||||
module2PropertiesFile.readString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `class name clashes`(@TempDir tempDir: Path) {
|
||||
val module1 =
|
||||
PklModule(
|
||||
"org.mod1",
|
||||
"""
|
||||
module org.mod1
|
||||
|
||||
class Person {
|
||||
name: String
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
val module2 =
|
||||
PklModule(
|
||||
"org.mod2",
|
||||
"""
|
||||
module org.mod2
|
||||
|
||||
import "mod1.pkl"
|
||||
|
||||
person1: mod1.Person
|
||||
person2: Person
|
||||
|
||||
class Person {
|
||||
age: Int
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
val module1PklFile = module1.writeToDisk(tempDir.resolve("org/mod1.pkl"))
|
||||
val module2PklFile = module2.writeToDisk(tempDir.resolve("org/mod2.pkl"))
|
||||
val outputDir = tempDir.resolve("output")
|
||||
|
||||
val generator =
|
||||
CliJavaCodeGenerator(
|
||||
CliJavaCodeGeneratorOptions(
|
||||
CliBaseOptions(listOf(module1PklFile.toUri(), module2PklFile.toUri())),
|
||||
outputDir
|
||||
)
|
||||
)
|
||||
|
||||
generator.run()
|
||||
|
||||
val module2JavaFile = outputDir.resolve("java/org/Mod2.java")
|
||||
assertContains(
|
||||
"""
|
||||
|public final class Mod2 {
|
||||
| public final Mod1. @NonNull Person person1;
|
||||
|
|
||||
| public final @NonNull Person person2;
|
||||
""",
|
||||
module2JavaFile.readString()
|
||||
)
|
||||
}
|
||||
|
||||
private fun assertContains(part: String, code: String) {
|
||||
val trimmedPart = part.trim().trimMargin()
|
||||
if (!code.contains(trimmedPart)) {
|
||||
// check for equality to get better error output (ide diff dialog)
|
||||
assertThat(code).isEqualTo(trimmedPart)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* 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.codegen.java
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.net.URI
|
||||
import javax.tools.*
|
||||
|
||||
class CompilationFailedException(msg: String) : RuntimeException(msg)
|
||||
|
||||
object InMemoryJavaCompiler {
|
||||
fun compile(sourceFiles: Map<String, String>): Map<String, Class<*>> {
|
||||
val compiler = ToolProvider.getSystemJavaCompiler()
|
||||
val diagnosticsCollector = DiagnosticCollector<JavaFileObject>()
|
||||
val fileManager =
|
||||
InMemoryFileManager(compiler.getStandardFileManager(diagnosticsCollector, null, null))
|
||||
val sourceObjects =
|
||||
sourceFiles.map { (filename, contents) -> ReadableSourceFileObject(filename, contents) }
|
||||
val task = compiler.getTask(null, fileManager, diagnosticsCollector, null, null, sourceObjects)
|
||||
val result = task.call()
|
||||
if (!result) {
|
||||
throw CompilationFailedException(
|
||||
buildString {
|
||||
appendLine("Compilation failed. Error(s):")
|
||||
for (diagnostic in diagnosticsCollector.diagnostics) {
|
||||
appendLine(diagnostic.getMessage(null))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
val loader = ClassFileObjectLoader(fileManager.outputFiles)
|
||||
return fileManager.outputFiles.mapValues { loader.loadClass(it.key) }
|
||||
}
|
||||
}
|
||||
|
||||
private class ClassFileObjectLoader(val fileObjects: Map<String, WritableBinaryFileObject>) :
|
||||
ClassLoader(ClassFileObjectLoader::class.java.classLoader) {
|
||||
|
||||
override fun findClass(name: String): Class<*> {
|
||||
val obj = fileObjects[name]
|
||||
if (obj == null || obj.kind != JavaFileObject.Kind.CLASS) {
|
||||
throw ClassNotFoundException(name)
|
||||
}
|
||||
val array = obj.out.toByteArray()
|
||||
return defineClass(name, array, 0, array.size)
|
||||
}
|
||||
}
|
||||
|
||||
private class ReadableSourceFileObject(path: String, private val contents: String) :
|
||||
SimpleJavaFileObject(URI(path), JavaFileObject.Kind.SOURCE) {
|
||||
|
||||
override fun openInputStream(): InputStream = contents.byteInputStream()
|
||||
|
||||
override fun getCharContent(ignoreEncodingErrors: Boolean): CharSequence = contents
|
||||
}
|
||||
|
||||
private class WritableBinaryFileObject(className: String, kind: JavaFileObject.Kind) :
|
||||
SimpleJavaFileObject(URI("/${className.replace(".", "/")}.${kind.extension}"), kind) {
|
||||
val out = ByteArrayOutputStream()
|
||||
|
||||
override fun openOutputStream(): OutputStream = out
|
||||
}
|
||||
|
||||
private class InMemoryFileManager(delegate: JavaFileManager) :
|
||||
ForwardingJavaFileManager<JavaFileManager>(delegate) {
|
||||
|
||||
val outputFiles = mutableMapOf<String, WritableBinaryFileObject>()
|
||||
|
||||
override fun getJavaFileForOutput(
|
||||
location: JavaFileManager.Location,
|
||||
className: String,
|
||||
kind: JavaFileObject.Kind,
|
||||
sibling: FileObject
|
||||
): JavaFileObject {
|
||||
|
||||
return WritableBinaryFileObject(className, kind).also { outputFiles[className] = it }
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.codegen.java
|
||||
|
||||
import java.nio.file.Path
|
||||
import org.pkl.commons.createParentDirectories
|
||||
import org.pkl.commons.writeString
|
||||
|
||||
data class PklModule(val name: String, val content: String) {
|
||||
fun writeToDisk(path: Path): Path {
|
||||
return path.createParentDirectories().writeString(content)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package my;
|
||||
|
||||
import java.lang.Object;
|
||||
import java.lang.Override;
|
||||
import java.lang.String;
|
||||
import java.lang.StringBuilder;
|
||||
import java.util.Objects;
|
||||
import org.pkl.config.java.mapper.Named;
|
||||
import org.pkl.config.java.mapper.NonNull;
|
||||
import org.pkl.core.DataSize;
|
||||
import org.pkl.core.Duration;
|
||||
|
||||
public final class Mod {
|
||||
private Mod() {
|
||||
}
|
||||
|
||||
private static void appendProperty(StringBuilder builder, String name, Object value) {
|
||||
builder.append("\n ").append(name).append(" = ");
|
||||
String[] lines = Objects.toString(value).split("\n");
|
||||
builder.append(lines[0]);
|
||||
for (int i = 1; i < lines.length; i++) {
|
||||
builder.append("\n ").append(lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class GenerateGetters {
|
||||
private final boolean urgent;
|
||||
|
||||
private final @NonNull String url;
|
||||
|
||||
private final @NonNull DataSize diskSize;
|
||||
|
||||
private final @NonNull Duration ETA;
|
||||
|
||||
private final @NonNull String _package;
|
||||
|
||||
public GenerateGetters(@Named("urgent") boolean urgent, @Named("url") @NonNull String url,
|
||||
@Named("diskSize") @NonNull DataSize diskSize, @Named("ETA") @NonNull Duration ETA,
|
||||
@Named("package") @NonNull String _package) {
|
||||
this.urgent = urgent;
|
||||
this.url = url;
|
||||
this.diskSize = diskSize;
|
||||
this.ETA = ETA;
|
||||
this._package = _package;
|
||||
}
|
||||
|
||||
public boolean isUrgent() {
|
||||
return urgent;
|
||||
}
|
||||
|
||||
public GenerateGetters withUrgent(boolean urgent) {
|
||||
return new GenerateGetters(urgent, url, diskSize, ETA, _package);
|
||||
}
|
||||
|
||||
public @NonNull String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public GenerateGetters withUrl(@NonNull String url) {
|
||||
return new GenerateGetters(urgent, url, diskSize, ETA, _package);
|
||||
}
|
||||
|
||||
public @NonNull DataSize getDiskSize() {
|
||||
return diskSize;
|
||||
}
|
||||
|
||||
public GenerateGetters withDiskSize(@NonNull DataSize diskSize) {
|
||||
return new GenerateGetters(urgent, url, diskSize, ETA, _package);
|
||||
}
|
||||
|
||||
public @NonNull Duration getETA() {
|
||||
return ETA;
|
||||
}
|
||||
|
||||
public GenerateGetters withETA(@NonNull Duration ETA) {
|
||||
return new GenerateGetters(urgent, url, diskSize, ETA, _package);
|
||||
}
|
||||
|
||||
public @NonNull String getPackage() {
|
||||
return _package;
|
||||
}
|
||||
|
||||
public GenerateGetters withPackage(@NonNull String _package) {
|
||||
return new GenerateGetters(urgent, url, diskSize, ETA, _package);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (this.getClass() != obj.getClass()) return false;
|
||||
GenerateGetters other = (GenerateGetters) obj;
|
||||
if (!Objects.equals(this.urgent, other.urgent)) return false;
|
||||
if (!Objects.equals(this.url, other.url)) return false;
|
||||
if (!Objects.equals(this.diskSize, other.diskSize)) return false;
|
||||
if (!Objects.equals(this.ETA, other.ETA)) return false;
|
||||
if (!Objects.equals(this._package, other._package)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + Objects.hashCode(this.urgent);
|
||||
result = 31 * result + Objects.hashCode(this.url);
|
||||
result = 31 * result + Objects.hashCode(this.diskSize);
|
||||
result = 31 * result + Objects.hashCode(this.ETA);
|
||||
result = 31 * result + Objects.hashCode(this._package);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(300);
|
||||
builder.append(GenerateGetters.class.getSimpleName()).append(" {");
|
||||
appendProperty(builder, "urgent", this.urgent);
|
||||
appendProperty(builder, "url", this.url);
|
||||
appendProperty(builder, "diskSize", this.diskSize);
|
||||
appendProperty(builder, "ETA", this.ETA);
|
||||
appendProperty(builder, "_package", this._package);
|
||||
builder.append("\n}");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package my;
|
||||
|
||||
import java.lang.Object;
|
||||
import java.lang.Override;
|
||||
import java.lang.String;
|
||||
import java.lang.StringBuilder;
|
||||
import java.util.Objects;
|
||||
import org.pkl.config.java.mapper.Named;
|
||||
import org.pkl.config.java.mapper.NonNull;
|
||||
import org.pkl.core.Duration;
|
||||
|
||||
public final class Mod {
|
||||
private Mod() {
|
||||
}
|
||||
|
||||
private static void appendProperty(StringBuilder builder, String name, Object value) {
|
||||
builder.append("\n ").append(name).append(" = ");
|
||||
String[] lines = Objects.toString(value).split("\n");
|
||||
builder.append(lines[0]);
|
||||
for (int i = 1; i < lines.length; i++) {
|
||||
builder.append("\n ").append(lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class Foo {
|
||||
protected final long one;
|
||||
|
||||
protected Foo(@Named("one") long one) {
|
||||
this.one = one;
|
||||
}
|
||||
|
||||
public long getOne() {
|
||||
return one;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (this.getClass() != obj.getClass()) return false;
|
||||
Foo other = (Foo) obj;
|
||||
if (!Objects.equals(this.one, other.one)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + Objects.hashCode(this.one);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(100);
|
||||
builder.append(Foo.class.getSimpleName()).append(" {");
|
||||
appendProperty(builder, "one", this.one);
|
||||
builder.append("\n}");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static class None extends Foo {
|
||||
public None(@Named("one") long one) {
|
||||
super(one);
|
||||
}
|
||||
|
||||
public None withOne(long one) {
|
||||
return new None(one);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Bar extends None {
|
||||
protected final String two;
|
||||
|
||||
public Bar(@Named("one") long one, @Named("two") String two) {
|
||||
super(one);
|
||||
this.two = two;
|
||||
}
|
||||
|
||||
public Bar withOne(long one) {
|
||||
return new Bar(one, two);
|
||||
}
|
||||
|
||||
public String getTwo() {
|
||||
return two;
|
||||
}
|
||||
|
||||
public Bar withTwo(String two) {
|
||||
return new Bar(one, two);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (this.getClass() != obj.getClass()) return false;
|
||||
Bar other = (Bar) obj;
|
||||
if (!Objects.equals(this.one, other.one)) return false;
|
||||
if (!Objects.equals(this.two, other.two)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + Objects.hashCode(this.one);
|
||||
result = 31 * result + Objects.hashCode(this.two);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(150);
|
||||
builder.append(Bar.class.getSimpleName()).append(" {");
|
||||
appendProperty(builder, "one", this.one);
|
||||
appendProperty(builder, "two", this.two);
|
||||
builder.append("\n}");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Baz extends Bar {
|
||||
private final @NonNull Duration three;
|
||||
|
||||
public Baz(@Named("one") long one, @Named("two") String two,
|
||||
@Named("three") @NonNull Duration three) {
|
||||
super(one, two);
|
||||
this.three = three;
|
||||
}
|
||||
|
||||
public Baz withOne(long one) {
|
||||
return new Baz(one, two, three);
|
||||
}
|
||||
|
||||
public Baz withTwo(String two) {
|
||||
return new Baz(one, two, three);
|
||||
}
|
||||
|
||||
public @NonNull Duration getThree() {
|
||||
return three;
|
||||
}
|
||||
|
||||
public Baz withThree(@NonNull Duration three) {
|
||||
return new Baz(one, two, three);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (this.getClass() != obj.getClass()) return false;
|
||||
Baz other = (Baz) obj;
|
||||
if (!Objects.equals(this.one, other.one)) return false;
|
||||
if (!Objects.equals(this.two, other.two)) return false;
|
||||
if (!Objects.equals(this.three, other.three)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + Objects.hashCode(this.one);
|
||||
result = 31 * result + Objects.hashCode(this.two);
|
||||
result = 31 * result + Objects.hashCode(this.three);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(200);
|
||||
builder.append(Baz.class.getSimpleName()).append(" {");
|
||||
appendProperty(builder, "one", this.one);
|
||||
appendProperty(builder, "two", this.two);
|
||||
appendProperty(builder, "three", this.three);
|
||||
builder.append("\n}");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package my;
|
||||
|
||||
import java.lang.Object;
|
||||
import java.lang.Override;
|
||||
import java.lang.String;
|
||||
import java.lang.StringBuilder;
|
||||
import java.util.Objects;
|
||||
import org.pkl.config.java.mapper.Named;
|
||||
import org.pkl.config.java.mapper.NonNull;
|
||||
|
||||
/**
|
||||
* module comment.
|
||||
* *emphasized* `code`.
|
||||
*/
|
||||
public final class Mod {
|
||||
/**
|
||||
* module property comment.
|
||||
* *emphasized* `code`.
|
||||
*/
|
||||
public final @NonNull Person pigeon;
|
||||
|
||||
public Mod(@Named("pigeon") @NonNull Person pigeon) {
|
||||
this.pigeon = pigeon;
|
||||
}
|
||||
|
||||
public Mod withPigeon(@NonNull Person pigeon) {
|
||||
return new Mod(pigeon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (this.getClass() != obj.getClass()) return false;
|
||||
Mod other = (Mod) obj;
|
||||
if (!Objects.equals(this.pigeon, other.pigeon)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + Objects.hashCode(this.pigeon);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(100);
|
||||
builder.append(Mod.class.getSimpleName()).append(" {");
|
||||
appendProperty(builder, "pigeon", this.pigeon);
|
||||
builder.append("\n}");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static void appendProperty(StringBuilder builder, String name, Object value) {
|
||||
builder.append("\n ").append(name).append(" = ");
|
||||
String[] lines = Objects.toString(value).split("\n");
|
||||
builder.append(lines[0]);
|
||||
for (int i = 1; i < lines.length; i++) {
|
||||
builder.append("\n ").append(lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* class comment.
|
||||
* *emphasized* `code`.
|
||||
*/
|
||||
public static final class Person {
|
||||
/**
|
||||
* class property comment.
|
||||
* *emphasized* `code`.
|
||||
*/
|
||||
public final @NonNull String name;
|
||||
|
||||
public Person(@Named("name") @NonNull String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Person withName(@NonNull String name) {
|
||||
return new Person(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (this.getClass() != obj.getClass()) return false;
|
||||
Person other = (Person) obj;
|
||||
if (!Objects.equals(this.name, other.name)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + Objects.hashCode(this.name);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(100);
|
||||
builder.append(Person.class.getSimpleName()).append(" {");
|
||||
appendProperty(builder, "name", this.name);
|
||||
builder.append("\n}");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
package my;
|
||||
|
||||
import java.lang.Object;
|
||||
import java.lang.Override;
|
||||
import java.lang.String;
|
||||
import java.lang.StringBuilder;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import org.pkl.config.java.mapper.Named;
|
||||
import org.pkl.config.java.mapper.NonNull;
|
||||
import org.pkl.core.DataSize;
|
||||
import org.pkl.core.DataSizeUnit;
|
||||
import org.pkl.core.Duration;
|
||||
import org.pkl.core.DurationUnit;
|
||||
import org.pkl.core.Pair;
|
||||
|
||||
public final class Mod {
|
||||
private Mod() {
|
||||
}
|
||||
|
||||
private static void appendProperty(StringBuilder builder, String name, Object value) {
|
||||
builder.append("\n ").append(name).append(" = ");
|
||||
String[] lines = Objects.toString(value).split("\n");
|
||||
builder.append(lines[0]);
|
||||
for (int i = 1; i < lines.length; i++) {
|
||||
builder.append("\n ").append(lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class PropertyTypes {
|
||||
public final boolean _boolean;
|
||||
|
||||
public final long _int;
|
||||
|
||||
public final double _float;
|
||||
|
||||
public final @NonNull String string;
|
||||
|
||||
public final @NonNull Duration duration;
|
||||
|
||||
public final @NonNull DurationUnit durationUnit;
|
||||
|
||||
public final @NonNull DataSize dataSize;
|
||||
|
||||
public final @NonNull DataSizeUnit dataSizeUnit;
|
||||
|
||||
public final String nullable;
|
||||
|
||||
public final String nullable2;
|
||||
|
||||
public final @NonNull Pair<Object, Object> pair;
|
||||
|
||||
public final @NonNull Pair<@NonNull String, @NonNull Other> pair2;
|
||||
|
||||
public final @NonNull Collection<Object> coll;
|
||||
|
||||
public final @NonNull Collection<@NonNull Other> coll2;
|
||||
|
||||
public final @NonNull List<Object> list;
|
||||
|
||||
public final @NonNull List<@NonNull Other> list2;
|
||||
|
||||
public final @NonNull Set<Object> set;
|
||||
|
||||
public final @NonNull Set<@NonNull Other> set2;
|
||||
|
||||
public final @NonNull Map<Object, Object> map;
|
||||
|
||||
public final @NonNull Map<@NonNull String, @NonNull Other> map2;
|
||||
|
||||
public final @NonNull Map<Object, Object> container;
|
||||
|
||||
public final @NonNull Map<@NonNull String, @NonNull Other> container2;
|
||||
|
||||
public final @NonNull Other other;
|
||||
|
||||
public final @NonNull Pattern regex;
|
||||
|
||||
public final Object any;
|
||||
|
||||
public final @NonNull Object nonNull;
|
||||
|
||||
public final @NonNull Direction _enum;
|
||||
|
||||
public PropertyTypes(@Named("boolean") boolean _boolean, @Named("int") long _int,
|
||||
@Named("float") double _float, @Named("string") @NonNull String string,
|
||||
@Named("duration") @NonNull Duration duration,
|
||||
@Named("durationUnit") @NonNull DurationUnit durationUnit,
|
||||
@Named("dataSize") @NonNull DataSize dataSize,
|
||||
@Named("dataSizeUnit") @NonNull DataSizeUnit dataSizeUnit,
|
||||
@Named("nullable") String nullable, @Named("nullable2") String nullable2,
|
||||
@Named("pair") @NonNull Pair<Object, Object> pair,
|
||||
@Named("pair2") @NonNull Pair<@NonNull String, @NonNull Other> pair2,
|
||||
@Named("coll") @NonNull Collection<Object> coll,
|
||||
@Named("coll2") @NonNull Collection<@NonNull Other> coll2,
|
||||
@Named("list") @NonNull List<Object> list,
|
||||
@Named("list2") @NonNull List<@NonNull Other> list2, @Named("set") @NonNull Set<Object> set,
|
||||
@Named("set2") @NonNull Set<@NonNull Other> set2,
|
||||
@Named("map") @NonNull Map<Object, Object> map,
|
||||
@Named("map2") @NonNull Map<@NonNull String, @NonNull Other> map2,
|
||||
@Named("container") @NonNull Map<Object, Object> container,
|
||||
@Named("container2") @NonNull Map<@NonNull String, @NonNull Other> container2,
|
||||
@Named("other") @NonNull Other other, @Named("regex") @NonNull Pattern regex,
|
||||
@Named("any") Object any, @Named("nonNull") @NonNull Object nonNull,
|
||||
@Named("enum") @NonNull Direction _enum) {
|
||||
this._boolean = _boolean;
|
||||
this._int = _int;
|
||||
this._float = _float;
|
||||
this.string = string;
|
||||
this.duration = duration;
|
||||
this.durationUnit = durationUnit;
|
||||
this.dataSize = dataSize;
|
||||
this.dataSizeUnit = dataSizeUnit;
|
||||
this.nullable = nullable;
|
||||
this.nullable2 = nullable2;
|
||||
this.pair = pair;
|
||||
this.pair2 = pair2;
|
||||
this.coll = coll;
|
||||
this.coll2 = coll2;
|
||||
this.list = list;
|
||||
this.list2 = list2;
|
||||
this.set = set;
|
||||
this.set2 = set2;
|
||||
this.map = map;
|
||||
this.map2 = map2;
|
||||
this.container = container;
|
||||
this.container2 = container2;
|
||||
this.other = other;
|
||||
this.regex = regex;
|
||||
this.any = any;
|
||||
this.nonNull = nonNull;
|
||||
this._enum = _enum;
|
||||
}
|
||||
|
||||
public PropertyTypes withBoolean(boolean _boolean) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withInt(long _int) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withFloat(double _float) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withString(@NonNull String string) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withDuration(@NonNull Duration duration) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withDurationUnit(@NonNull DurationUnit durationUnit) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withDataSize(@NonNull DataSize dataSize) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withDataSizeUnit(@NonNull DataSizeUnit dataSizeUnit) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withNullable(String nullable) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withNullable2(String nullable2) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withPair(@NonNull Pair<Object, Object> pair) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withPair2(@NonNull Pair<@NonNull String, @NonNull Other> pair2) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withColl(@NonNull Collection<Object> coll) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withColl2(@NonNull Collection<@NonNull Other> coll2) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withList(@NonNull List<Object> list) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withList2(@NonNull List<@NonNull Other> list2) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withSet(@NonNull Set<Object> set) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withSet2(@NonNull Set<@NonNull Other> set2) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withMap(@NonNull Map<Object, Object> map) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withMap2(@NonNull Map<@NonNull String, @NonNull Other> map2) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withContainer(@NonNull Map<Object, Object> container) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withContainer2(@NonNull Map<@NonNull String, @NonNull Other> container2) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withOther(@NonNull Other other) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withRegex(@NonNull Pattern regex) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withAny(Object any) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withNonNull(@NonNull Object nonNull) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
public PropertyTypes withEnum(@NonNull Direction _enum) {
|
||||
return new PropertyTypes(_boolean, _int, _float, string, duration, durationUnit, dataSize, dataSizeUnit, nullable, nullable2, pair, pair2, coll, coll2, list, list2, set, set2, map, map2, container, container2, other, regex, any, nonNull, _enum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (this.getClass() != obj.getClass()) return false;
|
||||
PropertyTypes other = (PropertyTypes) obj;
|
||||
if (!Objects.equals(this._boolean, other._boolean)) return false;
|
||||
if (!Objects.equals(this._int, other._int)) return false;
|
||||
if (!Objects.equals(this._float, other._float)) return false;
|
||||
if (!Objects.equals(this.string, other.string)) return false;
|
||||
if (!Objects.equals(this.duration, other.duration)) return false;
|
||||
if (!Objects.equals(this.durationUnit, other.durationUnit)) return false;
|
||||
if (!Objects.equals(this.dataSize, other.dataSize)) return false;
|
||||
if (!Objects.equals(this.dataSizeUnit, other.dataSizeUnit)) return false;
|
||||
if (!Objects.equals(this.nullable, other.nullable)) return false;
|
||||
if (!Objects.equals(this.nullable2, other.nullable2)) return false;
|
||||
if (!Objects.equals(this.pair, other.pair)) return false;
|
||||
if (!Objects.equals(this.pair2, other.pair2)) return false;
|
||||
if (!Objects.equals(this.coll, other.coll)) return false;
|
||||
if (!Objects.equals(this.coll2, other.coll2)) return false;
|
||||
if (!Objects.equals(this.list, other.list)) return false;
|
||||
if (!Objects.equals(this.list2, other.list2)) return false;
|
||||
if (!Objects.equals(this.set, other.set)) return false;
|
||||
if (!Objects.equals(this.set2, other.set2)) return false;
|
||||
if (!Objects.equals(this.map, other.map)) return false;
|
||||
if (!Objects.equals(this.map2, other.map2)) return false;
|
||||
if (!Objects.equals(this.container, other.container)) return false;
|
||||
if (!Objects.equals(this.container2, other.container2)) return false;
|
||||
if (!Objects.equals(this.other, other.other)) return false;
|
||||
if (!Objects.equals(this.regex.pattern(), other.regex.pattern())) return false;
|
||||
if (!Objects.equals(this.any, other.any)) return false;
|
||||
if (!Objects.equals(this.nonNull, other.nonNull)) return false;
|
||||
if (!Objects.equals(this._enum, other._enum)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + Objects.hashCode(this._boolean);
|
||||
result = 31 * result + Objects.hashCode(this._int);
|
||||
result = 31 * result + Objects.hashCode(this._float);
|
||||
result = 31 * result + Objects.hashCode(this.string);
|
||||
result = 31 * result + Objects.hashCode(this.duration);
|
||||
result = 31 * result + Objects.hashCode(this.durationUnit);
|
||||
result = 31 * result + Objects.hashCode(this.dataSize);
|
||||
result = 31 * result + Objects.hashCode(this.dataSizeUnit);
|
||||
result = 31 * result + Objects.hashCode(this.nullable);
|
||||
result = 31 * result + Objects.hashCode(this.nullable2);
|
||||
result = 31 * result + Objects.hashCode(this.pair);
|
||||
result = 31 * result + Objects.hashCode(this.pair2);
|
||||
result = 31 * result + Objects.hashCode(this.coll);
|
||||
result = 31 * result + Objects.hashCode(this.coll2);
|
||||
result = 31 * result + Objects.hashCode(this.list);
|
||||
result = 31 * result + Objects.hashCode(this.list2);
|
||||
result = 31 * result + Objects.hashCode(this.set);
|
||||
result = 31 * result + Objects.hashCode(this.set2);
|
||||
result = 31 * result + Objects.hashCode(this.map);
|
||||
result = 31 * result + Objects.hashCode(this.map2);
|
||||
result = 31 * result + Objects.hashCode(this.container);
|
||||
result = 31 * result + Objects.hashCode(this.container2);
|
||||
result = 31 * result + Objects.hashCode(this.other);
|
||||
result = 31 * result + Objects.hashCode(this.regex);
|
||||
result = 31 * result + Objects.hashCode(this.any);
|
||||
result = 31 * result + Objects.hashCode(this.nonNull);
|
||||
result = 31 * result + Objects.hashCode(this._enum);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(1400);
|
||||
builder.append(PropertyTypes.class.getSimpleName()).append(" {");
|
||||
appendProperty(builder, "_boolean", this._boolean);
|
||||
appendProperty(builder, "_int", this._int);
|
||||
appendProperty(builder, "_float", this._float);
|
||||
appendProperty(builder, "string", this.string);
|
||||
appendProperty(builder, "duration", this.duration);
|
||||
appendProperty(builder, "durationUnit", this.durationUnit);
|
||||
appendProperty(builder, "dataSize", this.dataSize);
|
||||
appendProperty(builder, "dataSizeUnit", this.dataSizeUnit);
|
||||
appendProperty(builder, "nullable", this.nullable);
|
||||
appendProperty(builder, "nullable2", this.nullable2);
|
||||
appendProperty(builder, "pair", this.pair);
|
||||
appendProperty(builder, "pair2", this.pair2);
|
||||
appendProperty(builder, "coll", this.coll);
|
||||
appendProperty(builder, "coll2", this.coll2);
|
||||
appendProperty(builder, "list", this.list);
|
||||
appendProperty(builder, "list2", this.list2);
|
||||
appendProperty(builder, "set", this.set);
|
||||
appendProperty(builder, "set2", this.set2);
|
||||
appendProperty(builder, "map", this.map);
|
||||
appendProperty(builder, "map2", this.map2);
|
||||
appendProperty(builder, "container", this.container);
|
||||
appendProperty(builder, "container2", this.container2);
|
||||
appendProperty(builder, "other", this.other);
|
||||
appendProperty(builder, "regex", this.regex);
|
||||
appendProperty(builder, "any", this.any);
|
||||
appendProperty(builder, "nonNull", this.nonNull);
|
||||
appendProperty(builder, "_enum", this._enum);
|
||||
builder.append("\n}");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Other {
|
||||
public final @NonNull String name;
|
||||
|
||||
public Other(@Named("name") @NonNull String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Other withName(@NonNull String name) {
|
||||
return new Other(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (this.getClass() != obj.getClass()) return false;
|
||||
Other other = (Other) obj;
|
||||
if (!Objects.equals(this.name, other.name)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + Objects.hashCode(this.name);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(100);
|
||||
builder.append(Other.class.getSimpleName()).append(" {");
|
||||
appendProperty(builder, "name", this.name);
|
||||
builder.append("\n}");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public enum Direction {
|
||||
NORTH("north"),
|
||||
|
||||
EAST("east"),
|
||||
|
||||
SOUTH("south"),
|
||||
|
||||
WEST("west");
|
||||
|
||||
private String value;
|
||||
|
||||
private Direction(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user