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() {
|
||||
private val block = run {
|
||||
val filteredStatements = statements.filterNot { it is BreakStatement || it is ContinueStatement }
|
||||
Block(filteredStatements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype(), true).assignNoPrototype()
|
||||
}
|
||||
|
||||
class WhenEntry(val selectors: List<WhenEntrySelector>, val body: Statement) : Statement() {
|
||||
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> {
|
||||
val cases: List<List<PsiElement>> = splitToCases(body)
|
||||
val allSwitchStatements = ArrayList<PsiElement>()
|
||||
if (body != null) {
|
||||
allSwitchStatements.addAll(body.getStatements())
|
||||
//TODO: this code is to be changed when continue in when is supported by Kotlin
|
||||
|
||||
val cases = splitToCases(body)
|
||||
|
||||
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>()
|
||||
var pendingSelectors = ArrayList<WhenEntrySelector>()
|
||||
var i = 0
|
||||
var hasDefaultCase = false
|
||||
for (ls in cases) {
|
||||
if (ls.isNotEmpty()) {
|
||||
val label = ls[0] as PsiSwitchLabelStatement
|
||||
hasDefaultCase = hasDefaultCase || label.isDefaultCase()
|
||||
// TODO assert {(label is PsiSwitchLabelStatement?)}
|
||||
// TODO assert("not a right index") {allSwitchStatements?.get(i) == label}
|
||||
pendingSelectors.add(converter.convertStatement(label) as WhenEntrySelector)
|
||||
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()
|
||||
for ((i, case) in cases.withIndices()) {
|
||||
if (case.label == null) { // invalid switch - no case labels
|
||||
result.add(WhenEntry(listOf(ValueWhenEntrySelector(Expression.Empty).assignNoPrototype()), convertCaseStatementsToBody(i)).assignNoPrototype())
|
||||
continue
|
||||
}
|
||||
pendingSelectors.add(converter.convertStatement(case.label) as WhenEntrySelector)
|
||||
if (case.statements.isNotEmpty()) {
|
||||
result.add(WhenEntry(pendingSelectors, convertCaseStatementsToBody(i)).assignNoPrototype())
|
||||
pendingSelectors = ArrayList()
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -359,44 +366,28 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
|
||||
(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> {
|
||||
val result = ArrayList<PsiElement>()
|
||||
for (i in start..allStatements.size() - 1) {
|
||||
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>()
|
||||
private fun splitToCases(body: PsiCodeBlock?): List<SwitchCase> {
|
||||
val cases = ArrayList<SwitchCase>()
|
||||
var currentCaseStatements = ArrayList<PsiStatement>()
|
||||
if (body != null) {
|
||||
var isFirst = true
|
||||
for (s in body.getChildren()) {
|
||||
if (s !is PsiStatement && s !is PsiComment) continue
|
||||
if (s is PsiSwitchLabelStatement) {
|
||||
if (isFirst) {
|
||||
isFirst = false
|
||||
}
|
||||
else {
|
||||
cases.add(currentCaseStatements)
|
||||
var label: PsiSwitchLabelStatement? = null
|
||||
for (statement in body.getStatements()) {
|
||||
if (statement is PsiSwitchLabelStatement) {
|
||||
if (label != null) {
|
||||
cases.add(SwitchCase(label, currentCaseStatements))
|
||||
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
|
||||
|
||||
@@ -2601,6 +2601,11 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
|
||||
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")
|
||||
public void testEmptySwitch() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@TestMetadata("invalidSwitch.java")
|
||||
public void testInvalidSwitch() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/switch/invalidSwitch.java");
|
||||
}
|
||||
|
||||
@TestMetadata("kt-539.java")
|
||||
public void testKt_539() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@TestMetadata("returnsAndThrows.java")
|
||||
public void testReturnsAndThrows() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/switch/returnsAndThrows.java");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("j2k/tests/testData/ast/synchronizedStatement")
|
||||
|
||||
@@ -2,21 +2,11 @@ class Test {
|
||||
class object {
|
||||
public fun getInt(i: Int): Int {
|
||||
when (i) {
|
||||
0 -> {
|
||||
return 0
|
||||
}
|
||||
1 -> {
|
||||
return 1
|
||||
}
|
||||
2 -> {
|
||||
return 2
|
||||
}
|
||||
3 -> {
|
||||
return 3
|
||||
}
|
||||
else -> {
|
||||
return -1
|
||||
}
|
||||
0 -> return 0
|
||||
1 -> return 1
|
||||
2 -> return 2
|
||||
3 -> return 3
|
||||
else -> return -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,7 @@ public class SwitchDemo {
|
||||
print(4)
|
||||
print(5)
|
||||
}
|
||||
5 -> {
|
||||
print(5)
|
||||
}
|
||||
5 -> print(5)
|
||||
6 -> {
|
||||
print(6)
|
||||
print(7)
|
||||
@@ -73,12 +71,8 @@ public class SwitchDemo {
|
||||
print(11)
|
||||
monthString = "December"
|
||||
}
|
||||
12 -> {
|
||||
monthString = "December"
|
||||
}
|
||||
else -> {
|
||||
monthString = "Invalid month"
|
||||
}
|
||||
12 -> monthString = "December"
|
||||
else -> monthString = "Invalid month"
|
||||
}
|
||||
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 monthString: String
|
||||
when (month) {
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 -> {
|
||||
monthString = "December"
|
||||
}
|
||||
else -> {
|
||||
monthString = "Invalid month"
|
||||
}
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 -> monthString = "December"
|
||||
else -> monthString = "Invalid month"
|
||||
}
|
||||
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 monthString: String
|
||||
when (month) {
|
||||
1 -> {
|
||||
monthString = "January"
|
||||
}
|
||||
2 -> {
|
||||
monthString = "February"
|
||||
}
|
||||
3 -> {
|
||||
monthString = "March"
|
||||
}
|
||||
4 -> {
|
||||
monthString = "April"
|
||||
}
|
||||
5 -> {
|
||||
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"
|
||||
}
|
||||
1 -> monthString = "January"
|
||||
2 -> monthString = "February"
|
||||
3 -> monthString = "March"
|
||||
4 -> monthString = "April"
|
||||
5 -> 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)
|
||||
}
|
||||
|
||||
@@ -5,15 +5,9 @@ public class NonDefault {
|
||||
val value = 3
|
||||
var valueString = ""
|
||||
when (value) {
|
||||
1 -> {
|
||||
valueString = "ONE"
|
||||
}
|
||||
2 -> {
|
||||
valueString = "TWO"
|
||||
}
|
||||
3 -> {
|
||||
valueString = "THREE"
|
||||
}
|
||||
1 -> valueString = "ONE"
|
||||
2 -> valueString = "TWO"
|
||||
3 -> valueString = "THREE"
|
||||
}
|
||||
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