[lombok] Strip 'is' prefix in boolean properties

This commit is contained in:
Andrey Zinovyev
2021-03-30 17:17:13 +03:00
committed by TeamCityServer
parent 8a2279de2b
commit f2c0b8c68d
10 changed files with 115 additions and 38 deletions
@@ -23,7 +23,7 @@ Features support:
- [ ] Copy annotations
- [ ] Strip defined prefixes - in config and @Accessors
- [ ] Skip generation with AccessLevel.NONE
- [ ] Strip 'is' prefix for boolean fields
- [x] Strip 'is' prefix for boolean fields
[~] [@With](https://projectlombok.org/features/With)
@@ -9,10 +9,7 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.lombok.utils.LombokNames
import org.jetbrains.kotlin.lombok.utils.getBooleanArgument
import org.jetbrains.kotlin.lombok.utils.getNonBlankStringArgument
import org.jetbrains.kotlin.lombok.utils.getVisibility
import org.jetbrains.kotlin.lombok.utils.*
import org.jetbrains.kotlin.name.FqName
/*
@@ -56,7 +53,12 @@ abstract class AnnotationAndConfigCompanion<T> {
}
data class Accessors(val fluent: Boolean = false, val chain: Boolean = false) {
data class Accessors(
val fluent: Boolean = false,
val chain: Boolean = false,
val noIsPrefix: Boolean = false,
val prefix: List<String> = emptyList()
) {
companion object : AnnotationAndConfigCompanion<Accessors>() {
override val annotationName: FqName = LombokNames.ACCESSORS
@@ -70,7 +72,11 @@ data class Accessors(val fluent: Boolean = false, val chain: Boolean = false) {
annotation?.getBooleanArgument("chain")
?: config.getBoolean("lombok.accessors.chain")
?: fluent
return Accessors(fluent, chain)
val noIsPrefix = config.getBoolean("lombok.getter.noIsPrefix") ?: false
val prefix = annotation?.getStringArrayArgument("prefix")
?: emptyList()
return Accessors(fluent, chain, noIsPrefix, prefix)
}
}
}
@@ -10,19 +10,11 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.lombok.config.*
import org.jetbrains.kotlin.lombok.utils.collectWithNotNull
import org.jetbrains.kotlin.lombok.utils.createFunction
import org.jetbrains.kotlin.lombok.utils.getJavaFields
import org.jetbrains.kotlin.lombok.utils.toPreparedBase
import org.jetbrains.kotlin.lombok.utils.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
import org.jetbrains.kotlin.types.typeUtil.isBoolean
class GetterProcessor(private val config: LombokConfig) : Processor {
private val noIsPrefix by lazy { config.getBooleanOrDefault("lombok.getter.noIsPrefix") }
override fun contribute(classDescriptor: ClassDescriptor, jClass: JavaClassImpl): Parts {
val globalAccessors = Accessors.get(classDescriptor, config)
val clGetter =
@@ -45,13 +37,13 @@ class GetterProcessor(private val config: LombokConfig) : Processor {
globalAccessors: Accessors
): SimpleFunctionDescriptor? {
val accessors = Accessors.getIfAnnotated(field, config) ?: globalAccessors
val propertyName = field.toPropertyName(accessors)
val functionName =
if (accessors.fluent) {
field.name.identifier
propertyName
} else {
val prefix = if (field.type.isPrimitiveBoolean() && !noIsPrefix) "is" else "get"
prefix + toPreparedBase(field.name.identifier)
val prefix = if (field.type.isPrimitiveBoolean() && !accessors.noIsPrefix) AccessorNames.IS else AccessorNames.GET
prefix + propertyName.capitalize()
}
return classDescriptor.createFunction(
Name.identifier(functionName),
@@ -61,6 +53,4 @@ class GetterProcessor(private val config: LombokConfig) : Processor {
)
}
private fun KotlinType.isPrimitiveBoolean(): Boolean = this is SimpleTypeMarker && isBoolean() //todo
}
@@ -38,13 +38,11 @@ class SetterProcessor(private val config: LombokConfig) : Processor {
globalAccessors: Accessors
): SimpleFunctionDescriptor? {
val accessors = Accessors.getIfAnnotated(field, config) ?: globalAccessors
val propertyName = field.toPropertyName(accessors)
val functionName =
if (accessors.fluent) {
field.name.identifier
} else {
"set" + toPreparedBase(field.name.identifier)
}
if (accessors.fluent) propertyName
else AccessorNames.SET + propertyName.capitalize()
val returnType = if (accessors.chain) classDescriptor.defaultType else classDescriptor.builtIns.unitType
@@ -33,7 +33,7 @@ class WithProcessor(private val config: LombokConfig) : Processor {
with: With
): SimpleFunctionDescriptor? {
val functionName = "with" + toPreparedBase(field.name.identifier)
val functionName = "with" + toPropertyNameCapitalized(field.name.identifier)
return classDescriptor.createFunction(
Name.identifier(functionName),
@@ -12,9 +12,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.java.JavaVisibilities
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.constants.BooleanValue
import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.constants.*
fun getVisibility(annotation: AnnotationDescriptor, field: String = "value"): DescriptorVisibility {
val value = annotation.getStringArgument(field) ?: "PUBLIC"
@@ -32,11 +30,19 @@ fun AnnotationDescriptor.getStringArgument(argumentName: String): String? {
val argument = allValueArguments[Name.identifier(argumentName)]
?: return null
return when (argument) {
is EnumValue -> argument.enumEntryName.identifier
is StringValue -> argument.value
else -> throw RuntimeException("Argument $argument is not supported")
}
return extractString(argument)
}
fun AnnotationDescriptor.getStringArrayArgument(argumentName: String): List<String>? {
val argument = allValueArguments[Name.identifier(argumentName)] ?: return emptyList()
if (argument !is ArrayValue) throw RuntimeException("Argument should be ArrayValue, got $argument")
return argument.value.map(::extractString)
}
private fun extractString(argument: ConstantValue<*>) = when (argument) {
is EnumValue -> argument.enumEntryName.identifier
is StringValue -> argument.value
else -> throw RuntimeException("Argument $argument is not supported")
}
fun AnnotationDescriptor.getNonBlankStringArgument(argumentName: String): String? =
@@ -15,6 +15,8 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaClassConstructorDescriptor
import org.jetbrains.kotlin.load.java.lazy.descriptors.isJavaField
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
import org.jetbrains.kotlin.types.typeUtil.isBoolean
data class ValueParameter(val name: Name, val type: KotlinType)
@@ -93,3 +95,5 @@ fun ClassDescriptor.getJavaFields(): List<PropertyDescriptor> =
.map {
this.unsubstitutedMemberScope.getContributedVariables(it, NoLookupLocation.FROM_SYNTHETIC_SCOPE).single()
}.filter { it.isJavaField }
fun KotlinType.isPrimitiveBoolean(): Boolean = this is SimpleTypeMarker && isBoolean()
@@ -5,6 +5,32 @@
package org.jetbrains.kotlin.lombok.utils
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.lombok.config.Accessors
fun toPreparedBase(name: String): String = name.capitalizeAsciiOnly()
object AccessorNames {
const val IS = "is"
const val GET = "get"
const val SET = "set"
}
fun PropertyDescriptor.toPropertyName(config: Accessors): String {
val isPrimitiveBoolean = type.isPrimitiveBoolean()
val prefixes = if (isPrimitiveBoolean) config.prefix + AccessorNames.IS else config.prefix
return toPropertyName(name.identifier, prefixes)
}
fun toPropertyName(name: String, prefixesToStrip: List<String> = emptyList()): String =
name.stripPrefixes(prefixesToStrip)
fun toPropertyNameCapitalized(name: String, prefixesToStrip: List<String> = emptyList()): String =
name.stripPrefixes(prefixesToStrip).capitalize()
private fun String.stripPrefixes(prefixes: List<String>): String =
prefixes.firstOrNull { isPrefix(it) }?.let { prefix ->
drop(prefix.length)
} ?: this
private fun String.isPrefix(prefix: String): Boolean =
length > prefix.length && startsWith(prefix) && get(prefix.length).isUpperCase()
@@ -0,0 +1,42 @@
//FILE: AccessorsTest.java
import lombok.*;
import lombok.experimental.*;
@Getter @Setter
public class AccessorsTest {
private int age = 10;
private boolean isHuman;
private Boolean isNonPrimitiveHuman;
static void test() {
val obj = new AccessorsTest();
obj.getAge();
obj.isHuman();
obj.setHuman(true);
obj.getIsNonPrimitiveHuman();
obj.setIsNonPrimitiveHuman(false);
}
}
//FILE: test.kt
class Test {
fun run() {
val obj = AccessorsTest()
obj.getAge()
val age = obj.age
obj.isHuman()
obj.setHuman(true)
}
}
@@ -31,6 +31,11 @@ public class LombokCompileTestGenerated extends AbstractLombokCompileTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/lombok/lombok-compiler-plugin/testData/compile"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("accessorsStripPrefix.kt")
public void testAccessorsStripPrefix() throws Exception {
runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/accessorsStripPrefix.kt");
}
@TestMetadata("allArgsConstructor.kt")
public void testAllArgsConstructor() throws Exception {
runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/allArgsConstructor.kt");