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 new file mode 100644 index 00000000000..f905d124268 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/config/annotationConfig.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.lombok.config + +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.getVisibility +import org.jetbrains.kotlin.name.FqName + +abstract class AnnotationCompanion { + + abstract val name: FqName + + abstract fun extract(annotation: AnnotationDescriptor): T + + fun getOrNull(annotated: Annotated): T? = + annotated.annotations.findAnnotation(name)?.let(this::extract) +} + +data class Accessors(val fluent: Boolean = false, val chain: Boolean = false) { + companion object : AnnotationCompanion() { + + val default = Accessors() + + override val name: FqName = LombokNames.ACCESSORS + + override fun extract(annotation: AnnotationDescriptor): Accessors = + Accessors( + fluent = annotation.getBooleanArgument("fluent"), + chain = annotation.getBooleanArgument("chain") + ) + } +} + +data class Getter(val visibility: DescriptorVisibility) { + companion object : AnnotationCompanion() { + override val name: FqName = LombokNames.GETTER + + override fun extract(annotation: AnnotationDescriptor): Getter = + Getter( + visibility = getVisibility(annotation) + ) + } +} 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 73d056ffc94..ca3ee1cd743 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 @@ -5,52 +5,56 @@ package org.jetbrains.kotlin.lombok.processor -import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor -import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl -import org.jetbrains.kotlin.lombok.utils.LombokAnnotationNames +import org.jetbrains.kotlin.lombok.config.Accessors +import org.jetbrains.kotlin.lombok.config.Getter import org.jetbrains.kotlin.lombok.utils.createFunction -import org.jetbrains.kotlin.lombok.utils.getVisibility import org.jetbrains.kotlin.lombok.utils.toPreparedBase 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 -import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType class GetterProcessor : Processor { override fun contribute(classDescriptor: ClassDescriptor, jClass: JavaClassImpl): Parts { + val clAccessors = Accessors.getOrNull(classDescriptor) + val clGetter = Getter.getOrNull(classDescriptor) val functions = classDescriptor.unsubstitutedMemberScope.getVariableNames() .map { classDescriptor.unsubstitutedMemberScope.getContributedVariables(it, NoLookupLocation.FROM_SYNTHETIC_SCOPE).single() - }.collectWithNotNull { it.annotations.findAnnotation(LombokAnnotationNames.GETTER) } - .mapNotNull { (field, annotation) -> createGetter(classDescriptor, field, annotation) } + }.collectWithNotNull { Getter.getOrNull(it) ?: clGetter } + .mapNotNull { (field, annotation) -> createGetter(classDescriptor, field, annotation, clAccessors) } -// val functions = jClass.fields.collectWithNotNull { it.findAnnotation(LombokAnnotationNames.GETTER) }.mapNotNull { (field, annotation) -> -// createGetter(classDescriptor, field, annotation) -// } return Parts(functions) } private fun createGetter( classDescriptor: ClassDescriptor, field: PropertyDescriptor, - annotation: AnnotationDescriptor + getter: Getter, + classLevelAccessors: Accessors? ): SimpleFunctionDescriptor? { - val prefix = if (field.type.isPrimitiveBoolean()) "is" else "get" - val functionName = Name.identifier(prefix + toPreparedBase(field.name.identifier)) + val accessors = Accessors.getOrNull(field) ?: classLevelAccessors ?: Accessors.default + + val functionName = + if (accessors.fluent) { + field.name.identifier + } else { + val prefix = if (field.type.isPrimitiveBoolean()) "is" else "get" + prefix + toPreparedBase(field.name.identifier) + } return createFunction( classDescriptor, - functionName, + Name.identifier(functionName), emptyList(), field.returnType, - visibility = getVisibility(annotation) + visibility = getter.visibility ) } diff --git a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/LombokAnnotationNames.kt b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/LombokNames.kt similarity index 82% rename from plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/LombokAnnotationNames.kt rename to plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/LombokNames.kt index e2909dca267..22c95a22d3c 100644 --- a/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/LombokAnnotationNames.kt +++ b/plugins/lombok/lombok-compiler-plugin/src/org/jetbrains/kotlin/lombok/utils/LombokNames.kt @@ -7,8 +7,9 @@ package org.jetbrains.kotlin.lombok.utils import org.jetbrains.kotlin.name.FqName -object LombokAnnotationNames { +object LombokNames { + val ACCESSORS = FqName("lombok.experimental.Accessors") val GETTER = FqName("lombok.Getter") val SETTER = FqName("lombok.Setter") 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 1c4ed8ce428..f662f0e1ccb 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 @@ -10,27 +10,12 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.java.JavaVisibilities -import org.jetbrains.kotlin.load.java.structure.JavaAnnotation -import org.jetbrains.kotlin.load.java.structure.JavaEnumValueAnnotationArgument -import org.jetbrains.kotlin.load.java.structure.JavaLiteralAnnotationArgument import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.constants.BooleanValue import org.jetbrains.kotlin.resolve.constants.EnumValue -import java.lang.IllegalArgumentException - -internal fun getVisibility(annotation: JavaAnnotation, field: String = "value"): DescriptorVisibility { - val value = getStringAnnotationValue(annotation, field, "PUBLIC") - val visibility = when (value) { - "PUBLIC" -> Visibilities.Public - "PROTECTED" -> Visibilities.Protected - "PRIVATE" -> Visibilities.Private - "PACKAGE" -> JavaVisibilities.PackageVisibility - else -> Visibilities.Public - } - return DescriptorVisibilities.toDescriptorVisibility(visibility) -} internal fun getVisibility(annotation: AnnotationDescriptor, field: String = "value"): DescriptorVisibility { - val value = getStringAnnotationValue(annotation, field, "PUBLIC") + val value = annotation.getStringArgument(field, "PUBLIC") val visibility = when (value) { "PUBLIC" -> Visibilities.Public "PROTECTED" -> Visibilities.Protected @@ -41,19 +26,8 @@ internal fun getVisibility(annotation: AnnotationDescriptor, field: String = "va return DescriptorVisibilities.toDescriptorVisibility(visibility) } -private fun getStringAnnotationValue(annotation: JavaAnnotation, argumentName: String, default: String): String { - val argument = annotation.arguments.find { it.name?.identifier == argumentName } - ?: throw IllegalArgumentException("No argument '$argumentName' found in $annotation") - - return when (argument) { - is JavaEnumValueAnnotationArgument -> argument.entryName?.asString() ?: default - is JavaLiteralAnnotationArgument -> argument.value?.toString() ?: default - else -> throw RuntimeException("Argument $argument is not supported") - } -} - -private fun getStringAnnotationValue(annotation: AnnotationDescriptor, argumentName: String, default: String): String { - val argument = annotation.allValueArguments[Name.identifier(argumentName)] +private fun AnnotationDescriptor.getStringArgument(argumentName: String, default: String): String { + val argument = allValueArguments[Name.identifier(argumentName)] ?: return default return when (argument) { @@ -61,3 +35,13 @@ private fun getStringAnnotationValue(annotation: AnnotationDescriptor, argumentN else -> throw RuntimeException("Argument $argument is not supported") } } + +internal fun AnnotationDescriptor.getBooleanArgument(argumentName: String, default: Boolean = false): Boolean { + val argument = allValueArguments[Name.identifier(argumentName)] + ?: return default + + return when (argument) { + is BooleanValue -> argument.value + else -> throw RuntimeException("Argument $argument is not supported for Boolean value") + } +} diff --git a/plugins/lombok/lombok-compiler-plugin/testData/compile/getters.kt b/plugins/lombok/lombok-compiler-plugin/testData/compile/getters.kt index ce67f112030..9fe12beef18 100644 --- a/plugins/lombok/lombok-compiler-plugin/testData/compile/getters.kt +++ b/plugins/lombok/lombok-compiler-plugin/testData/compile/getters.kt @@ -34,5 +34,14 @@ object Test { obj.boxedBoolean obj.getBoxedBoolean() + + //shouldn't be accesible from here +// obj.getName() + } + + class OverridenGetterTest : GetterTest() { + fun usage() { + getName() + } } } diff --git a/plugins/lombok/lombok-compiler-plugin/testData/compile/gettersFluent.kt b/plugins/lombok/lombok-compiler-plugin/testData/compile/gettersFluent.kt new file mode 100644 index 00000000000..0547d53e107 --- /dev/null +++ b/plugins/lombok/lombok-compiler-plugin/testData/compile/gettersFluent.kt @@ -0,0 +1,42 @@ +//FILE: FluentTest.java + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.Accessors; + +@Accessors(fluent = true) +public class FluentTest { + @Getter private int age = 10; + + @Getter(AccessLevel.PROTECTED) private String name; + + @Getter private boolean primitiveBoolean; + + @Getter private Boolean boxedBoolean; + + void test() { + age(); + primitiveBoolean(); + } + +} + + +//FILE: test.kt + +object Test { + fun usage() { + val obj = FluentTest() + val getter = obj.age() + + obj.primitiveBoolean() + + obj.boxedBoolean() + } + + class OverridenGetterTest : FluentTest() { + fun usage() { + name() + } + } +} 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 6ac151fa190..df7bb613beb 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 @@ -29,6 +29,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("gettersFluent.kt") + public void testGettersFluent() throws Exception { + runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/gettersFluent.kt"); + } + @TestMetadata("getters.kt") public void testGetters() throws Exception { runTest("plugins/lombok/lombok-compiler-plugin/testData/compile/getters.kt");