Skip to content

Commit

Permalink
Merge pull request #1029 from Kotlin/rename-to-camelcase-2
Browse files Browse the repository at this point in the history
Rename to camelCase support in compiler plugin
  • Loading branch information
Jolanrensen authored Jan 28, 2025
2 parents b67c928 + cd5888c commit b4739b9
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 52 deletions.
1 change: 1 addition & 0 deletions core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -7496,6 +7496,7 @@ public final class org/jetbrains/kotlinx/dataframe/api/RenameKt {
public static final fun rename (Lorg/jetbrains/kotlinx/dataframe/columns/ColumnReference;Lkotlin/reflect/KProperty;)Lorg/jetbrains/kotlinx/dataframe/columns/ColumnReference;
public static final fun rename (Lorg/jetbrains/kotlinx/dataframe/columns/ColumnReference;Lorg/jetbrains/kotlinx/dataframe/columns/ColumnAccessor;)Lorg/jetbrains/kotlinx/dataframe/columns/ColumnReference;
public static final fun renameToCamelCase (Lorg/jetbrains/kotlinx/dataframe/DataFrame;)Lorg/jetbrains/kotlinx/dataframe/DataFrame;
public static final fun renameToCamelCase (Lorg/jetbrains/kotlinx/dataframe/columns/ColumnReference;)Lorg/jetbrains/kotlinx/dataframe/columns/ColumnReference;
public static final fun toCamelCase (Lorg/jetbrains/kotlinx/dataframe/api/RenameClause;)Lorg/jetbrains/kotlinx/dataframe/DataFrame;
}

