Skip to content

Commit 52f28a1

Browse files
committed
Add generateDataClasses for df.toListOf<generated classes> workflow
1 parent 4d31ef7 commit 52f28a1

File tree

4 files changed

+88
-16
lines changed

4 files changed

+88
-16
lines changed

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/codeGen/CodeGenerator.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public interface CodeGenerator : ExtensionsCodeGenerator {
3636
knownMarkers: Iterable<Marker> = emptyList(),
3737
readDfMethod: DefaultReadDfMethod? = null,
3838
fieldNameNormalizer: NameNormalizer = NameNormalizer.id(),
39+
asDataClass: Boolean = false
3940
): CodeGenResult
4041

4142
public fun generate(

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/codeGen/generateCode.kt

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ public inline fun <reified T> DataFrame<T>.generateCode(
88
fields: Boolean = true,
99
extensionProperties: Boolean = true,
1010
): String {
11-
val name = if (T::class.isAbstract) {
12-
T::class.simpleName!!
13-
} else "DataEntry"
11+
val name = markerName<T>()
1412
return generateCode(name, fields, extensionProperties)
1513
}
1614

@@ -36,6 +34,30 @@ public inline fun <reified T> DataFrame<T>.generateInterfaces(): String = genera
3634
extensionProperties = false
3735
)
3836

