[lombok] Refactoring, cleaning, better tests

Add assertions to tests
Add lombok license
Provide String.[de]capitalize() implementation
Get rid of weak map because it seems it is useless here
This commit is contained in:
Andrey Zinovyev
2021-04-01 12:51:58 +03:00
committed by TeamCityServer
parent c4147bc2fd
commit 01dd3d6ed4
28 changed files with 278 additions and 234 deletions
@@ -17,7 +17,7 @@ interface SyntheticJavaResolveExtension {
) { ) {
fun getProvider(project: Project): SyntheticJavaPartsProvider { fun getProvider(project: Project): SyntheticJavaPartsProvider {
val instances = getInstances(project) val instances = getInstances(project)
val providers = instances.map { it.getProvider() } val providers = instances.map { it.buildProvider() }
return if (providers.isEmpty()) { return if (providers.isEmpty()) {
SyntheticJavaPartsProvider.EMPTY SyntheticJavaPartsProvider.EMPTY
} else { } else {
@@ -26,7 +26,7 @@ interface SyntheticJavaResolveExtension {
} }
} }
fun getProvider(): SyntheticJavaPartsProvider fun buildProvider(): SyntheticJavaPartsProvider
} }
+19
View File
@@ -0,0 +1,19 @@
Copyright (C) 2009-2021 The Project Lombok Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -13,5 +13,5 @@ class LombokResolveExtension(pluginConfig: LombokPluginConfig) : SyntheticJavaRe
private val config = pluginConfig.configFile?.let(LombokConfig::parse) ?: LombokConfig.Empty private val config = pluginConfig.configFile?.let(LombokConfig::parse) ?: LombokConfig.Empty
override fun getProvider(): SyntheticJavaPartsProvider = LombokSyntheticJavaPartsProvider(config) override fun buildProvider(): SyntheticJavaPartsProvider = LombokSyntheticJavaPartsProvider(config)
} }
@@ -37,7 +37,7 @@ class LombokSyntheticJavaPartsProvider(config: LombokConfig) : SyntheticJavaPart
* but for us it is much easier to run full generation for class once * but for us it is much easier to run full generation for class once
* hence we cache results and reuse it * hence we cache results and reuse it
*/ */
private val partsCache: MutableMap<ClassDescriptor, SyntheticParts> = WeakHashMap() private val partsCache: MutableMap<ClassDescriptor, SyntheticParts> = HashMap()
override fun getMethodNames(thisDescriptor: ClassDescriptor): List<Name> = override fun getMethodNames(thisDescriptor: ClassDescriptor): List<Name> =
getSyntheticParts(thisDescriptor).methods.map { it.name } getSyntheticParts(thisDescriptor).methods.map { it.name }
@@ -75,28 +75,19 @@ class LombokSyntheticJavaPartsProvider(config: LombokConfig) : SyntheticJavaPart
} }
} ?: SyntheticParts.Empty } ?: SyntheticParts.Empty
private fun computeSyntheticParts(descriptor: ClassDescriptor): SyntheticParts = private fun computeSyntheticParts(descriptor: ClassDescriptor): SyntheticParts {
processors.map { it.contribute(descriptor) }.reduce { a, b -> a + b } val builder = SyntheticPartsBuilder()
processors.forEach { it.contribute(descriptor, builder) }
return builder.build()
}
/** /**
* Deduplicates generated functions using name and argument counts, as lombok does * Deduplicates generated functions using name and argument counts, as lombok does
*/ */
private fun <T : FunctionDescriptor> addNonExistent(result: MutableCollection<T>, toAdd: List<T>) { private fun <T : FunctionDescriptor> addNonExistent(result: MutableCollection<T>, toAdd: List<T>) {
if (toAdd.isEmpty()) return
val index = mutableMapOf<Name, List<T>>()
fun addToIndex(f: T) {
index.merge(f.name, listOf(f)) { a, b -> a + b}
}
result.forEach(::addToIndex)
toAdd.forEach { f -> toAdd.forEach { f ->
val existing = index.getOrDefault(f.name, emptyList()) if (result.none { sameSignature(it, f) }) {
if (existing.none { sameSignature (it, f) } ) {
result += f result += f
addToIndex(f)
} }
} }
} }
@@ -106,6 +97,7 @@ class LombokSyntheticJavaPartsProvider(config: LombokConfig) : SyntheticJavaPart
/** /**
* Lombok treat functions as having the same signature by arguments count only * Lombok treat functions as having the same signature by arguments count only
* Corresponding code in lombok - https://github.com/projectlombok/lombok/blob/v1.18.20/src/core/lombok/javac/handlers/JavacHandlerUtil.java#L752
*/ */
private fun sameSignature(a: FunctionDescriptor, b: FunctionDescriptor): Boolean { private fun sameSignature(a: FunctionDescriptor, b: FunctionDescriptor): Boolean {
val aVararg = a.valueParameters.any { it.varargElementType != null } val aVararg = a.valueParameters.any { it.varargElementType != null }
@@ -20,9 +20,7 @@ import org.jetbrains.kotlin.name.FqName
* *
*/ */
abstract class AnnotationCompanion<T> { abstract class AnnotationCompanion<T>(val name: FqName) {
abstract val name: FqName
abstract fun extract(annotation: AnnotationDescriptor): T abstract fun extract(annotation: AnnotationDescriptor): T
@@ -30,10 +28,7 @@ abstract class AnnotationCompanion<T> {
annotated.annotations.findAnnotation(name)?.let(this::extract) annotated.annotations.findAnnotation(name)?.let(this::extract)
} }
abstract class AnnotationAndConfigCompanion<T> { abstract class AnnotationAndConfigCompanion<T>(private val annotationName: FqName) {
abstract val annotationName: FqName
abstract fun extract(annotation: AnnotationDescriptor?, config: LombokConfig): T abstract fun extract(annotation: AnnotationDescriptor?, config: LombokConfig): T
@@ -53,155 +48,148 @@ abstract class AnnotationAndConfigCompanion<T> {
} }
data class Accessors( object LombokAnnotations {
val fluent: Boolean = false, class Accessors(
val chain: Boolean = false, val fluent: Boolean = false,
val noIsPrefix: Boolean = false, val chain: Boolean = false,
val prefix: List<String> = emptyList() val noIsPrefix: Boolean = false,
) { val prefix: List<String> = emptyList()
companion object : AnnotationAndConfigCompanion<Accessors>() { ) {
companion object : AnnotationAndConfigCompanion<Accessors>(LombokNames.ACCESSORS) {
override val annotationName: FqName = LombokNames.ACCESSORS override fun extract(annotation: AnnotationDescriptor?, config: LombokConfig): Accessors {
val fluent =
annotation?.getBooleanArgument("fluent")
?: config.getBoolean("lombok.accessors.fluent")
?: false
val chain =
annotation?.getBooleanArgument("chain")
?: config.getBoolean("lombok.accessors.chain")
?: fluent
val noIsPrefix = config.getBoolean("lombok.getter.noIsPrefix") ?: false
val prefix =
annotation?.getStringArrayArgument("prefix")
?: config.getMultiString("lombok.accessors.prefix")
?: emptyList()
override fun extract(annotation: AnnotationDescriptor?, config: LombokConfig): Accessors { return Accessors(fluent, chain, noIsPrefix, prefix)
val fluent = }
annotation?.getBooleanArgument("fluent") }
?: config.getBoolean("lombok.accessors.fluent") }
?: false
val chain = class Getter(val visibility: AccessLevel = AccessLevel.PUBLIC) {
annotation?.getBooleanArgument("chain") companion object : AnnotationCompanion<Getter>(LombokNames.GETTER) {
?: config.getBoolean("lombok.accessors.chain")
?: fluent override fun extract(annotation: AnnotationDescriptor): Getter =
val noIsPrefix = config.getBoolean("lombok.getter.noIsPrefix") ?: false Getter(
val prefix = visibility = getAccessLevel(annotation)
annotation?.getStringArrayArgument("prefix") )
?: config.getMultiString("lombok.accessors.prefix") }
?: emptyList() }
class Setter(val visibility: AccessLevel = AccessLevel.PUBLIC) {
companion object : AnnotationCompanion<Setter>( LombokNames.SETTER) {
override fun extract(annotation: AnnotationDescriptor): Setter =
Setter(
visibility = getAccessLevel(annotation)
)
}
}
class With(val visibility: AccessLevel = AccessLevel.PUBLIC) {
companion object : AnnotationCompanion<With>(LombokNames.WITH) {
override fun extract(annotation: AnnotationDescriptor): With =
With(
visibility = getAccessLevel(annotation)
)
}
}
interface ConstructorAnnotation {
val visibility: DescriptorVisibility
val staticName: String?
}
class NoArgsConstructor(
override val visibility: DescriptorVisibility,
override val staticName: String?
) : ConstructorAnnotation {
companion object : AnnotationCompanion<NoArgsConstructor>( LombokNames.NO_ARGS_CONSTRUCTOR) {
override fun extract(annotation: AnnotationDescriptor): NoArgsConstructor =
NoArgsConstructor(
visibility = getVisibility(annotation, "access"),
staticName = annotation.getNonBlankStringArgument("staticName")
)
}
}
class AllArgsConstructor(
override val visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC,
override val staticName: String? = null
) : ConstructorAnnotation {
companion object : AnnotationCompanion<AllArgsConstructor>(LombokNames.ALL_ARGS_CONSTRUCTOR) {
override fun extract(annotation: AnnotationDescriptor): AllArgsConstructor =
AllArgsConstructor(
visibility = getVisibility(annotation, "access"),
staticName = annotation.getNonBlankStringArgument("staticName")
)
}
}
class RequiredArgsConstructor(
override val visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC,
override val staticName: String? = null
) : ConstructorAnnotation {
companion object : AnnotationCompanion<RequiredArgsConstructor>(LombokNames.REQUIRED_ARGS_CONSTRUCTOR) {
override fun extract(annotation: AnnotationDescriptor): RequiredArgsConstructor =
RequiredArgsConstructor(
visibility = getVisibility(annotation, "access"),
staticName = annotation.getNonBlankStringArgument("staticName")
)
}
}
class Data(val staticConstructor: String?) {
fun asSetter(): Setter = Setter()
fun asGetter(): Getter = Getter()
fun asRequiredArgsConstructor(): RequiredArgsConstructor = RequiredArgsConstructor(
staticName = staticConstructor
)
companion object : AnnotationCompanion<Data>(LombokNames.DATA) {
override fun extract(annotation: AnnotationDescriptor): Data =
Data(
staticConstructor = annotation.getNonBlankStringArgument("staticConstructor")
)
}
}
class Value(val staticConstructor: String?) {
fun asGetter(): Getter = Getter()
fun asAllArgsConstructor(): AllArgsConstructor = AllArgsConstructor(
staticName = staticConstructor
)
companion object : AnnotationCompanion<Value>(LombokNames.VALUE) {
override fun extract(annotation: AnnotationDescriptor): Value =
Value(
staticConstructor = annotation.getNonBlankStringArgument("staticConstructor")
)
return Accessors(fluent, chain, noIsPrefix, prefix)
} }
} }
} }
data class Getter(val visibility: AccessLevel = AccessLevel.PUBLIC) {
companion object : AnnotationCompanion<Getter>() {
override val name: FqName = LombokNames.GETTER
override fun extract(annotation: AnnotationDescriptor): Getter =
Getter(
visibility = getAccessLevel(annotation)
)
}
}
data class Setter(val visibility: AccessLevel = AccessLevel.PUBLIC) {
companion object : AnnotationCompanion<Setter>() {
override val name: FqName = LombokNames.SETTER
override fun extract(annotation: AnnotationDescriptor): Setter =
Setter(
visibility = getAccessLevel(annotation)
)
}
}
data class With(val visibility: AccessLevel = AccessLevel.PUBLIC) {
companion object : AnnotationCompanion<With>() {
override val name: FqName = LombokNames.WITH
override fun extract(annotation: AnnotationDescriptor): With =
With(
visibility = getAccessLevel(annotation)
)
}
}
interface ConstructorAnnotation {
val visibility: DescriptorVisibility
val staticName: String?
}
data class NoArgsConstructor(
override val visibility: DescriptorVisibility,
override val staticName: String?
) : ConstructorAnnotation {
companion object : AnnotationCompanion<NoArgsConstructor>() {
override val name: FqName = LombokNames.NO_ARGS_CONSTRUCTOR
override fun extract(annotation: AnnotationDescriptor): NoArgsConstructor =
NoArgsConstructor(
visibility = getVisibility(annotation, "access"),
staticName = annotation.getNonBlankStringArgument("staticName")
)
}
}
data class AllArgsConstructor(
override val visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC,
override val staticName: String? = null
) : ConstructorAnnotation {
companion object : AnnotationCompanion<AllArgsConstructor>() {
override val name: FqName = LombokNames.ALL_ARGS_CONSTRUCTOR
override fun extract(annotation: AnnotationDescriptor): AllArgsConstructor =
AllArgsConstructor(
visibility = getVisibility(annotation, "access"),
staticName = annotation.getNonBlankStringArgument("staticName")
)
}
}
data class RequiredArgsConstructor(
override val visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC,
override val staticName: String? = null
) : ConstructorAnnotation {
companion object : AnnotationCompanion<RequiredArgsConstructor>() {
override val name: FqName = LombokNames.REQUIRED_ARGS_CONSTRUCTOR
override fun extract(annotation: AnnotationDescriptor): RequiredArgsConstructor =
RequiredArgsConstructor(
visibility = getVisibility(annotation, "access"),
staticName = annotation.getNonBlankStringArgument("staticName")
)
}
}
data class Data(val staticConstructor: String?) {
fun asSetter(): Setter = Setter()
fun asGetter(): Getter = Getter()
fun asRequiredArgsConstructor(): RequiredArgsConstructor = RequiredArgsConstructor(
staticName = staticConstructor
)
companion object : AnnotationCompanion<Data>() {
override val name: FqName = LombokNames.DATA
override fun extract(annotation: AnnotationDescriptor): Data =
Data(
staticConstructor = annotation.getNonBlankStringArgument("staticConstructor")
)
}
}
data class Value(val staticConstructor: String?) {
fun asGetter(): Getter = Getter()
fun asAllArgsConstructor(): AllArgsConstructor = AllArgsConstructor(
staticName = staticConstructor
)
companion object : AnnotationCompanion<Value>() {
override val name: FqName = LombokNames.VALUE
override fun extract(annotation: AnnotationDescriptor): Value =
Value(
staticConstructor = annotation.getNonBlankStringArgument("staticConstructor")
)
}
}
@@ -7,26 +7,26 @@ package org.jetbrains.kotlin.lombok.processor
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.lombok.config.ConstructorAnnotation import org.jetbrains.kotlin.lombok.config.LombokAnnotations.ConstructorAnnotation
import org.jetbrains.kotlin.lombok.utils.ValueParameter import org.jetbrains.kotlin.lombok.utils.LombokValueParameter
import org.jetbrains.kotlin.lombok.utils.createJavaConstructor
import org.jetbrains.kotlin.lombok.utils.createFunction import org.jetbrains.kotlin.lombok.utils.createFunction
import org.jetbrains.kotlin.lombok.utils.createJavaConstructor
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
abstract class AbstractConstructorProcessor<A : ConstructorAnnotation> : Processor { abstract class AbstractConstructorProcessor<A : ConstructorAnnotation> : Processor {
override fun contribute(classDescriptor: ClassDescriptor): SyntheticParts { override fun contribute(classDescriptor: ClassDescriptor, partsBuilder: SyntheticPartsBuilder) {
val valueParameters = getPropertiesForParameters(classDescriptor).map { property -> val valueParameters = getPropertiesForParameters(classDescriptor).map { property ->
ValueParameter(property.name, property.type) LombokValueParameter(property.name, property.type)
} }
val result = getAnnotation(classDescriptor)?.let { annotation -> getAnnotation(classDescriptor)?.let { annotation ->
if (annotation.staticName == null) { if (annotation.staticName == null) {
val constructor = classDescriptor.createJavaConstructor( val constructor = classDescriptor.createJavaConstructor(
valueParameters = valueParameters, valueParameters = valueParameters,
visibility = annotation.visibility visibility = annotation.visibility
) )
SyntheticParts(constructors = listOfNotNull(constructor)) partsBuilder.addConstructor(constructor)
} else { } else {
val function = classDescriptor.createFunction( val function = classDescriptor.createFunction(
Name.identifier(annotation.staticName!!), Name.identifier(annotation.staticName!!),
@@ -36,10 +36,9 @@ abstract class AbstractConstructorProcessor<A : ConstructorAnnotation> : Process
visibility = annotation.visibility, visibility = annotation.visibility,
receiver = null receiver = null
) )
SyntheticParts(staticFunctions = listOf(function)) partsBuilder.addStaticFunction(function)
} }
} }
return result ?: SyntheticParts.Empty
} }
protected abstract fun getAnnotation(classDescriptor: ClassDescriptor): A? protected abstract fun getAnnotation(classDescriptor: ClassDescriptor): A?
@@ -7,8 +7,8 @@ package org.jetbrains.kotlin.lombok.processor
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.lombok.config.AllArgsConstructor import org.jetbrains.kotlin.lombok.config.LombokAnnotations.AllArgsConstructor
import org.jetbrains.kotlin.lombok.config.Value import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Value
import org.jetbrains.kotlin.lombok.utils.getJavaFields import org.jetbrains.kotlin.lombok.utils.getJavaFields
class AllArgsConstructorProcessor : AbstractConstructorProcessor<AllArgsConstructor>() { class AllArgsConstructorProcessor : AbstractConstructorProcessor<AllArgsConstructor>() {
@@ -9,24 +9,27 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.lombok.config.* import org.jetbrains.kotlin.lombok.config.*
import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Accessors
import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Getter
import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Data
import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Value
import org.jetbrains.kotlin.lombok.utils.* import org.jetbrains.kotlin.lombok.utils.*
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
class GetterProcessor(private val config: LombokConfig) : Processor { class GetterProcessor(private val config: LombokConfig) : Processor {
override fun contribute(classDescriptor: ClassDescriptor): SyntheticParts { override fun contribute(classDescriptor: ClassDescriptor, partsBuilder: SyntheticPartsBuilder) {
val globalAccessors = Accessors.get(classDescriptor, config) val globalAccessors = Accessors.get(classDescriptor, config)
val clGetter = val clGetter =
Getter.getOrNull(classDescriptor) Getter.getOrNull(classDescriptor)
?: Data.getOrNull(classDescriptor)?.asGetter() ?: Data.getOrNull(classDescriptor)?.asGetter()
?: Value.getOrNull(classDescriptor)?.asGetter() ?: Value.getOrNull(classDescriptor)?.asGetter()
val functions = classDescriptor classDescriptor
.getJavaFields() .getJavaFields()
.collectWithNotNull { Getter.getOrNull(it) ?: clGetter } .collectWithNotNull { Getter.getOrNull(it) ?: clGetter }
.mapNotNull { (field, annotation) -> createGetter(classDescriptor, field, annotation, globalAccessors) } .mapNotNull { (field, annotation) -> createGetter(classDescriptor, field, annotation, globalAccessors) }
.forEach(partsBuilder::addMethod)
return SyntheticParts(functions)
} }
private fun createGetter( private fun createGetter(
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.lombok.processor
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.lombok.config.NoArgsConstructor import org.jetbrains.kotlin.lombok.config.LombokAnnotations.NoArgsConstructor
class NoArgsConstructorProcessor : AbstractConstructorProcessor<NoArgsConstructor>() { class NoArgsConstructorProcessor : AbstractConstructorProcessor<NoArgsConstructor>() {
@@ -12,5 +12,6 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
*/ */
interface Processor { interface Processor {
fun contribute(classDescriptor: ClassDescriptor): SyntheticParts fun contribute(classDescriptor: ClassDescriptor, partsBuilder: SyntheticPartsBuilder)
} }
@@ -9,8 +9,8 @@ import com.intellij.psi.PsiField
import com.intellij.psi.PsiModifier import com.intellij.psi.PsiModifier
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.lombok.config.Data import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Data
import org.jetbrains.kotlin.lombok.config.RequiredArgsConstructor import org.jetbrains.kotlin.lombok.config.LombokAnnotations.RequiredArgsConstructor
import org.jetbrains.kotlin.lombok.utils.LombokNames import org.jetbrains.kotlin.lombok.utils.LombokNames
import org.jetbrains.kotlin.lombok.utils.getJavaFields import org.jetbrains.kotlin.lombok.utils.getJavaFields
import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.resolve.source.getPsi
@@ -9,22 +9,25 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.lombok.config.* import org.jetbrains.kotlin.lombok.config.*
import org.jetbrains.kotlin.lombok.utils.* import org.jetbrains.kotlin.lombok.utils.*
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Accessors
import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Setter
import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Data
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
class SetterProcessor(private val config: LombokConfig) : Processor { class SetterProcessor(private val config: LombokConfig) : Processor {
override fun contribute(classDescriptor: ClassDescriptor): SyntheticParts { override fun contribute(classDescriptor: ClassDescriptor, partsBuilder: SyntheticPartsBuilder) {
//lombok doesn't generate setters for enums //lombok doesn't generate setters for enums
if (classDescriptor.kind == ClassKind.ENUM_CLASS) return SyntheticParts.Empty if (classDescriptor.kind == ClassKind.ENUM_CLASS) return
val globalAccessors = Accessors.get(classDescriptor, config) val globalAccessors = Accessors.get(classDescriptor, config)
val clSetter = Setter.getOrNull(classDescriptor) ?: Data.getOrNull(classDescriptor)?.asSetter() val clSetter = Setter.getOrNull(classDescriptor) ?: Data.getOrNull(classDescriptor)?.asSetter()
val functions = classDescriptor classDescriptor
.getJavaFields() .getJavaFields()
.collectWithNotNull { field -> Setter.getOrNull(field) ?: clSetter.takeIf { field.isVar } } .collectWithNotNull { field -> Setter.getOrNull(field) ?: clSetter.takeIf { field.isVar } }
.mapNotNull { (field, setter) -> createSetter(classDescriptor, field, setter, globalAccessors) } .mapNotNull { (field, setter) -> createSetter(classDescriptor, field, setter, globalAccessors) }
return SyntheticParts(functions) .forEach(partsBuilder::addMethod)
} }
private fun createSetter( private fun createSetter(
@@ -45,7 +48,7 @@ class SetterProcessor(private val config: LombokConfig) : Processor {
classDescriptor.createFunction( classDescriptor.createFunction(
Name.identifier(functionName), Name.identifier(functionName),
listOf(ValueParameter(field.name, field.type)), listOf(LombokValueParameter(field.name, field.type)),
returnType, returnType,
visibility = getter.visibility.toDescriptorVisibility() visibility = getter.visibility.toDescriptorVisibility()
) )
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.lombok.processor
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
data class SyntheticParts( class SyntheticParts(
val methods: List<SimpleFunctionDescriptor> = emptyList(), val methods: List<SimpleFunctionDescriptor> = emptyList(),
val staticFunctions: List<SimpleFunctionDescriptor> = emptyList(), val staticFunctions: List<SimpleFunctionDescriptor> = emptyList(),
val constructors: List<ClassConstructorDescriptor> = emptyList() val constructors: List<ClassConstructorDescriptor> = emptyList()
@@ -24,3 +24,23 @@ data class SyntheticParts(
val Empty = SyntheticParts() val Empty = SyntheticParts()
} }
} }
class SyntheticPartsBuilder {
private val methods = mutableListOf<SimpleFunctionDescriptor>()
private val staticFunctions = mutableListOf<SimpleFunctionDescriptor>()
private val constructors = mutableListOf<ClassConstructorDescriptor>()
fun addMethod(method: SimpleFunctionDescriptor) {
methods += method
}
fun addStaticFunction(function: SimpleFunctionDescriptor) {
staticFunctions += function
}
fun addConstructor(constructor: ClassConstructorDescriptor) {
constructors += constructor
}
fun build(): SyntheticParts = SyntheticParts(methods, staticFunctions, constructors)
}
@@ -9,21 +9,19 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.lombok.config.AccessLevel import org.jetbrains.kotlin.lombok.config.AccessLevel
import org.jetbrains.kotlin.lombok.config.With import org.jetbrains.kotlin.lombok.config.LombokAnnotations.With
import org.jetbrains.kotlin.lombok.utils.* import org.jetbrains.kotlin.lombok.utils.*
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
class WithProcessor : Processor { class WithProcessor : Processor {
override fun contribute(classDescriptor: ClassDescriptor): SyntheticParts { override fun contribute(classDescriptor: ClassDescriptor, partsBuilder: SyntheticPartsBuilder) {
val clWith = With.getOrNull(classDescriptor) val clWith = With.getOrNull(classDescriptor)
val functions = classDescriptor classDescriptor
.getJavaFields() .getJavaFields()
.collectWithNotNull { With.getOrNull(it) ?: clWith } .collectWithNotNull { With.getOrNull(it) ?: clWith }
.mapNotNull { (field, annotation) -> createWith(classDescriptor, field, annotation) } .mapNotNull { (field, annotation) -> createWith(classDescriptor, field, annotation) }
.forEach(partsBuilder::addMethod)
return SyntheticParts(functions)
} }
private fun createWith( private fun createWith(
@@ -37,7 +35,7 @@ class WithProcessor : Processor {
return classDescriptor.createFunction( return classDescriptor.createFunction(
Name.identifier(functionName), Name.identifier(functionName),
listOf(ValueParameter(field.name, field.type)), listOf(LombokValueParameter(field.name, field.type)),
classDescriptor.defaultType, classDescriptor.defaultType,
visibility = with.visibility.toDescriptorVisibility() visibility = with.visibility.toDescriptorVisibility()
) )
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.lombok.utils
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
@@ -18,11 +17,11 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.model.SimpleTypeMarker import org.jetbrains.kotlin.types.model.SimpleTypeMarker
import org.jetbrains.kotlin.types.typeUtil.isBoolean import org.jetbrains.kotlin.types.typeUtil.isBoolean
data class ValueParameter(val name: Name, val type: KotlinType) data class LombokValueParameter(val name: Name, val type: KotlinType)
fun ClassDescriptor.createFunction( fun ClassDescriptor.createFunction(
name: Name, name: Name,
valueParameters: List<ValueParameter>, valueParameters: List<LombokValueParameter>,
returnType: KotlinType?, returnType: KotlinType?,
typeParameters: List<TypeParameterDescriptor> = emptyList(), typeParameters: List<TypeParameterDescriptor> = emptyList(),
modality: Modality? = Modality.OPEN, modality: Modality? = Modality.OPEN,
@@ -52,7 +51,7 @@ fun ClassDescriptor.createFunction(
} }
fun ClassDescriptor.createJavaConstructor( fun ClassDescriptor.createJavaConstructor(
valueParameters: List<ValueParameter>, valueParameters: List<LombokValueParameter>,
visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC
): ClassConstructorDescriptor { ): ClassConstructorDescriptor {
val constructor = JavaClassConstructorDescriptor.create( val constructor = JavaClassConstructorDescriptor.create(
@@ -74,7 +73,7 @@ fun ClassDescriptor.createJavaConstructor(
return constructor return constructor
} }
private fun CallableDescriptor.makeValueParameter(param: ValueParameter, index: Int): ValueParameterDescriptor { private fun CallableDescriptor.makeValueParameter(param: LombokValueParameter, index: Int): ValueParameterDescriptor {
return ValueParameterDescriptorImpl( return ValueParameterDescriptorImpl(
containingDeclaration = this, containingDeclaration = this,
original = null, original = null,
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.lombok.utils package org.jetbrains.kotlin.lombok.utils
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.lombok.config.Accessors import org.jetbrains.kotlin.lombok.config.LombokAnnotations.Accessors
object AccessorNames { object AccessorNames {
const val IS = "is" const val IS = "is"
@@ -5,8 +5,15 @@
package org.jetbrains.kotlin.lombok.utils package org.jetbrains.kotlin.lombok.utils
import java.util.*
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <E, R> Collection<E>.collectWithNotNull(f: (E) -> R?): List<Pair<E, R>> = fun <E, R> Collection<E>.collectWithNotNull(f: (E) -> R?): List<Pair<E, R>> =
map { it to f(it) }.filter { it.second != null } as List<Pair<E, R>> map { it to f(it) }.filter { it.second != null } as List<Pair<E, R>>
fun String?.trimToNull(): String? = this?.trim()?.takeIf { it.isNotEmpty() } fun String?.trimToNull(): String? = this?.trim()?.takeIf { it.isNotEmpty() }
//because capitalize from stdlib is deprecated
fun String.capitalize(): String = replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
fun String.decapitalize(): String = replaceFirstChar { if (it.isUpperCase()) it.lowercase(Locale.getDefault()) else it.toString() }
@@ -0,0 +1,3 @@
fun <T> assertEquals(a: T, b: T) {
if (a != b) throw RuntimeException("'$a' was not equal to '$b'")
}
@@ -31,11 +31,13 @@ class Test {
fun run() { fun run() {
val obj = DataExample("name") val obj = DataExample("name")
obj.getName() obj.getName()
val name = obj.name assertEquals(obj.name, "name")
obj.getTags() obj.getTags()
val tags = obj.tags val tags = obj.tags
obj.setScore(1.5) obj.setScore(1.5)
assertEquals(obj.score, 1.5)
obj.score = 2.5 obj.score = 2.5
assertEquals(obj.score, 2.5)
val ex: DataExample.Exercise<Int> = DataExample.Exercise.of("name", 12) val ex: DataExample.Exercise<Int> = DataExample.Exercise.of("name", 12)
} }
@@ -24,6 +24,8 @@ public class ConstructorExample<A, B> {
class Test { class Test {
fun run() { fun run() {
val generated: ConstructorExample<Long, Boolean> = ConstructorExample.of(12, 42L, true) val generated: ConstructorExample<Long, Boolean> = ConstructorExample.of(12, 42L, true)
val generatedReq: ConstructorExample<String, Boolean> = ConstructorExample.of("234"); assertEquals(generated.name, 42L)
val generatedReq: ConstructorExample<String, Boolean> = ConstructorExample.of("234")
assertEquals(generatedReq.name, "234")
} }
} }
@@ -30,7 +30,7 @@ public class FluentTest {
class Test { class Test {
fun run() { fun run() {
val obj = FluentTest() val obj = FluentTest()
val getter = obj.age() assertEquals(obj.age(), 10)
obj.primitiveBoolean() obj.primitiveBoolean()
@@ -36,5 +36,6 @@ public class ConstructorExample {
class Test { class Test {
fun run() { fun run() {
val generated = ConstructorExample("foo", true) val generated = ConstructorExample("foo", true)
assertEquals(generated.foo, "foo")
} }
} }
@@ -38,5 +38,6 @@ public class ConstructorExample {
class Test { class Test {
fun run() { fun run() {
val generated = ConstructorExample.build("foo", true, 12) val generated = ConstructorExample.build("foo", true, 12)
assertEquals(generated.foo, "foo")
} }
} }
@@ -27,7 +27,9 @@ class Test {
fun run() { fun run() {
val obj = SetterTest() val obj = SetterTest()
obj.setAge(42) obj.setAge(42)
obj.age = 42 assertEquals(obj.age, 42)
obj.age = 43
assertEquals(obj.age, 43)
obj.setPrimitiveBoolean(true) obj.setPrimitiveBoolean(true)
@@ -29,8 +29,11 @@ class Test {
fun run() { fun run() {
val obj = SetterTest() val obj = SetterTest()
obj.fluent(12) obj.fluent(12)
assertEquals(obj.fluent(), 12)
obj.setChained("zz").getChained() obj.setChained("zz").getChained()
assertEquals(obj.getChained(), "zz")
obj.whyNotBoth("zzz").whyNotBoth() obj.whyNotBoth("zzz").whyNotBoth()
assertEquals(obj.whyNotBoth(), "zzz")
} }
} }
@@ -31,12 +31,14 @@ import lombok.*;
class Test { class Test {
fun run() { fun run() {
val obj = ValueExample("name", 12, 4.5) val obj = ValueExample("name", 12, 4.5)
obj.getName() assertEquals(obj.getName(), "name")
val name = obj.name assertEquals(obj.name, "name")
obj.getAge() assertEquals(obj.getAge(), 12)
val age = obj.age assertEquals(obj.age, 12)
val score = obj.score assertEquals(obj.score, 4.5)
val ex: ValueExample.Exercise<Int> = ValueExample.Exercise.of("name", 12) val ex: ValueExample.Exercise<Int> = ValueExample.Exercise.of("nam1e", 42)
assertEquals(ex.name, "nam1e")
assertEquals(ex.value, 42)
} }
} }
@@ -1,25 +1,17 @@
//FILE: WithExample.java //FILE: WithExample.java
import lombok.AccessLevel; import lombok.*;
import lombok.With;
@AllArgsConstructor
@NoArgsConstructor
public class WithExample { public class WithExample {
//todo replace constructors with annotations when supported @Getter @With private int age = 10;
public WithExample() {
} @Getter @With private String name;
public WithExample(int age, String name) { public static WithExample test() {
return new WithExample().withAge(16).withName("fooo");
}
@With private int age = 10;
@With private String name;
static void test() {
WithExample obj = new WithExample().withAge(16).withName("fooo");
} }
} }
@@ -27,7 +19,10 @@ public class WithExample {
//FILE: test.kt //FILE: test.kt
class Test { class Test {
fun run() { fun run() {
val obj: WithExample = WithExample().withAge(16).withName("fooo") val obj: WithExample = WithExample().withAge(16).withName("fooo")
assertEquals(obj.getName(), "fooo")
assertEquals(obj.age, 16)
} }
} }
@@ -18,9 +18,13 @@ import java.net.URLClassLoader
abstract class AbstractLombokCompileTest : CodegenTestCase() { abstract class AbstractLombokCompileTest : CodegenTestCase() {
private val commonClassLoader = URLClassLoader(arrayOf(getLombokJar().toURI().toURL())) private val commonClassLoader = URLClassLoader(arrayOf(getLombokJar().toURI().toURL()))
private val commonSourceFile = TestFile(
"common.kt",
File("plugins/lombok/lombok-compiler-plugin/testData/common.kt").readText()
)
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>) { override fun doMultiFileTest(wholeFile: File, files: List<TestFile>) {
compile(files, true, true) compile(files + commonSourceFile, true, true)
} }
override fun setupEnvironment(environment: KotlinCoreEnvironment) { override fun setupEnvironment(environment: KotlinCoreEnvironment) {