From f2c0b8c68d68d10cfc13909bbc0a4837044dfc5e Mon Sep 17 00:00:00 2001 From: Andrey Zinovyev Date: Tue, 30 Mar 2021 17:17:13 +0300 Subject: [PATCH] [lombok] Strip 'is' prefix in boolean properties --- .../lombok/lombok-compiler-plugin/README.md | 2 +- .../kotlin/lombok/config/annotationConfig.kt | 18 +++++--- .../lombok/processor/GetterProcessor.kt | 20 +++------ .../lombok/processor/SetterProcessor.kt | 8 ++-- .../kotlin/lombok/processor/WithProcessor.kt | 2 +- .../kotlin/lombok/utils/annotationUtils.kt | 22 ++++++---- .../kotlin/lombok/utils/descriptorUtils.kt | 4 ++ .../kotlin/lombok/utils/nameUtils.kt | 30 ++++++++++++- .../testData/compile/accessorsStripPrefix.kt | 42 +++++++++++++++++++ .../lombok/LombokCompileTestGenerated.java | 5 +++ 10 files changed, 115 insertions(+), 38 deletions(-) create mode 100644 plugins/lombok/lombok-compiler-plugin/testData/compile/accessorsStripPrefix.kt diff --git a/plugins/lombok/lombok-compiler-plugin/README.md b/plugins/lombok/lombok-compiler-plugin/README.md index dfc2d678179..6cb5ef691a2 100644 --- a/plugins/lombok/lombok-compiler-plugin/README.md +++ b/plugins/lombok/lombok-compiler-plugin/README.md @@ -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) diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/config/annotationConfig.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/config/annotationConfig.kt index a4df656932c..78cf3e70856 100644 --- a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/config/annotationConfig.kt +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/config/annotationConfig.kt @@ -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 { } -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 = emptyList() +) { companion object : AnnotationAndConfigCompanion() { 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) } } } diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/GetterProcessor.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/GetterProcessor.kt index 53ca8bc4fbe..85e7bc1def9 100644 --- a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/GetterProcessor.kt +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/GetterProcessor.kt @@ -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 - } diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/SetterProcessor.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/SetterProcessor.kt index 4b9134c3196..80c6d4ef85f 100644 --- a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/SetterProcessor.kt +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/SetterProcessor.kt @@ -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 diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/WithProcessor.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/WithProcessor.kt index 03602931a96..3d5652a9c5f 100644 --- a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/WithProcessor.kt +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/processor/WithProcessor.kt @@ -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), diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/annotationUtils.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/annotationUtils.kt index 210959a8031..2533ae9837d 100644 --- a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/annotationUtils.kt +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/annotationUtils.kt @@ -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? { + 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? = diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/descriptorUtils.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/descriptorUtils.kt index cd41e090643..8aaf79d9838 100644 --- a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/descriptorUtils.kt +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/descriptorUtils.kt @@ -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 = .map { this.unsubstitutedMemberScope.getContributedVariables(it, NoLookupLocation.FROM_SYNTHETIC_SCOPE).single() }.filter { it.isJavaField } + +fun KotlinType.isPrimitiveBoolean(): Boolean = this is SimpleTypeMarker && isBoolean() diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt index 53121836f1f..fbd0424d4c4 100644 --- a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/nameUtils.kt @@ -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 = emptyList()): String = + name.stripPrefixes(prefixesToStrip) + +fun toPropertyNameCapitalized(name: String, prefixesToStrip: List = emptyList()): String = + name.stripPrefixes(prefixesToStrip).capitalize() + +private fun String.stripPrefixes(prefixes: List): 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() diff --git a/plugins/lombok/lombok-compiler-plugin/testData/compile/accessorsStripPrefix.kt b/plugins/lombok/lombok-compiler-plugin/testData/compile/accessorsStripPrefix.kt new file mode 100644 index 00000000000..973089f42f7 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/testData/compile/accessorsStripPrefix.kt @@ -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) + + } + +} diff --git a/plugins/lombok/lombok-compiler-plugin/tests/org/jetbrains/kotlin/lombok/LombokCompileTestGenerated.java b/plugins/lombok/lombok-compiler-plugin/tests/org/jetbrains/kotlin/lombok/LombokCompileTestGenerated.java index b6a7cb0f440..dc6f4d24f36 100644 --- a/plugins/lombok/lombok-compiler-plugin/tests/org/jetbrains/kotlin/lombok/LombokCompileTestGenerated.java +++ b/plugins/lombok/lombok-compiler-plugin/tests/org/jetbrains/kotlin/lombok/LombokCompileTestGenerated.java @@ -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");