Java to Kotlin converter: refactoring and fixing switch conversion

#KT-5390 Fixed
This commit is contained in:
Valentin Kipyatkov
2014-07-04 23:29:52 +04:00
parent 6bd5a12498
commit 3ccd8839ff
14 changed files with 164 additions and 146 deletions
@@ -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")
+5 -15
View File
@@ -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()
}
}
+2 -6
View File
@@ -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")
}
}
}
+13 -39
View File
@@ -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)
}
+3 -9
View File
@@ -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
}
}
}