Java to Kotlin converter: refactoring and fixing switch conversion
#KT-5390 Fixed
This commit is contained in:
@@ -157,14 +157,9 @@ class WhenStatement(val subject: Expression, val caseContainers: List<WhenEntry>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WhenEntry(val selectors: List<WhenEntrySelector>, statements: List<Statement>) : Statement() {
|
class WhenEntry(val selectors: List<WhenEntrySelector>, val body: Statement) : Statement() {
|
||||||
private val block = run {
|
|
||||||
val filteredStatements = statements.filterNot { it is BreakStatement || it is ContinueStatement }
|
|
||||||
Block(filteredStatements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype(), true).assignNoPrototype()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun generateCode(builder: CodeBuilder) {
|
override fun generateCode(builder: CodeBuilder) {
|
||||||
builder.append(selectors, ", ").append(" -> ").append(block)
|
builder.append(selectors, ", ").append(" -> ").append(body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -210,38 +210,45 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun switchBodyToWhenEntries(body: PsiCodeBlock?): List<WhenEntry> {
|
private fun switchBodyToWhenEntries(body: PsiCodeBlock?): List<WhenEntry> {
|
||||||
val cases: List<List<PsiElement>> = splitToCases(body)
|
//TODO: this code is to be changed when continue in when is supported by Kotlin
|
||||||
val allSwitchStatements = ArrayList<PsiElement>()
|
|
||||||
if (body != null) {
|
val cases = splitToCases(body)
|
||||||
allSwitchStatements.addAll(body.getStatements())
|
|
||||||
|
fun isSwitchBreak(statement: PsiStatement) = statement is PsiBreakStatement && statement.getLabelIdentifier() == null
|
||||||
|
|
||||||
|
fun convertStatements(statements: List<PsiStatement>): List<Statement>
|
||||||
|
= statements.filterNot(::isSwitchBreak).map { converter.convertStatement(it) }
|
||||||
|
|
||||||
|
fun convertCaseStatements(caseIndex: Int): List<Statement> {
|
||||||
|
val case = cases[caseIndex]
|
||||||
|
return if (case.statements.any { it is PsiBreakStatement || it is PsiContinueStatement || it is PsiReturnStatement || it is PsiThrowStatement } ||
|
||||||
|
caseIndex == cases.lastIndex) {
|
||||||
|
convertStatements(case.statements)
|
||||||
|
}
|
||||||
|
else { // otherwise we fall through into the next case
|
||||||
|
convertStatements(case.statements) + convertCaseStatements(caseIndex + 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun convertCaseStatementsToBody(caseIndex: Int): Statement {
|
||||||
|
val statements = convertCaseStatements(caseIndex)
|
||||||
|
return if (statements.size == 1)
|
||||||
|
statements.single()
|
||||||
|
else
|
||||||
|
Block(statements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype(), true).assignNoPrototype()
|
||||||
|
}
|
||||||
|
|
||||||
val result = ArrayList<WhenEntry>()
|
val result = ArrayList<WhenEntry>()
|
||||||
var pendingSelectors = ArrayList<WhenEntrySelector>()
|
var pendingSelectors = ArrayList<WhenEntrySelector>()
|
||||||
var i = 0
|
for ((i, case) in cases.withIndices()) {
|
||||||
var hasDefaultCase = false
|
if (case.label == null) { // invalid switch - no case labels
|
||||||
for (ls in cases) {
|
result.add(WhenEntry(listOf(ValueWhenEntrySelector(Expression.Empty).assignNoPrototype()), convertCaseStatementsToBody(i)).assignNoPrototype())
|
||||||
if (ls.isNotEmpty()) {
|
continue
|
||||||
val label = ls[0] as PsiSwitchLabelStatement
|
}
|
||||||
hasDefaultCase = hasDefaultCase || label.isDefaultCase()
|
pendingSelectors.add(converter.convertStatement(case.label) as WhenEntrySelector)
|
||||||
// TODO assert {(label is PsiSwitchLabelStatement?)}
|
if (case.statements.isNotEmpty()) {
|
||||||
// TODO assert("not a right index") {allSwitchStatements?.get(i) == label}
|
result.add(WhenEntry(pendingSelectors, convertCaseStatementsToBody(i)).assignNoPrototype())
|
||||||
pendingSelectors.add(converter.convertStatement(label) as WhenEntrySelector)
|
pendingSelectors = ArrayList()
|
||||||
if (ls.size() > 1) {
|
|
||||||
val slice = ls.drop(1)
|
|
||||||
|
|
||||||
fun convertStatements(elements: List<PsiElement>): List<Statement>
|
|
||||||
= elements.map { if (it is PsiStatement) converter.convertStatement(it) else null }.filterNotNull()
|
|
||||||
|
|
||||||
if (!containsBreak(slice)) {
|
|
||||||
val statements = convertStatements(slice) + convertStatements(getAllToNextBreak(allSwitchStatements, i + ls.size()))
|
|
||||||
result.add(WhenEntry(pendingSelectors, statements).assignNoPrototype())
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
result.add(WhenEntry(pendingSelectors, convertStatements(slice)).assignNoPrototype())
|
|
||||||
}
|
|
||||||
pendingSelectors = ArrayList()
|
|
||||||
}
|
|
||||||
i += ls.size()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@@ -359,44 +366,28 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
|
|||||||
(this is PsiPrefixExpression && this.getOperationTokenType() == JavaTokenType.PLUSPLUS)
|
(this is PsiPrefixExpression && this.getOperationTokenType() == JavaTokenType.PLUSPLUS)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun containsBreak(slice: List<PsiElement?>) = slice.any { it is PsiBreakStatement }
|
private data class SwitchCase(val label: PsiSwitchLabelStatement?, val statements: List<PsiStatement>)
|
||||||
|
|
||||||
private fun getAllToNextBreak(allStatements: List<PsiElement>, start: Int): List<PsiElement> {
|
private fun splitToCases(body: PsiCodeBlock?): List<SwitchCase> {
|
||||||
val result = ArrayList<PsiElement>()
|
val cases = ArrayList<SwitchCase>()
|
||||||
for (i in start..allStatements.size() - 1) {
|
var currentCaseStatements = ArrayList<PsiStatement>()
|
||||||
val s = allStatements[i]
|
|
||||||
if (s is PsiBreakStatement || s is PsiReturnStatement) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s !is PsiSwitchLabelStatement) {
|
|
||||||
result.add(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun splitToCases(body: PsiCodeBlock?): List<List<PsiElement>> {
|
|
||||||
val cases = ArrayList<List<PsiElement>>()
|
|
||||||
var currentCaseStatements = ArrayList<PsiElement>()
|
|
||||||
if (body != null) {
|
if (body != null) {
|
||||||
var isFirst = true
|
var label: PsiSwitchLabelStatement? = null
|
||||||
for (s in body.getChildren()) {
|
for (statement in body.getStatements()) {
|
||||||
if (s !is PsiStatement && s !is PsiComment) continue
|
if (statement is PsiSwitchLabelStatement) {
|
||||||
if (s is PsiSwitchLabelStatement) {
|
if (label != null) {
|
||||||
if (isFirst) {
|
cases.add(SwitchCase(label, currentCaseStatements))
|
||||||
isFirst = false
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cases.add(currentCaseStatements)
|
|
||||||
currentCaseStatements = ArrayList()
|
currentCaseStatements = ArrayList()
|
||||||
}
|
}
|
||||||
|
label = statement
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentCaseStatements.add(statement)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentCaseStatements.add(s)
|
|
||||||
}
|
}
|
||||||
cases.add(currentCaseStatements)
|
if (label != null || currentCaseStatements.isNotEmpty()) {
|
||||||
|
cases.add(SwitchCase(label, currentCaseStatements))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cases
|
return cases
|
||||||
|
|||||||
@@ -2601,6 +2601,11 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
|
|||||||
doTest("j2k/tests/testData/ast/switch/comlicatedFallDown.java");
|
doTest("j2k/tests/testData/ast/switch/comlicatedFallDown.java");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("continueAndBreakWithLabel.java")
|
||||||
|
public void testContinueAndBreakWithLabel() throws Exception {
|
||||||
|
doTest("j2k/tests/testData/ast/switch/continueAndBreakWithLabel.java");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("emptySwitch.java")
|
@TestMetadata("emptySwitch.java")
|
||||||
public void testEmptySwitch() throws Exception {
|
public void testEmptySwitch() throws Exception {
|
||||||
doTest("j2k/tests/testData/ast/switch/emptySwitch.java");
|
doTest("j2k/tests/testData/ast/switch/emptySwitch.java");
|
||||||
@@ -2611,6 +2616,11 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
|
|||||||
doTest("j2k/tests/testData/ast/switch/fallDown.java");
|
doTest("j2k/tests/testData/ast/switch/fallDown.java");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("invalidSwitch.java")
|
||||||
|
public void testInvalidSwitch() throws Exception {
|
||||||
|
doTest("j2k/tests/testData/ast/switch/invalidSwitch.java");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kt-539.java")
|
@TestMetadata("kt-539.java")
|
||||||
public void testKt_539() throws Exception {
|
public void testKt_539() throws Exception {
|
||||||
doTest("j2k/tests/testData/ast/switch/kt-539.java");
|
doTest("j2k/tests/testData/ast/switch/kt-539.java");
|
||||||
@@ -2621,6 +2631,11 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
|
|||||||
doTest("j2k/tests/testData/ast/switch/nondefault.java");
|
doTest("j2k/tests/testData/ast/switch/nondefault.java");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("returnsAndThrows.java")
|
||||||
|
public void testReturnsAndThrows() throws Exception {
|
||||||
|
doTest("j2k/tests/testData/ast/switch/returnsAndThrows.java");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("j2k/tests/testData/ast/synchronizedStatement")
|
@TestMetadata("j2k/tests/testData/ast/synchronizedStatement")
|
||||||
|
|||||||
@@ -2,21 +2,11 @@ class Test {
|
|||||||
class object {
|
class object {
|
||||||
public fun getInt(i: Int): Int {
|
public fun getInt(i: Int): Int {
|
||||||
when (i) {
|
when (i) {
|
||||||
0 -> {
|
0 -> return 0
|
||||||
return 0
|
1 -> return 1
|
||||||
}
|
2 -> return 2
|
||||||
1 -> {
|
3 -> return 3
|
||||||
return 1
|
else -> return -1
|
||||||
}
|
|
||||||
2 -> {
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
3 -> {
|
|
||||||
return 3
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,9 +31,7 @@ public class SwitchDemo {
|
|||||||
print(4)
|
print(4)
|
||||||
print(5)
|
print(5)
|
||||||
}
|
}
|
||||||
5 -> {
|
5 -> print(5)
|
||||||
print(5)
|
|
||||||
}
|
|
||||||
6 -> {
|
6 -> {
|
||||||
print(6)
|
print(6)
|
||||||
print(7)
|
print(7)
|
||||||
@@ -73,12 +71,8 @@ public class SwitchDemo {
|
|||||||
print(11)
|
print(11)
|
||||||
monthString = "December"
|
monthString = "December"
|
||||||
}
|
}
|
||||||
12 -> {
|
12 -> monthString = "December"
|
||||||
monthString = "December"
|
else -> monthString = "Invalid month"
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
monthString = "Invalid month"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
System.out.println(monthString)
|
System.out.println(monthString)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
//method
|
||||||
|
void foo() {
|
||||||
|
Loop:
|
||||||
|
while(true) {
|
||||||
|
switch(take()) {
|
||||||
|
case 1: continue;
|
||||||
|
case 2: System.out.println("2"); return;
|
||||||
|
case 3: break Loop;
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
fun foo() {
|
||||||
|
@Loop while (true) {
|
||||||
|
when (take()) {
|
||||||
|
1 -> continue
|
||||||
|
2 -> {
|
||||||
|
System.out.println("2")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
3 -> break@Loop
|
||||||
|
}
|
||||||
|
System.out.println()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,12 +6,8 @@ public class SwitchDemo {
|
|||||||
val month = 8
|
val month = 8
|
||||||
val monthString: String
|
val monthString: String
|
||||||
when (month) {
|
when (month) {
|
||||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 -> {
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 -> monthString = "December"
|
||||||
monthString = "December"
|
else -> monthString = "Invalid month"
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
monthString = "Invalid month"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
System.out.println(monthString)
|
System.out.println(monthString)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
//method
|
||||||
|
void foo() {
|
||||||
|
switch(a) {
|
||||||
|
System.out.println("1")
|
||||||
|
System.out.println("2")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fun foo() {
|
||||||
|
when (a) {
|
||||||
|
-> {
|
||||||
|
System.out.println("1")
|
||||||
|
System.out.println("2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,45 +7,19 @@ public class SwitchDemo {
|
|||||||
val month = 8
|
val month = 8
|
||||||
val monthString: String
|
val monthString: String
|
||||||
when (month) {
|
when (month) {
|
||||||
1 -> {
|
1 -> monthString = "January"
|
||||||
monthString = "January"
|
2 -> monthString = "February"
|
||||||
}
|
3 -> monthString = "March"
|
||||||
2 -> {
|
4 -> monthString = "April"
|
||||||
monthString = "February"
|
5 -> monthString = "May"
|
||||||
}
|
6 -> monthString = "June"
|
||||||
3 -> {
|
7 -> monthString = "July"
|
||||||
monthString = "March"
|
8 -> monthString = "August"
|
||||||
}
|
9 -> monthString = "September"
|
||||||
4 -> {
|
10 -> monthString = "October"
|
||||||
monthString = "April"
|
11 -> monthString = "November"
|
||||||
}
|
12 -> monthString = "December"
|
||||||
5 -> {
|
else -> monthString = "Invalid month"
|
||||||
monthString = "May"
|
|
||||||
}
|
|
||||||
6 -> {
|
|
||||||
monthString = "June"
|
|
||||||
}
|
|
||||||
7 -> {
|
|
||||||
monthString = "July"
|
|
||||||
}
|
|
||||||
8 -> {
|
|
||||||
monthString = "August"
|
|
||||||
}
|
|
||||||
9 -> {
|
|
||||||
monthString = "September"
|
|
||||||
}
|
|
||||||
10 -> {
|
|
||||||
monthString = "October"
|
|
||||||
}
|
|
||||||
11 -> {
|
|
||||||
monthString = "November"
|
|
||||||
}
|
|
||||||
12 -> {
|
|
||||||
monthString = "December"
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
monthString = "Invalid month"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
System.out.println(monthString)
|
System.out.println(monthString)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,9 @@ public class NonDefault {
|
|||||||
val value = 3
|
val value = 3
|
||||||
var valueString = ""
|
var valueString = ""
|
||||||
when (value) {
|
when (value) {
|
||||||
1 -> {
|
1 -> valueString = "ONE"
|
||||||
valueString = "ONE"
|
2 -> valueString = "TWO"
|
||||||
}
|
3 -> valueString = "THREE"
|
||||||
2 -> {
|
|
||||||
valueString = "TWO"
|
|
||||||
}
|
|
||||||
3 -> {
|
|
||||||
valueString = "THREE"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
System.out.println(valueString)
|
System.out.println(valueString)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
//method
|
||||||
|
int foo(int a) {
|
||||||
|
switch(a) {
|
||||||
|
case 1: System.out.println("1"); return 1;
|
||||||
|
case 2: System.out.println("2"); return 2;
|
||||||
|
case 3: System.out.println("3"); throw new RuntimeException();
|
||||||
|
default: System.out.println("default"); return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
fun foo(a: Int): Int {
|
||||||
|
when (a) {
|
||||||
|
1 -> {
|
||||||
|
System.out.println("1")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
System.out.println("2")
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
3 -> {
|
||||||
|
System.out.println("3")
|
||||||
|
throw RuntimeException()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
System.out.println("default")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user