-
Notifications
You must be signed in to change notification settings - Fork 409
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix special "is" case for accessors and refactor logic in general
- Loading branch information
1 parent
b8ec1d2
commit fb37558
Showing
6 changed files
with
389 additions
and
243 deletions.
There are no files selected for viewing
12 changes: 12 additions & 0 deletions
12
plugins/base/src/main/kotlin/translators/CollectionExtensions.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package org.jetbrains.dokka.base.translators | ||
|
||
// TODO [beresnev] remove this copy-paste and use the same method from stdlib instead after updating to 1.5 | ||
internal inline fun <T, R : Any> Iterable<T>.firstNotNullOfOrNull(transform: (T) -> R?): R? { | ||
for (element in this) { | ||
val result = transform(element) | ||
if (result != null) { | ||
return result | ||
} | ||
} | ||
return null | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
plugins/base/src/main/kotlin/translators/descriptors/DescriptorAccessorConventionUtil.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package org.jetbrains.dokka.base.translators.descriptors | ||
|
||
import org.jetbrains.dokka.base.translators.firstNotNullOfOrNull | ||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor | ||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor | ||
import org.jetbrains.kotlin.load.java.JvmAbi | ||
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor | ||
import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName | ||
import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName | ||
|
||
|
||
internal data class DescriptorFunctionsHolder( | ||
val regularFunctions: List<FunctionDescriptor>, | ||
val accessors: Map<PropertyDescriptor, List<FunctionDescriptor>> | ||
) | ||
|
||
internal fun splitFunctionsAndAccessors( | ||
properties: List<PropertyDescriptor>, | ||
functions: List<FunctionDescriptor> | ||
): DescriptorFunctionsHolder { | ||
val fieldsByName = properties.associateBy { it.name.asString() } | ||
val regularFunctions = mutableListOf<FunctionDescriptor>() | ||
val accessors = mutableMapOf<PropertyDescriptor, MutableList<FunctionDescriptor>>() | ||
functions.forEach { function -> | ||
val possiblePropertyNamesForFunction = function.toPossiblePropertyNames() | ||
val field = possiblePropertyNamesForFunction.firstNotNullOfOrNull { fieldsByName[it] } | ||
if (field != null) { | ||
accessors.getOrPut(field, ::mutableListOf).add(function) | ||
} else { | ||
regularFunctions.add(function) | ||
} | ||
} | ||
return DescriptorFunctionsHolder(regularFunctions, accessors) | ||
} | ||
|
||
internal fun FunctionDescriptor.toPossiblePropertyNames(): List<String> { | ||
val stringName = this.name.asString() | ||
return when { | ||
JvmAbi.isSetterName(stringName) -> propertyNamesBySetMethodName(this.name).map { it.asString() } | ||
JvmAbi.isGetterName(stringName) -> propertyNamesByGetMethod(this) | ||
else -> listOf() | ||
} | ||
} | ||
|
||
internal fun propertyNamesByGetMethod(functionDescriptor: FunctionDescriptor): List<String> { | ||
val stringName = functionDescriptor.name.asString() | ||
// In java, the convention for boolean property accessors is as follows: | ||
// - `private boolean active;` | ||
// - `private boolean isActive();` | ||
// | ||
// Whereas in Kotlin, because there are no explicit accessors, the convention is | ||
// - `val isActive: Boolean` | ||
// | ||
// This makes it difficult to guess the name of the accessor property in case of Java | ||
val javaPropName = if (functionDescriptor is JavaMethodDescriptor && JvmAbi.startsWithIsPrefix(stringName)) { | ||
val javaPropName = stringName.removePrefix("is").let { newName -> | ||
newName.replaceFirst(newName[0], newName[0].toLowerCase()) | ||
} | ||
javaPropName | ||
} else { | ||
null | ||
} | ||
val kotlinPropName = propertyNameByGetMethodName(functionDescriptor.name)?.asString() | ||
return listOfNotNull(javaPropName, kotlinPropName) | ||
} | ||
|
||
internal fun FunctionDescriptor.isGetterFor(property: PropertyDescriptor): Boolean { | ||
return this.returnType == property.returnType | ||
&& this.valueParameters.isEmpty() | ||
&& !property.visibility.isPublicAPI | ||
&& this.visibility.isPublicAPI | ||
} | ||
|
||
internal fun FunctionDescriptor.isSetterFor(property: PropertyDescriptor): Boolean { | ||
return this.valueParameters.size == 1 | ||
&& this.valueParameters[0].type == property.returnType | ||
&& !property.visibility.isPublicAPI | ||
&& this.visibility.isPublicAPI | ||
} | ||
|
Oops, something went wrong.