mirror of
https://github.com/apple/pkl.git
synced 2026-04-20 15:31:28 +02:00
Allow renaming Java/Kotlin classes/packages during code generation (#499)
Adds a `rename` field to the Java/Kotlin code generators that allows renaming packages and classes during codegen. * Add `--rename` flag to CLIs * Add `rename` property to Gradle API
This commit is contained in:
73
pkl-commons/src/main/kotlin/org/pkl/commons/NameMapper.kt
Normal file
73
pkl-commons/src/main/kotlin/org/pkl/commons/NameMapper.kt
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.commons
|
||||
|
||||
/**
|
||||
* A helper class for translating names of Pkl modules to different names of classes and/or objects
|
||||
* in the target language of a code generation execution.
|
||||
*
|
||||
* The `mapping` parameter is expected to contain valid prefixes of Pkl module names, with an
|
||||
* optional dot at the end, and values should be valid class names in the language for which code
|
||||
* generation is performed.
|
||||
*
|
||||
* If the rename patterns do not explicitly rename the class, the class name is capitalized.
|
||||
*
|
||||
* When computing the appropriate target name, the longest matching prefix is used.
|
||||
*
|
||||
* Prefix replacements are literal, and therefore dots are important. When renaming packages, in
|
||||
* most cases, you must ensure that you have an ending dot on both sides of a mapping (except for
|
||||
* the empty mapping, if you use it), otherwise you may get unexpected results:
|
||||
* ```kotlin
|
||||
* val mapper = NameMapper(
|
||||
* mapOf(
|
||||
* "com.foo." to "x", // Dot on the left only
|
||||
* "org.bar" to "y.", // Dot on the right only
|
||||
* "net.baz" to "z" // No dots
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* assertThat(mapper.map("com.foo.bar")).isEqualTo("" to "xbar") // Target prefix merged into the suffix
|
||||
* assertThat(mapper.map("org.bar.baz")).isEqualTo("y." to "Baz") // Double dot, invalid package name
|
||||
* assertThat(mapper.map("net.baz.qux")).isEqualTo("z" to "Qux") // Looks okay, but...
|
||||
* assertThat(mapper.map("net.bazqux")).isEqualTo("" to "zqux") // ...may cut the package name in the middle.
|
||||
* ```
|
||||
*/
|
||||
class NameMapper(mapping: Map<String, String>) {
|
||||
private val sortedMapping = mapping.toList().sortedBy { -it.first.length }
|
||||
|
||||
private fun doMap(sourceName: String): Pair<String, Boolean> {
|
||||
for ((sourcePrefix, targetPrefix) in sortedMapping) {
|
||||
if (sourceName.startsWith(sourcePrefix)) {
|
||||
val rest = sourceName.substring(sourcePrefix.length)
|
||||
val mapped = targetPrefix + rest
|
||||
val wasClassRenamed =
|
||||
!targetPrefix.endsWith('.') && (sourcePrefix.length - 1) >= sourceName.lastIndexOf('.')
|
||||
return mapped to wasClassRenamed
|
||||
}
|
||||
}
|
||||
return sourceName to false
|
||||
}
|
||||
|
||||
fun map(sourceName: String): Pair<String, String> {
|
||||
val (mappedName, wasClassRenamed) = doMap(sourceName)
|
||||
val packageName = mappedName.substringBeforeLast(".", "")
|
||||
val mappedClassName = mappedName.substringAfterLast(".")
|
||||
val className =
|
||||
if (wasClassRenamed) mappedClassName
|
||||
else mappedClassName.replaceFirstChar { it.titlecaseChar() }
|
||||
return packageName to className
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.commons
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class NameMapperTest {
|
||||
@Test
|
||||
fun `empty prefixes everything`() {
|
||||
val mapper = NameMapper(mapOf("" to "bar."))
|
||||
assertThat(mapper.map("foo.bar.Baz")).isEqualTo("bar.foo.bar" to "Baz")
|
||||
assertThat(mapper.map("Baz")).isEqualTo("bar" to "Baz")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `longest prefix wins`() {
|
||||
val mapper = NameMapper(mapOf("bar." to "com.bar.", "bar.baz." to "foo.bar."))
|
||||
assertThat(mapper.map("bar.baz.Buzzy")).isEqualTo("foo.bar" to "Buzzy")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `implicit uppercase classname`() {
|
||||
val mapper = NameMapper(mapOf("foo." to "bar."))
|
||||
assertThat(mapper.map("foo.bar.baz")).isEqualTo("bar.bar" to "Baz")
|
||||
assertThat(mapper.map("foo.bar")).isEqualTo("bar" to "Bar")
|
||||
assertThat(mapper.map("baz")).isEqualTo("" to "Baz")
|
||||
assertThat(mapper.map("baz")).isEqualTo("" to "Baz")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no implicit uppercased classname if explicitly renamed`() {
|
||||
val mapper =
|
||||
NameMapper(
|
||||
mapOf(
|
||||
"foo.bar" to "bar.bar",
|
||||
"foo.c" to "foo.z",
|
||||
"com.foo." to "x",
|
||||
)
|
||||
)
|
||||
assertThat(mapper.map("foo.bar")).isEqualTo("bar" to "bar")
|
||||
assertThat(mapper.map("foo.bar")).isEqualTo("bar" to "bar")
|
||||
assertThat(mapper.map("foo.cow")).isEqualTo("foo" to "zow")
|
||||
assertThat(mapper.map("com.foo.bar")).isEqualTo("" to "xbar")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user