Expand Down
25 changes: 19 additions & 6 deletions core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/rename.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public class RenameClause<T, C>(internal val df: DataFrame<T>, internal val colu
* and converting the first char to lowercase.
* Even [DataFrames][DataFrame] inside [FrameColumns][FrameColumn] are traversed recursively.
*/
@Refine
@Interpretable("RenameToCamelCase")
public fun <T> DataFrame<T>.renameToCamelCase(): DataFrame<T> =
// recursively rename all columns written with delimiters or starting with a capital to camel case
rename {
Expand Down Expand Up @@ -83,17 +85,28 @@ public fun <T, C> RenameClause<T, C>.into(transform: (ColumnWithPath<C>) -> Stri
* Renames the selected columns to `camelCase` by replacing all [delimiters][DELIMITERS_REGEX]
* and converting the first char to lowercase.
*/
public fun <T, C> RenameClause<T, C>.toCamelCase(): DataFrame<T> =
into {
it.name()
.toCamelCaseByDelimiters(DELIMITERS_REGEX)
.replaceFirstChar { it.lowercaseChar() }
}
@Refine
@Interpretable("RenameToCamelCaseClause")
public fun <T, C> RenameClause<T, C>.toCamelCase(): DataFrame<T> = into { it.renameToCamelCase().name() }

// endregion

// region DataColumn

/**
* ## Rename to camelCase
*
* Renames this column to `camelCase` by replacing all [delimiters][DELIMITERS_REGEX]
* and converting the first char to lowercase.
*/
@Suppress("UNCHECKED_CAST")
public fun <T, C : ColumnReference<T>> C.renameToCamelCase(): C =
rename(
this.name()
.toCamelCaseByDelimiters(DELIMITERS_REGEX)
.replaceFirstChar { it.lowercaseChar() },
) as C

@Suppress("UNCHECKED_CAST")
public fun <T, C : ColumnReference<T>> C.rename(column: KProperty<T>): C = rename(column.columnName) as C

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.io.File;
Expand All @@ -16,26 +15,26 @@
@TestMetadata("testData/box")
@TestDataPath("$PROJECT_ROOT")
public class ExplainerBlackBoxCodegenTestGenerated extends AbstractExplainerBlackBoxCodegenTest {
@Test
public void testAllFilesPresentInBox() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("testData/box"), Pattern.compile("^(.+)\\.kt$"), null, true);
}

@Test
@TestMetadata("any.kt")
public void testAny() throws Exception {
runTest("testData/box/any.kt");
}

@Test
@TestMetadata("df.kt")
public void testDf() throws Exception {
runTest("testData/box/df.kt");
}

@Test
@TestMetadata("test.kt")
public void testTest() throws Exception {
runTest("testData/box/test.kt");
}
@Test
public void testAllFilesPresentInBox() {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("testData/box"), Pattern.compile("^(.+)\\.kt$"), null, true);
}

@Test
@TestMetadata("any.kt")
public void testAny() {
runTest("testData/box/any.kt");
}

@Test
@TestMetadata("df.kt")
public void testDf() {
runTest("testData/box/df.kt");
}

@Test
@TestMetadata("test.kt")
public void testTest() {
runTest("testData/box/test.kt");
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
package org.jetbrains.kotlinx.dataframe.plugin.impl.api

import org.jetbrains.kotlinx.dataframe.api.rename
import org.jetbrains.kotlinx.dataframe.api.renameToCamelCase
import org.jetbrains.kotlinx.dataframe.api.toCamelCase
import org.jetbrains.kotlinx.dataframe.columns.toColumnSet
import org.jetbrains.kotlinx.dataframe.plugin.impl.AbstractInterpreter
import org.jetbrains.kotlinx.dataframe.plugin.impl.AbstractSchemaModificationInterpreter
import org.jetbrains.kotlinx.dataframe.plugin.impl.Arguments
import org.jetbrains.kotlinx.dataframe.plugin.impl.PluginDataFrameSchema
import org.jetbrains.kotlinx.dataframe.plugin.impl.SimpleCol
import org.jetbrains.kotlinx.dataframe.plugin.impl.SimpleDataColumn
import org.jetbrains.kotlinx.dataframe.plugin.impl.SimpleColumnGroup
import org.jetbrains.kotlinx.dataframe.plugin.impl.SimpleDataColumn
import org.jetbrains.kotlinx.dataframe.plugin.impl.SimpleFrameColumn
import org.jetbrains.kotlinx.dataframe.plugin.impl.asDataFrame
import org.jetbrains.kotlinx.dataframe.plugin.impl.dataFrame
import org.jetbrains.kotlinx.dataframe.plugin.impl.toPluginDataFrameSchema

class Rename : AbstractInterpreter<RenameClauseApproximation>() {
private val Arguments.receiver by dataFrame()
private val Arguments.columns: ColumnsResolver by arg()
override fun Arguments.interpret(): RenameClauseApproximation {
return RenameClauseApproximation(receiver, columns)
}

override fun Arguments.interpret(): RenameClauseApproximation = RenameClauseApproximation(receiver, columns)
}

class RenameClauseApproximation(val schema: PluginDataFrameSchema, val columns: ColumnsResolver)
Expand All @@ -28,55 +33,87 @@ class RenameInto : AbstractSchemaModificationInterpreter() {
val columns = receiver.columns.resolve(receiver.schema)
require(columns.size == newNames.size)
var i = 0
return receiver.schema.map(columns.mapTo(mutableSetOf()) { it.path.path }, nextName = { newNames[i].also { i += 1 } })
return receiver.schema.map(
selected = columns.mapTo(mutableSetOf()) { it.path.path },
nextName = { newNames[i].also { i += 1 } },
)
}
}

internal fun PluginDataFrameSchema.map(selected: ColumnsSet, nextName: () -> String): PluginDataFrameSchema {
return PluginDataFrameSchema(
f(columns(), nextName, selected, emptyList())
internal fun PluginDataFrameSchema.map(selected: ColumnsSet, nextName: () -> String): PluginDataFrameSchema =
PluginDataFrameSchema(
f(columns(), nextName, selected, emptyList()),
)
}

internal fun f(columns: List<SimpleCol>, nextName: () -> String, selected: ColumnsSet, path: List<String>): List<SimpleCol> {
return columns.map {
internal fun f(
columns: List<SimpleCol>,
nextName: () -> String,
selected: ColumnsSet,
path: List<String>,
): List<SimpleCol> =
columns.map {
val fullPath = path + listOf(it.name)
when (it) {
is SimpleColumnGroup -> {
val group = if (fullPath in selected) {
it.rename(nextName())
} else {
} else {
it
}
group.map(selected, fullPath, nextName)
}

is SimpleFrameColumn -> {
val frame = if (fullPath in selected) {
it.rename(nextName())
} else {
} else {
it
}
frame.map(selected, fullPath, nextName)
}

is SimpleDataColumn -> if (fullPath in selected) {
it.rename(nextName())
} else {
it
}
}
}
}

internal fun SimpleColumnGroup.map(selected: ColumnsSet, path: List<String>, nextName: () -> String): SimpleColumnGroup {
return SimpleColumnGroup(
name,
f(columns(), nextName, selected, path)
internal fun SimpleColumnGroup.map(
selected: ColumnsSet,
path: List<String>,
nextName: () -> String,
): SimpleColumnGroup =
SimpleColumnGroup(
name = name,
columns = f(columns(), nextName, selected, path),
)
}

internal fun SimpleFrameColumn.map(selected: ColumnsSet, path: List<String>, nextName: () -> String): SimpleFrameColumn {
return SimpleFrameColumn(
name,
f(columns(), nextName, selected, path)
internal fun SimpleFrameColumn.map(
selected: ColumnsSet,
path: List<String>,
nextName: () -> String,
): SimpleFrameColumn =
SimpleFrameColumn(
name = name,
columns = f(columns(), nextName, selected, path),
)

class RenameToCamelCase : AbstractSchemaModificationInterpreter() {
val Arguments.receiver by dataFrame()

override fun Arguments.interpret(): PluginDataFrameSchema =
receiver.asDataFrame().renameToCamelCase().toPluginDataFrameSchema()
}

class RenameToCamelCaseClause : AbstractSchemaModificationInterpreter() {
val Arguments.receiver: RenameClauseApproximation by arg()

override fun Arguments.interpret(): PluginDataFrameSchema {
val selectedPaths = receiver.columns.resolve(receiver.schema).map { it.path }
return receiver.schema.asDataFrame()
.rename { selectedPaths.toColumnSet() }.toCamelCase()
.toPluginDataFrameSchema()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ import org.jetbrains.kotlinx.dataframe.plugin.impl.api.TrimMargin
import org.jetbrains.kotlinx.dataframe.plugin.impl.api.Update0
import org.jetbrains.kotlinx.dataframe.plugin.impl.api.UpdateWith0
import org.jetbrains.kotlinx.dataframe.plugin.impl.api.ValueCounts
import org.jetbrains.kotlinx.dataframe.plugin.impl.api.RenameToCamelCase
import org.jetbrains.kotlinx.dataframe.plugin.impl.api.RenameToCamelCaseClause
import org.jetbrains.kotlinx.dataframe.plugin.utils.Names

internal fun FirFunctionCall.loadInterpreter(session: FirSession): Interpreter<*>? {
Expand Down Expand Up @@ -251,6 +253,8 @@ internal inline fun <reified T> String.load(): T {
"Aggregate" -> Aggregate()
"DataFrameOf3" -> DataFrameOf3()
"ValueCounts" -> ValueCounts()
"RenameToCamelCase" -> RenameToCamelCase()
"RenameToCamelCaseClause" -> RenameToCamelCaseClause()
else -> error("$this")
} as T
}
49 changes: 49 additions & 0 deletions plugins/kotlin-dataframe/testData/box/renameToCamelCase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import org.jetbrains.kotlinx.dataframe.*
import org.jetbrains.kotlinx.dataframe.annotations.*
import org.jetbrains.kotlinx.dataframe.api.*
import org.jetbrains.kotlinx.dataframe.io.*

fun box(): String {
// Test DataFrame.renameToCamelCase()
val df = dataFrameOf("first_name", "last_name", "user_age")(
"John", "Doe", 30
)
val renamed = df.renameToCamelCase()

// Verify extension properties
if (renamed.firstName[0] != "John") return "DataFrame.renameToCamelCase: 'firstName' failed"
if (renamed.lastName[0] != "Doe") return "DataFrame.renameToCamelCase: 'lastName' failed"
if (renamed.userAge[0] != 30) return "DataFrame.renameToCamelCase: 'userAge' failed"

// Test RenameClause.toCamelCase()
val df2 = dataFrameOf("first_name", "last_name", "user_age")(
"Jane", "Smith", 25
)
val renamed2 = df2.rename { first_name and last_name and user_age }.toCamelCase()

// Verify extension properties for RenameClause.toCamelCase
if (renamed2.firstName[0] != "Jane") return "RenameClause.toCamelCase: 'firstName' failed"
if (renamed2.lastName[0] != "Smith") return "RenameClause.toCamelCase: 'lastName' failed"
if (renamed2.userAge[0] != 25) return "RenameClause.toCamelCase: 'userAge' failed"

// Test nested DataFrame with both methods
val nestedDf = dataFrameOf("user_info")(
dataFrameOf("first_name", "last_name")(
"John", "Doe"
)
)

// Test DataFrame.renameToCamelCase with nested
val renamedNested = nestedDf.renameToCamelCase()
val nestedData = renamedNested.userInfo[0]
if (nestedData.firstName[0] != "John") return "Nested DataFrame.renameToCamelCase: 'firstName' failed"
if (nestedData.lastName[0] != "Doe") return "Nested DataFrame.renameToCamelCase: 'lastName' failed"

// Test RenameClause.toCamelCase with nested, only uses selection
val renamedNested2 = nestedDf.rename { all() }.toCamelCase()
val nestedData2 = renamedNested2.userInfo[0]
if (nestedData2.first_name[0] != "John") return "Nested RenameClause.toCamelCase: 'first_name' failed"
if (nestedData2.last_name[0] != "Doe") return "Nested RenameClause.toCamelCase: 'last_name' failed"

return "OK"
}
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,12 @@ public void testRename() {
runTest("testData/box/rename.kt");
}

@Test
@TestMetadata("renameToCamelCase.kt")
public void testRenameToCamelCase() {
runTest("testData/box/renameToCamelCase.kt");
}

@Test
@TestMetadata("Schema.kt")
public void testSchema() {
Expand Down

0 comments on commit b4739b9

Please sign in to comment.