37+
public inline fun <reified T> DataFrame<T>.generateDataClasses(
38+
markerName: String? = null,
39+
extensionProperties: Boolean = true,
40+
visibility: MarkerVisibility = MarkerVisibility.IMPLICIT_PUBLIC,
41+
useFqNames: Boolean = false
42+
): String {
43+
val name = markerName ?: markerName<T>()
44+
val codeGen = CodeGenerator.create(useFqNames)
45+
return codeGen.generate(
46+
schema = schema(),
47+
name = name,
48+
fields = true,
49+
extensionProperties = extensionProperties,
50+
isOpen = false,
51+
visibility = visibility,
52+
asDataClass = true
53+
).code.declarations
54+
}
55+
56+
@PublishedApi
57+
internal inline fun <reified T> markerName(): String = if (T::class.isAbstract) {
58+
T::class.simpleName!!
59+
} else "DataEntry"
60+
3961
public fun <T> DataFrame<T>.generateInterfaces(markerName: String): String = generateCode(
4062
markerName = markerName,
4163
fields = true,

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/codeGen/CodeGeneratorImpl.kt

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -448,12 +448,18 @@ internal class CodeGeneratorImpl(typeRendering: TypeRenderingStrategy = FullyQua
448448
knownMarkers: Iterable<Marker>,
449449
readDfMethod: DefaultReadDfMethod?,
450450
fieldNameNormalizer: NameNormalizer,
451+
asDataClass: Boolean
451452
): CodeGenResult {
452-
val context = SchemaProcessor.create(name, knownMarkers, fieldNameNormalizer)
453+
val context = SchemaProcessor.create(name, if (asDataClass) emptyList() else knownMarkers, fieldNameNormalizer)
453454
val marker = context.process(schema, isOpen, visibility)
454455
val declarations = mutableListOf<Code>()
455456
context.generatedMarkers.forEach { itMarker ->
456-
declarations.add(generateInterface(itMarker, fields, readDfMethod.takeIf { marker == itMarker }))
457+
val declaration = if (asDataClass) {
458+
generateClasses(itMarker)
459+
} else {
460+
generateInterface(itMarker, fields, readDfMethod.takeIf { marker == itMarker })
461+
}
462+
declarations.add(declaration)
457463
if (extensionProperties) {
458464
declarations.add(generateExtensionProperties(itMarker, withNullable = false))
459465
}
@@ -479,17 +485,7 @@ internal class CodeGeneratorImpl(typeRendering: TypeRenderingStrategy = FullyQua
479485
.joinToString() else ""
480486
val resultDeclarations = mutableListOf<String>()
481487

482-
val fieldsDeclaration = if (fields) marker.fields.map {
483-
val override = if (it.overrides) "override " else ""
484-
val columnNameAnnotation = if (it.columnName != it.fieldName.quotedIfNeeded) {
485-
" @ColumnName(\"${renderStringLiteral(it.columnName)}\")\n"
486-
} else {
487-
""
488-
}
489-
490-
val fieldType = it.renderFieldType()
491-
"$columnNameAnnotation ${propertyVisibility}${override}val ${it.fieldName.quotedIfNeeded}: $fieldType"
492-
}.join() else ""
488+
val fieldsDeclaration = if (fields) renderFields(marker, propertyVisibility).join() else ""
493489

494490
val readDfMethodDeclaration = readDfMethod?.toDeclaration(marker, propertyVisibility)
495491

@@ -515,6 +511,36 @@ internal class CodeGeneratorImpl(typeRendering: TypeRenderingStrategy = FullyQua
515511
resultDeclarations.add(header + baseInterfacesDeclaration + body)
516512
return resultDeclarations.join()
517513
}
514+
515+
private fun generateClasses(marker: Marker): Code {
516+
val annotationName = DataSchema::class.simpleName
517+
518+
val visibility = renderTopLevelDeclarationVisibility(marker)
519+
val propertyVisibility = renderInternalDeclarationVisibility(marker)
520+
val header =
521+
"@$annotationName\n${visibility}data class ${marker.name}("
522+
523+
val fieldsDeclaration = renderFields(marker, propertyVisibility).joinToString(",\n")
524+
return buildString {
525+
appendLine(header)
526+
appendLine(fieldsDeclaration)
527+
append(")")
528+
}
529+
}
530+
531+
private fun renderFields(marker: Marker, propertyVisibility: String): List<String> {
532+
return marker.fields.map {
533+
val override = if (it.overrides) "override " else ""
534+
val columnNameAnnotation = if (it.columnName != it.fieldName.quotedIfNeeded) {
535+
" @ColumnName(\"${renderStringLiteral(it.columnName)}\")\n"
536+
} else {
537+
""
538+
}
539+
540+
val fieldType = it.renderFieldType()
541+
"$columnNameAnnotation ${propertyVisibility}${override}val ${it.fieldName.quotedIfNeeded}: $fieldType"
542+
}
543+
}
518544
}
519545

520546
public fun CodeWithConverter.toStandaloneSnippet(packageName: String, additionalImports: List<String>): String =

core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/codeGen/CodeGenerationTests.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import org.jetbrains.kotlinx.dataframe.DataColumn
1111
import org.jetbrains.kotlinx.dataframe.DataRow
1212
import org.jetbrains.kotlinx.dataframe.api.dataFrameOf
1313
import org.jetbrains.kotlinx.dataframe.api.dropNulls
14+
import org.jetbrains.kotlinx.dataframe.api.groupBy
1415
import org.jetbrains.kotlinx.dataframe.api.move
1516
import org.jetbrains.kotlinx.dataframe.api.schema
17+
import org.jetbrains.kotlinx.dataframe.api.toDataFrame
1618
import org.jetbrains.kotlinx.dataframe.api.under
1719
import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup
1820
import org.jetbrains.kotlinx.dataframe.impl.codeGen.ReplCodeGeneratorImpl
@@ -305,6 +307,27 @@ class CodeGenerationTests : BaseTest() {
305307
}
306308
}
307309

310+
@Test
311+
fun `check method generateDataClasses`() {
312+
val code = typed.groupBy { name }.toDataFrame().generateDataClasses(extensionProperties = false)
313+
314+
code shouldBe """
315+
@DataSchema
316+
data class Person1(
317+
val age: Int,
318+
val city: String?,
319+
val name: String,
320+
val weight: Int?
321+
)
322+
323+
@DataSchema
324+
data class Person(
325+
val group: List<Person1>,
326+
val name: String
327+
)
328+
""".trimIndent()
329+
}
330+
308331
@Test
309332
fun patterns() {
310333
"""^[\d]""".toRegex().matches("3fds")

0 commit comments

Comments
 (0)