1 // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
3 package org.jetbrains.kotlin.nj2k.conversions
6 import org.jetbrains.annotations.NonNls
7 import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
8 import org.jetbrains.kotlin.nj2k.tree.*
9 import java.math.BigInteger
11 class LiteralConversion(context: NewJ2kConverterContext) : RecursiveApplicableConversionBase(context) {
12 override fun applyToElement(element: JKTreeElement): JKTreeElement {
13 if (element !is JKLiteralExpression) return recurse(element)
15 val convertedElement = try {
16 element.apply { convertLiteral() }
17 } catch (_: NumberFormatException) {
18 createTodoCall(cannotConvertLiteralMessage(element))
21 return recurse(convertedElement)
24 private fun createTodoCall(@NonNls message: String): JKCallExpressionImpl {
25 val todoMethodSymbol = symbolProvider.provideMethodSymbol("kotlin.TODO")
26 val todoMessageArgument = JKArgumentImpl(JKLiteralExpression("\"$message\"", JKLiteralExpression.LiteralType.STRING))
28 return JKCallExpressionImpl(todoMethodSymbol, JKArgumentList(todoMessageArgument))
31 private fun cannotConvertLiteralMessage(element: JKLiteralExpression): String {
32 val literalType = element.type.toString().toLowerCase()
33 val literalValue = element.literal
34 return "Could not convert $literalType literal '$literalValue' to Kotlin"
37 private fun JKLiteralExpression.convertLiteral() {
38 literal = when (type) {
39 JKLiteralExpression.LiteralType.DOUBLE -> toDoubleLiteral()
40 JKLiteralExpression.LiteralType.FLOAT -> toFloatLiteral()
41 JKLiteralExpression.LiteralType.LONG -> toLongLiteral()
42 JKLiteralExpression.LiteralType.INT -> toIntLiteral()
43 JKLiteralExpression.LiteralType.CHAR -> convertCharLiteral()
44 JKLiteralExpression.LiteralType.STRING -> toStringLiteral()
49 private fun JKLiteralExpression.toDoubleLiteral() =
50 literal.cleanFloatAndDoubleLiterals().let { text ->
51 if (!text.contains(".") && !text.contains("e", true))
55 if (text.endsWith(".")) "${text}0" else text
59 private fun JKLiteralExpression.toFloatLiteral() =
60 literal.cleanFloatAndDoubleLiterals().let { text ->
61 if (!text.endsWith("f")) "${text}f"
65 private fun JKLiteralExpression.toStringLiteral() =
66 literal.replace("""((?:\\)*)\\([0-3]?[0-7]{1,2})""".toRegex()) { matchResult ->
67 val leadingBackslashes = matchResult.groupValues[1]
68 if (leadingBackslashes.length % 2 == 0)
69 String.format("%s\\u%04x", leadingBackslashes, Integer.parseInt(matchResult.groupValues[2], 8))
70 else matchResult.value
71 }.replace("""\$([A-Za-z]+|\{)""".toRegex(), "\\\\$0")
72 .replace( "\\f", "\\u000c")
75 private fun JKLiteralExpression.convertCharLiteral() =
76 literal.replace("""\\([0-3]?[0-7]{1,2})""".toRegex()) {
77 String.format("\\u%04x", Integer.parseInt(it.groupValues[1], 8))
81 private fun JKLiteralExpression.toIntLiteral() =
83 .cleanIntAndLongLiterals()
84 .convertHexLiteral(isLongLiteral = false)
85 .convertBinaryLiteral(isLongLiteral = false)
86 .convertOctalLiteral(isLongLiteral = false)
89 private fun JKLiteralExpression.toLongLiteral() =
91 .cleanIntAndLongLiterals()
92 .convertHexLiteral(isLongLiteral = true)
93 .convertBinaryLiteral(isLongLiteral = true)
94 .convertOctalLiteral(isLongLiteral = true) + "L"
96 private fun String.convertHexLiteral(isLongLiteral: Boolean): String {
97 if (!startsWith("0x", ignoreCase = true)) return this
98 val value = BigInteger(drop(2), 16)
100 isLongLiteral && value.bitLength() > 63 ->
101 "-0x${value.toLong().toString(16).substring(1)}"
103 !isLongLiteral && value.bitLength() > 31 ->
104 "-0x${value.toInt().toString(16).substring(1)}"
110 private fun String.convertBinaryLiteral(isLongLiteral: Boolean): String {
111 if (!startsWith("0b", ignoreCase = true)) return this
112 val value = BigInteger(drop(2), 2)
113 return if (isLongLiteral) value.toLong().toString(10) else value.toInt().toString()
116 private fun String.convertOctalLiteral(isLongLiteral: Boolean): String {
117 if (!startsWith("0") || length == 1 || get(1).toLowerCase() == 'x') return this
118 val value = BigInteger(drop(1), 8)
119 return if (isLongLiteral) value.toLong().toString(10) else value.toInt().toString(10)
122 private fun String.cleanFloatAndDoubleLiterals() =
123 replace("L", "", ignoreCase = true)
124 .replace("d", "", ignoreCase = true)
125 .replace(".e", "e", ignoreCase = true)
126 .replace(".f", "", ignoreCase = true)
127 .replace("f", "", ignoreCase = true)
130 private fun String.cleanIntAndLongLiterals() =
131 replace("l", "", ignoreCase = true)