diff --git a/README.md b/README.md index 04b55ed5ee..1b04b4a7fb 100644 --- a/README.md +++ b/README.md @@ -27,26 +27,32 @@ fun main(vararg args: String) { And this is the code to generate it with KotlinPoet: ```kotlin -val greeterClass = ClassName("", "Greeter") -val file = FileSpec.builder("", "HelloWorld") - .addType(TypeSpec.classBuilder("Greeter") - .primaryConstructor(FunSpec.constructorBuilder() - .addParameter("name", String::class) - .build()) - .addProperty(PropertySpec.builder("name", String::class) - .initializer("name") - .build()) - .addFunction(FunSpec.builder("greet") - .addStatement("println(%S)", "Hello, \$name") - .build()) - .build()) - .addFunction(FunSpec.builder("main") - .addParameter("args", String::class, VARARG) - .addStatement("%T(args[0]).greet()", greeterClass) - .build()) +fun main(args: Array) { + + val greetFunSpec = FunSpec.builder("greet") + .addStatement("println(%S)", "Hello, \$name") + .build() + + val greeterTypeSpec = TypeSpec.of( + className = "Greeter", + constructorProperties = listOf(PropertySpec.property("name", typeName())), + functions = listOf(greetFunSpec) + ) + + val mainFunSpec = FunSpec.builder("main") + .addParameter("args", String::class, KModifier.VARARG) + .addStatement("%T(args[0]).greet()", ClassName("", "Greeter")) .build() -file.writeTo(System.out) + val file = FileSpec.of( + packageName = "", + fileName = "HelloWorld", + types = listOf(greeterTypeSpec), + functions = listOf(mainFunSpec) + ) + file.writeTo(System.out) +} + ``` The [KDoc][kdoc] catalogs the complete KotlinPoet API, which is inspired by [JavaPoet][javapoet]. @@ -135,16 +141,22 @@ returns its own name: ```kotlin fun main(args: Array) { - val helloWorld = TypeSpec.classBuilder("HelloWorld") - .addFunction(whatsMyNameYo("slimShady")) - .addFunction(whatsMyNameYo("eminem")) - .addFunction(whatsMyNameYo("marshallMathers")) - .build() - - val kotlinFile = FileSpec.builder("com.example.helloworld", "HelloWorld") - .addType(helloWorld) - .build() - + + val helloWorld = TypeSpec.of( + className = "HelloWorld", + functions = listOf( + whatsMyNameYo("slimShady"), + whatsMyNameYo("eminem"), + whatsMyNameYo("marshallMathers") + ) + ) + + val kotlinFile = FileSpec.of( + packageName = "com.example.helloworld", + fileName = "HelloWorld", + types = listOf(helloWorld) + ) + kotlinFile.writeTo(System.out) } @@ -279,15 +291,11 @@ and you'll have to add the import statement manually to get those extensions. KotlinPoet also supports Kotlin's nullable types. Simply add the extension `asNullable()` to any type to make it nullable. For example: ```kotlin -val java = PropertySpec.varBuilder("java", String::class.asTypeName().asNullable()) - .addModifiers(KModifier.PRIVATE) - .initializer("null") - .build() - -val helloWorld = TypeSpec.classBuilder("HelloWorld") - .addProperty(java) - .addProperty("kotlin", String::class, KModifier.PRIVATE) - .build() +val java = PropertySpec.nullableVarProperty("java", typeName(), + listOf(KModifier.PRIVATE), CodeBlock.of("null")) + +val kotlin = PropertySpec.property("kotlin", typeName(), + listOf(KModifier.PRIVATE)) ``` generates: @@ -466,6 +474,8 @@ FunSpec.builder("add") .build() ``` + + ### Constructors `FunSpec` is a slight misnomer; it can also be used for constructors: @@ -494,6 +504,27 @@ class HelloWorld { } ``` + +Here is a simpler way to produce + +```kotlin +data class User(val greeting: String = "", val id: Int = -1) +``` + + +```kotlin +val greeting = PropertySpec.property("greeting", typeName()) + +val id = PropertySpec.property("id", typeName()) + +val userTypeSpec = TypeSpec.of( + className = "User", + constructorProperties = listOf(greeting, id), + modifiers = listOf(KModifier.DATA) +) +``` + + For the most part, constructors work just like methods. When emitting code, KotlinPoet will place constructors before methods in the output file. diff --git a/src/main/java/com/squareup/kotlinpoet/BuilderLess.kt b/src/main/java/com/squareup/kotlinpoet/BuilderLess.kt new file mode 100644 index 0000000000..fecc2e4abf --- /dev/null +++ b/src/main/java/com/squareup/kotlinpoet/BuilderLess.kt @@ -0,0 +1,155 @@ +package com.squareup.kotlinpoet + +import kotlin.reflect.KClass + +fun main(args: Array) { + + val greetFunSpec = FunSpec.builder("greet") + .addStatement("println(%S)", "Hello, \$name") + .build() + + val greeterTypeSpec = TypeSpec.of( + className = "Greeter", + constructorProperties = listOf(PropertySpec.property("name", typeName())), + functions = listOf(greetFunSpec) + ) + + val mainFunSpec = FunSpec.builder("main") + .addParameter("args", String::class, KModifier.VARARG) + .addStatement("%T(args[0]).greet()", ClassName("", "Greeter")) + .build() + + val file = FileSpec.of( + packageName = "", + fileName = "HelloWorld", + types = listOf(greeterTypeSpec), + functions = listOf(mainFunSpec) + ) + file.writeTo(System.out) +} + +inline fun typeName(clazz: Class = T::class.java): ClassName + = T::class.asTypeName() + + +fun PropertySpec.Companion.property( + name: String, + type: ClassName, + modifiers: List = emptyList(), + initializer: CodeBlock? = null, + configuration: PropertySpec.Builder.() -> Unit = {} +): PropertySpec = PropertySpec.builder(name, type, *modifiers.toTypedArray()) + .apply { + if (initializer != null) initializer(initializer) + configuration() + } + .build() + +fun PropertySpec.Companion.varProperty( + name: String, + type: ClassName, + modifiers: List = emptyList(), + initializer: CodeBlock? = null, + configuration: PropertySpec.Builder.() -> Unit = {} +) : PropertySpec = PropertySpec.varBuilder(name, type, *modifiers.toTypedArray()) + .apply { + if (initializer != null) initializer(initializer) + configuration() + }.build() + +fun PropertySpec.Companion.nullableProperty( + name: String, + type: ClassName, + modifiers: List = emptyList(), + initializer: CodeBlock? = null, + configuration: PropertySpec.Builder.() -> Unit = {} +) : PropertySpec = PropertySpec.property(name, type.asNullable(), modifiers, initializer) + +fun PropertySpec.Companion.nullableVarProperty( + name: String, + type: ClassName, + modifiers: List = emptyList(), + initializer: CodeBlock? = null, + configuration: PropertySpec.Builder.() -> Unit = {} +): PropertySpec = PropertySpec.nullableProperty(name, type.asNullable(), modifiers, initializer, configuration) + + +fun TypeSpec.Companion.of( + className: String, + constructorProperties: List = emptyList(), + modifiers: List = emptyList(), + types: Iterable = emptyList(), + primaryConstructor: FunSpec? = null, + functions: Iterable = emptyList(), + annotations: Annotations = Annotations(), + properties: List = emptyList(), + configuration: TypeSpec.Builder.() -> Unit = {} +): TypeSpec { + return TypeSpec.classBuilder(className) + .apply { + require(constructorProperties.isEmpty() || primaryConstructor == null) + if (primaryConstructor != null) { + primaryConstructor(primaryConstructor) + } + if (constructorProperties.isNotEmpty()) { + val parameterSpecs = mutableListOf() + for (cp in constructorProperties) { + parameterSpecs += ParameterSpec.builder(cp.name, cp.type, *cp.modifiers.toTypedArray()) + .apply { + if (cp.initializer != null) defaultValue(cp.initializer) + } + .build() + addProperty(cp.toBuilder().initializer(cp.name).build()) + } + + primaryConstructor( + FunSpec.constructorBuilder() + .addParameters(parameterSpecs) + .build()) + } + for (t in types) addType(t) + for (f in functions) addFunction(f) + for (a in annotations.specs) addAnnotation(a) + for (a in annotations.classes) addAnnotation(a) + for (a in annotations.classNames) addAnnotation(a) + for (a in annotations.kclasses) addAnnotation(a) + addModifiers(*modifiers.toTypedArray()) + addProperties(properties) + + configuration() + } + .build() +} + +fun FileSpec.Companion.of( + packageName: String, + fileName: String, + types: Iterable = emptyList(), + functions: Iterable = emptyList(), + annotations: Annotations = Annotations(), + properties: List = emptyList(), + comment: String = "", + typeAliases: Iterable = emptyList(), + configuration: FileSpec.Builder.() -> Unit = {} +): FileSpec = FileSpec.builder(packageName, fileName) + .apply { + for (t in types) addType(t) + for (f in functions) addFunction(f) + for (a in annotations.specs) addAnnotation(a) + for (a in annotations.classes) addAnnotation(a) + for (a in annotations.classNames) addAnnotation(a) + for (a in annotations.kclasses) addAnnotation(a) + for (p in properties) addProperty(p) + if (comment.isNotEmpty()) addComment(comment) + for (ty in typeAliases) addTypeAlias(ty) + configuration() + } + .build() + + +data class Annotations( + val specs: Iterable = emptyList(), + val classes: Iterable> = emptyList(), + val kclasses: Iterable> = emptyList(), + val classNames: Iterable = emptyList() +)