New J2K: Add support of more operations on string conversion
This commit is contained in:
committed by
Ilya Kirillov
parent
e39fc53f25
commit
8caf45820c
@@ -5,10 +5,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.nj2k.conversions
|
||||
|
||||
import org.jetbrains.kotlin.nj2k.ConversionContext
|
||||
import org.jetbrains.kotlin.nj2k.parenthesizeIfBinaryExpression
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.nj2k.*
|
||||
import org.jetbrains.kotlin.nj2k.tree.*
|
||||
import org.jetbrains.kotlin.nj2k.tree.impl.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class BuiltinMembersConversion(private val context: ConversionContext) : RecursiveApplicableConversionBase() {
|
||||
override fun applyToElement(element: JKTreeElement): JKTreeElement {
|
||||
@@ -113,11 +115,18 @@ class BuiltinMembersConversion(private val context: ConversionContext) : Recursi
|
||||
}
|
||||
}
|
||||
|
||||
private inner class CustomExpressionBuilder(
|
||||
val builder: (JKExpression) -> JKExpression
|
||||
) : ResultBuilder {
|
||||
override fun build(from: JKExpression): JKExpression = builder(from)
|
||||
}
|
||||
|
||||
private fun Conversion.createBuilder(): ResultBuilder =
|
||||
when (to) {
|
||||
is Method -> MethodBuilder(to.fqName, argumentsProvider ?: { it })
|
||||
is Field -> FieldBuilder(to.fqName)
|
||||
is ExtensionMethod -> ExtensionMethodBuilder(to.fqName)
|
||||
is CustomExpression -> CustomExpressionBuilder(to.expressionBuilder)
|
||||
else -> error("Bad conversion")
|
||||
}
|
||||
|
||||
@@ -126,23 +135,27 @@ class BuiltinMembersConversion(private val context: ConversionContext) : Recursi
|
||||
REPLACE_SELECTOR, FULL_REPLACE
|
||||
}
|
||||
|
||||
private interface Info {
|
||||
|
||||
private interface Info
|
||||
private interface SymbolInfo : Info {
|
||||
val fqName: String
|
||||
}
|
||||
|
||||
private data class Method(override val fqName: String) : Info
|
||||
private data class Field(override val fqName: String) : Info
|
||||
private data class ExtensionMethod(override val fqName: String) : Info
|
||||
private data class Method(override val fqName: String) : SymbolInfo
|
||||
private data class Field(override val fqName: String) : SymbolInfo
|
||||
private data class ExtensionMethod(override val fqName: String) : SymbolInfo
|
||||
private data class CustomExpression(val expressionBuilder: (JKExpression) -> JKExpression) : Info
|
||||
|
||||
private data class Conversion(
|
||||
val from: Info,
|
||||
val from: SymbolInfo,
|
||||
val to: Info,
|
||||
val replaceType: ReplaceType = ReplaceType.REPLACE_SELECTOR,
|
||||
val byArgumentsFilter: ((List<JKExpression>) -> Boolean)? = null,
|
||||
val argumentsProvider: ((JKExpressionList) -> JKExpressionList)? = null
|
||||
val argumentsProvider: ((JKArgumentList) -> JKArgumentList)? = null,
|
||||
val actionAfter: ((JKExpression) -> JKExpression)? = null
|
||||
)
|
||||
|
||||
private infix fun Info.convertTo(to: Info) =
|
||||
private infix fun SymbolInfo.convertTo(to: Info) =
|
||||
Conversion(this, to)
|
||||
|
||||
private infix fun Conversion.withReplaceType(replaceType: ReplaceType) =
|
||||
@@ -151,9 +164,12 @@ class BuiltinMembersConversion(private val context: ConversionContext) : Recursi
|
||||
private infix fun Conversion.withByArgumentsFilter(filter: (List<JKExpression>) -> Boolean) =
|
||||
copy(byArgumentsFilter = filter)
|
||||
|
||||
private infix fun Conversion.withArgumentsProvider(argumentsProvider: (JKExpressionList) -> JKExpressionList) =
|
||||
private infix fun Conversion.withArgumentsProvider(argumentsProvider: (JKArgumentList) -> JKArgumentList) =
|
||||
copy(argumentsProvider = argumentsProvider)
|
||||
|
||||
private infix fun Conversion.andAfter(actionAfter: (JKExpression) -> JKExpression) =
|
||||
copy(actionAfter = actionAfter)
|
||||
|
||||
private val conversions: Map<String, List<Conversion>> =
|
||||
listOf(
|
||||
Method("java.lang.Integer.intValue") convertTo Method("kotlin.Int.toInt"),//TODO do not list all variants
|
||||
@@ -166,6 +182,17 @@ class BuiltinMembersConversion(private val context: ConversionContext) : Recursi
|
||||
Method("java.util.Map.values") convertTo Field("kotlin.collections.Map.values"),
|
||||
Method("java.util.Collection.size") convertTo Field("kotlin.collections.Collection.size"),
|
||||
Method("java.util.Collection.remove") convertTo Method("kotlin.collections.MutableCollection.remove"),
|
||||
Method("java.util.Collection.toArray")
|
||||
convertTo Method("kotlin.collections.toTypedArray")
|
||||
withByArgumentsFilter { it.isEmpty() },
|
||||
Method("java.util.Collection.toArray")
|
||||
convertTo Method("kotlin.collections.toTypedArray")
|
||||
withByArgumentsFilter {
|
||||
it.singleOrNull()?.let { parameter ->
|
||||
parameter.safeAs<JKMethodCallExpression>()?.identifier?.fqName == "kotlin.arrayOfNulls"
|
||||
} == true
|
||||
} withArgumentsProvider { JKArgumentListImpl() },
|
||||
|
||||
Method("java.util.List.remove") convertTo Method("kotlin.collections.MutableCollection.removeAt"),
|
||||
Method("java.util.Map.Entry.getKey") convertTo Field("kotlin.collections.Map.Entry.key"),
|
||||
Method("java.util.Map.Entry.getValue") convertTo Field("kotlin.collections.Map.Entry.value"),
|
||||
@@ -178,11 +205,148 @@ class BuiltinMembersConversion(private val context: ConversionContext) : Recursi
|
||||
|
||||
Method("java.lang.CharSequence.length") convertTo Field("kotlin.String.length"),
|
||||
Method("java.lang.CharSequence.charAt") convertTo Method("kotlin.String.get"),
|
||||
Method("java.lang.String.indexOf") convertTo Method("kotlin.text.indexOf"),
|
||||
Method("java.lang.String.lastIndexOf") convertTo Method("kotlin.text.lastIndexOf"),
|
||||
Method("java.lang.String.getBytes") convertTo Method("kotlin.text.toByteArray"),
|
||||
Method("java.lang.String.valueOf")
|
||||
convertTo ExtensionMethod("kotlin.Any.toString")
|
||||
withReplaceType ReplaceType.FULL_REPLACE
|
||||
withByArgumentsFilter { it.isNotEmpty() && it.first().type(context.symbolProvider)?.isArrayType() == false },
|
||||
|
||||
Method("java.lang.String.valueOf")
|
||||
convertTo Method("kotlin.String")
|
||||
withReplaceType ReplaceType.FULL_REPLACE
|
||||
withByArgumentsFilter { it.isNotEmpty() && it.first().type(context.symbolProvider)?.isArrayType() == true },
|
||||
|
||||
Method("java.lang.String.copyValueOf")
|
||||
convertTo Method("kotlin.String")
|
||||
withReplaceType ReplaceType.FULL_REPLACE
|
||||
withByArgumentsFilter { it.isNotEmpty() && it.first().type(context.symbolProvider)?.isArrayType() == true },
|
||||
|
||||
Method("java.lang.String.replaceAll")
|
||||
convertTo Method("kotlin.text.replace")
|
||||
withArgumentsProvider { arguments ->
|
||||
val detachedArguments = arguments::arguments.detached()
|
||||
val first =
|
||||
detachedArguments.first()::value.detached().callOn(
|
||||
context.symbolProvider.provideByFqName(
|
||||
"kotlin.text.toRegex",
|
||||
multiResolve = true
|
||||
)
|
||||
)
|
||||
JKArgumentListImpl(listOf(JKArgumentImpl(first)) + detachedArguments.drop(1))
|
||||
},
|
||||
Method("java.lang.String.replaceFirst")
|
||||
convertTo Method("kotlin.text.replaceFirst")
|
||||
withArgumentsProvider { arguments ->
|
||||
val detachedArguments = arguments::arguments.detached()
|
||||
val first =
|
||||
detachedArguments.first()::value.detached().callOn(
|
||||
context.symbolProvider.provideByFqName(
|
||||
"kotlin.text.toRegex",
|
||||
multiResolve = true
|
||||
)
|
||||
)
|
||||
JKArgumentListImpl(listOf(JKArgumentImpl(first)) + detachedArguments.drop(1))
|
||||
},
|
||||
Method("java.lang.String.equalsIgnoreCase")
|
||||
convertTo Method("kotlin.text.equals")
|
||||
withArgumentsProvider { arguments ->
|
||||
JKArgumentListImpl(
|
||||
arguments::arguments.detached() + JKNamedArgumentImpl(
|
||||
JKBooleanLiteral(true),
|
||||
JKNameIdentifierImpl("ignoreCase")
|
||||
)
|
||||
)
|
||||
},
|
||||
|
||||
Method("java.lang.String.compareToIgnoreCase")
|
||||
convertTo Method("kotlin.text.compareTo")
|
||||
withArgumentsProvider { arguments ->
|
||||
JKArgumentListImpl(
|
||||
arguments::arguments.detached() + JKNamedArgumentImpl(
|
||||
JKBooleanLiteral(true),
|
||||
JKNameIdentifierImpl("ignoreCase")
|
||||
)
|
||||
)
|
||||
},
|
||||
|
||||
|
||||
Method("java.lang.String.split")
|
||||
convertTo Method("kotlin.text.split")
|
||||
withByArgumentsFilter { it.size == 2 }
|
||||
andAfter { expression ->
|
||||
val arguments =
|
||||
expression.cast<JKQualifiedExpression>()
|
||||
.selector.cast<JKMethodCallExpression>()
|
||||
.arguments
|
||||
val limitArgument = arguments.arguments[1].value
|
||||
val limit = limitArgument.asLiteralTextWithPrefix()?.toIntOrNull()
|
||||
when {
|
||||
limit != null -> {
|
||||
if (limit > 0) expression
|
||||
else expression
|
||||
.also {
|
||||
arguments.arguments = arguments.arguments.dropLast(1)
|
||||
}.let {
|
||||
it.callOn(
|
||||
context.symbolProvider.provideByFqName("kotlin.collections.dropLastWhile", multiResolve = true),
|
||||
JKLambdaExpressionImpl(
|
||||
JKFieldAccessExpressionImpl(
|
||||
JKUnresolvedField(//TODO replace with `it` parameter
|
||||
"it",
|
||||
context.symbolProvider
|
||||
)
|
||||
).callOn(
|
||||
context.symbolProvider.provideByFqName(
|
||||
"kotlin.text.isEmpty",
|
||||
multiResolve = true
|
||||
)
|
||||
).asStatement(),
|
||||
emptyList()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> expression.also {
|
||||
val lastArgument = arguments.arguments.last().value.copyTreeAndDetach()
|
||||
.callOn(
|
||||
context.symbolProvider.provideByFqName("kotlin.ranges.coerceAtLeast"),
|
||||
JKKtLiteralExpressionImpl("0", JKLiteralExpression.LiteralType.INT)
|
||||
)
|
||||
arguments.arguments = arguments.arguments.dropLast(1) + JKArgumentImpl(lastArgument)
|
||||
}
|
||||
}.castToTypedArray()
|
||||
},
|
||||
|
||||
|
||||
Method("java.lang.String.split")
|
||||
convertTo Method("kotlin.text.split")
|
||||
andAfter { it.castToTypedArray() },
|
||||
|
||||
Method("java.lang.String.trim")
|
||||
convertTo Method("kotlin.text.trim")
|
||||
withArgumentsProvider {
|
||||
JKArgumentListImpl(
|
||||
JKLambdaExpressionImpl(
|
||||
JKExpressionStatementImpl(
|
||||
kotlinBinaryExpression(
|
||||
JKFieldAccessExpressionImpl(
|
||||
JKUnresolvedField(//TODO replace with `it` parameter
|
||||
"it",
|
||||
context.symbolProvider
|
||||
)
|
||||
),
|
||||
JKKtLiteralExpressionImpl("' '", JKLiteralExpression.LiteralType.CHAR),
|
||||
KtTokens.LTEQ,
|
||||
context.symbolProvider
|
||||
)
|
||||
),
|
||||
emptyList()
|
||||
)
|
||||
)
|
||||
},
|
||||
|
||||
|
||||
Method("java.util.Collections.singletonList") convertTo Method("kotlin.collections.listOf")
|
||||
withReplaceType ReplaceType.FULL_REPLACE,
|
||||
@@ -195,4 +359,26 @@ class BuiltinMembersConversion(private val context: ConversionContext) : Recursi
|
||||
Method("java.util.Collections.emptyMap")
|
||||
convertTo Method("kotlin.collections.emptyMap") withReplaceType ReplaceType.FULL_REPLACE
|
||||
).groupBy { it.from.fqName }
|
||||
|
||||
|
||||
private fun JKExpression.callOn(symbol: JKMethodSymbol, arguments: List<JKArgument> = emptyList()) =
|
||||
JKQualifiedExpressionImpl(
|
||||
this,
|
||||
JKKtQualifierImpl.DOT,
|
||||
JKKtCallExpressionImpl(
|
||||
symbol,
|
||||
JKArgumentListImpl(arguments),
|
||||
JKTypeArgumentListImpl()
|
||||
)
|
||||
)
|
||||
|
||||
private fun JKExpression.callOn(symbol: JKMethodSymbol, vararg arguments: JKArgument) =
|
||||
callOn(symbol, arguments.toList())
|
||||
|
||||
private fun JKExpression.callOn(symbol: JKMethodSymbol, vararg arguments: JKExpression) =
|
||||
callOn(symbol, arguments.map { JKArgumentImpl(it) })
|
||||
|
||||
|
||||
private fun JKExpression.castToTypedArray() =
|
||||
callOn(context.symbolProvider.provideByFqName("kotlin.collections.toTypedArray", multiResolve = true))
|
||||
}
|
||||
|
||||
@@ -46,8 +46,7 @@ class ImplicitCastsConversion(private val context: ConversionContext) : Recursiv
|
||||
} else this
|
||||
}
|
||||
|
||||
fun JKBinaryExpression.convertComparationExpression(): JKBinaryExpression {
|
||||
if (!operator.isComparationOperator()) return this
|
||||
fun JKBinaryExpression.convertBinaryOperationWithChar(): JKBinaryExpression {
|
||||
val leftType = left.type(context.symbolProvider)?.asPrimitiveType() ?: return this
|
||||
val rightType = right.type(context.symbolProvider)?.asPrimitiveType() ?: return this
|
||||
|
||||
@@ -67,7 +66,6 @@ class ImplicitCastsConversion(private val context: ConversionContext) : Recursiv
|
||||
|
||||
)
|
||||
}
|
||||
println()
|
||||
|
||||
return when {
|
||||
leftType.jvmPrimitiveType == rightType.jvmPrimitiveType -> this
|
||||
@@ -80,7 +78,7 @@ class ImplicitCastsConversion(private val context: ConversionContext) : Recursiv
|
||||
}
|
||||
}
|
||||
|
||||
return binaryExpression.convertComparationExpression().addBangBang()
|
||||
return binaryExpression.convertBinaryOperationWithChar().addBangBang()
|
||||
}
|
||||
|
||||
private fun convertVariable(variable: JKVariable) {
|
||||
@@ -136,6 +134,22 @@ class ImplicitCastsConversion(private val context: ConversionContext) : Recursiv
|
||||
return null
|
||||
}
|
||||
|
||||
private fun JKExpression.castStringToRegex(toType: JKType): JKExpression? {
|
||||
if (toType.safeAs<JKClassType>()?.classReference?.fqName != "java.util.regex.Pattern") return null
|
||||
val expressionType = type(context.symbolProvider) ?: return null
|
||||
if (!expressionType.isStringType()) return null
|
||||
return JKQualifiedExpressionImpl(
|
||||
copyTreeAndDetach().parenthesizeIfBinaryExpression(),
|
||||
JKKtQualifierImpl.DOT,
|
||||
JKKtCallExpressionImpl(
|
||||
context.symbolProvider.provideByFqName("kotlin.text.toRegex", multiResolve = true),
|
||||
JKArgumentListImpl(),
|
||||
JKTypeArgumentListImpl()
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
private fun JKExpression.castToAsPrimitiveTypes(toType: JKType, strict: Boolean): JKExpression? {
|
||||
if (this is JKPrefixExpression
|
||||
&& (operator.token.text == "+" || operator.token.text == "-")
|
||||
@@ -181,6 +195,7 @@ class ImplicitCastsConversion(private val context: ConversionContext) : Recursiv
|
||||
val expressionType = type(context.symbolProvider)
|
||||
if (expressionType == toType) return null
|
||||
castToAsPrimitiveTypes(toType, strict)?.also { return it }
|
||||
castStringToRegex(toType)?.also { return it }
|
||||
return addBangBang(toType)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user