Java to Kotlin converter: better formatting preserving from original code
#KT-4801 Fixed
This commit is contained in:
@@ -348,7 +348,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
|
||||
}
|
||||
dispatcher.expressionVisitor = ExpressionVisitor(this, typeConverter, usageReplacementMap)
|
||||
try {
|
||||
Block(convertStatements(body.getStatements().filter{ !statementsToRemove.contains(it) }), false)
|
||||
convertBlock(body, false, { !statementsToRemove.contains(it) })
|
||||
}
|
||||
finally {
|
||||
dispatcher.expressionVisitor = ExpressionVisitor(this, typeConverter, mapOf())
|
||||
@@ -404,14 +404,12 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
|
||||
return null
|
||||
}
|
||||
|
||||
public fun convertBlock(block: PsiCodeBlock?): Block {
|
||||
public fun convertBlock(block: PsiCodeBlock?, notEmpty: Boolean = true, statementFilter: (PsiStatement) -> Boolean = {true}): Block {
|
||||
if (block == null) return Block.Empty
|
||||
|
||||
return Block(convertStatements(block.getChildren().toList()), true)
|
||||
}
|
||||
|
||||
public fun convertStatements(statements: List<PsiElement>): StatementList {
|
||||
return StatementList(statements.map { if (it is PsiStatement) convertStatement(it) else convertElement(it) })
|
||||
val filteredChildren = block.getChildren().filter { it !is PsiStatement || statementFilter(it) }
|
||||
val statementList = StatementList(filteredChildren.map { if (it is PsiStatement) convertStatement(it) else convertElement(it) })
|
||||
return Block(statementList, notEmpty)
|
||||
}
|
||||
|
||||
public fun convertStatement(statement: PsiStatement?): Statement {
|
||||
|
||||
@@ -78,3 +78,17 @@ fun isQualifierEmptyOrThis(ref: PsiReferenceExpression): Boolean {
|
||||
val qualifier = ref.getQualifierExpression()
|
||||
return qualifier == null || (qualifier is PsiThisExpression && qualifier.getQualifier() == null)
|
||||
}
|
||||
|
||||
fun PsiElement.isInSingleLine(): Boolean {
|
||||
if (this is PsiWhiteSpace) {
|
||||
val text = getText()!!
|
||||
return text.indexOf('\n') < 0 && text.indexOf('\r') < 0
|
||||
}
|
||||
|
||||
var child = getFirstChild()
|
||||
while (child != null) {
|
||||
if (!child!!.isInSingleLine()) return false
|
||||
child = child!!.getNextSibling()
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -27,7 +27,6 @@ fun Block(statements: List<Statement>, notEmpty: Boolean = false): Block {
|
||||
}
|
||||
|
||||
class Block(val statementList: StatementList, val notEmpty: Boolean = false) : Statement() {
|
||||
|
||||
val statements: List<Statement> = statementList.statements
|
||||
|
||||
|
||||
@@ -43,6 +42,6 @@ class Block(val statementList: StatementList, val notEmpty: Boolean = false) : S
|
||||
}
|
||||
|
||||
class object {
|
||||
val Empty = Block(StatementList(ArrayList()))
|
||||
val Empty = Block(StatementList(listOf()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,48 +46,61 @@ class ReturnStatement(val expression: Expression) : Statement() {
|
||||
class IfStatement(
|
||||
val condition: Expression,
|
||||
val thenStatement: Element,
|
||||
val elseStatement: Element
|
||||
val elseStatement: Element,
|
||||
singleLine: Boolean
|
||||
) : Expression() {
|
||||
override fun toKotlin(): String {
|
||||
val result: String = "if (" + condition.toKotlin() + ")\n" + thenStatement.toKotlin()
|
||||
if (!elseStatement.isEmpty) {
|
||||
return result + "\nelse\n" + elseStatement.toKotlin()
|
||||
}
|
||||
private val br = if (singleLine) " " else "\n"
|
||||
|
||||
override fun toKotlin(): String {
|
||||
val result = "if (" + condition.toKotlin() + ")$br" + thenStatement.toKotlin()
|
||||
if (!elseStatement.isEmpty) {
|
||||
return "$result${br}else$br${elseStatement.toKotlin()}"
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// Loops --------------------------------------------------------------------------------------------------
|
||||
|
||||
open class WhileStatement(val condition: Expression, val body: Element) : Statement() {
|
||||
override fun toKotlin() = "while (" + condition.toKotlin() + ")\n" + body.toKotlin()
|
||||
class WhileStatement(val condition: Expression, val body: Element, singleLine: Boolean) : Statement() {
|
||||
private val br = if (singleLine) " " else "\n"
|
||||
|
||||
override fun toKotlin() = "while (" + condition.toKotlin() + ")$br" + body.toKotlin()
|
||||
}
|
||||
|
||||
class DoWhileStatement(condition: Expression, body: Element) : WhileStatement(condition, body) {
|
||||
override fun toKotlin() = "do\n" + body.toKotlin() + "\nwhile (" + condition.toKotlin() + ")"
|
||||
class DoWhileStatement(val condition: Expression, val body: Element, singleLine: Boolean) : Statement() {
|
||||
private val br = if (singleLine) " " else "\n"
|
||||
|
||||
override fun toKotlin() = "do$br" + body.toKotlin() + "${br}while (" + condition.toKotlin() + ")"
|
||||
}
|
||||
|
||||
class ForeachStatement(
|
||||
val variable: Parameter,
|
||||
val expression: Expression,
|
||||
val body: Element
|
||||
val body: Element,
|
||||
singleLine: Boolean
|
||||
) : Statement() {
|
||||
override fun toKotlin() = "for (" + variable.identifier.toKotlin() + " in " + expression.toKotlin() + ")\n" + body.toKotlin()
|
||||
|
||||
private val br = if (singleLine) " " else "\n"
|
||||
|
||||
override fun toKotlin() = "for (" + variable.identifier.toKotlin() + " in " + expression.toKotlin() + ")$br" + body.toKotlin()
|
||||
}
|
||||
|
||||
class ForeachWithRangeStatement(val identifier: Identifier,
|
||||
val start: Expression,
|
||||
val end: Expression,
|
||||
val body: Element) : Statement() {
|
||||
override fun toKotlin() = "for (" + identifier.toKotlin() + " in " + start.toKotlin() + ".." + end.toKotlin() + ") " + body.toKotlin()
|
||||
val start: Expression,
|
||||
val end: Expression,
|
||||
val body: Element,
|
||||
singleLine: Boolean) : Statement() {
|
||||
private val br = if (singleLine) " " else "\n"
|
||||
|
||||
override fun toKotlin() = "for (" + identifier.toKotlin() + " in " + start.toKotlin() + ".." + end.toKotlin() + ")$br" + body.toKotlin()
|
||||
}
|
||||
|
||||
open class BreakStatement(val label: Identifier = Identifier.Empty) : Statement() {
|
||||
class BreakStatement(val label: Identifier = Identifier.Empty) : Statement() {
|
||||
override fun toKotlin() = "break" + label.withPrefix("@")
|
||||
}
|
||||
|
||||
open class ContinueStatement(val label: Identifier = Identifier.Empty) : Statement() {
|
||||
class ContinueStatement(val label: Identifier = Identifier.Empty) : Statement() {
|
||||
override fun toKotlin() = "continue" + label.withPrefix("@")
|
||||
}
|
||||
|
||||
@@ -142,7 +155,8 @@ class SynchronizedStatement(val expression: Expression, val block: Block) : Stat
|
||||
override fun toKotlin() = "synchronized (" + expression.toKotlin() + ") " + block.toKotlin()
|
||||
}
|
||||
|
||||
class StatementList(elements: List<Element>) : WhiteSpaceSeparatedElementList(elements, WhiteSpace.NewLine) {
|
||||
class StatementList(elements: List<Element>)
|
||||
: WhiteSpaceSeparatedElementList(elements, WhiteSpace.NewLine) {
|
||||
val statements: List<Statement>
|
||||
get() = elements.filterIsInstance(javaClass<Statement>())
|
||||
}
|
||||
@@ -103,7 +103,8 @@ class ExpressionVisitor(private val converter: Converter,
|
||||
converter.convertExpression(condition)
|
||||
result = IfStatement(expr,
|
||||
converter.convertExpression(expression.getThenExpression()),
|
||||
converter.convertExpression(expression.getElseExpression()))
|
||||
converter.convertExpression(expression.getElseExpression()),
|
||||
expression.isInSingleLine())
|
||||
}
|
||||
|
||||
override fun visitExpressionList(list: PsiExpressionList) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.jet.j2k.ast.*
|
||||
import org.jetbrains.jet.j2k.countWriteAccesses
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.jet.j2k.hasWriteAccesses
|
||||
import org.jetbrains.jet.j2k.isInSingleLine
|
||||
|
||||
class StatementVisitor(public val converter: Converter) : JavaElementVisitor() {
|
||||
public var result: Statement = Statement.Empty
|
||||
@@ -64,7 +65,7 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() {
|
||||
converter.convertExpression(condition, condition.getType())
|
||||
else
|
||||
converter.convertExpression(condition)
|
||||
result = DoWhileStatement(expression, converter.convertStatement(statement.getBody()))
|
||||
result = DoWhileStatement(expression, converter.convertStatement(statement.getBody()), statement.isInSingleLine())
|
||||
}
|
||||
|
||||
override fun visitExpressionStatement(statement: PsiExpressionStatement) {
|
||||
@@ -106,7 +107,8 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() {
|
||||
result = ForeachWithRangeStatement(Identifier(loopVar.getName()!!),
|
||||
converter.convertExpression(loopVar.getInitializer()),
|
||||
endExpression,
|
||||
converter.convertStatement(body))
|
||||
converter.convertStatement(body),
|
||||
statement.isInSingleLine())
|
||||
}
|
||||
else {
|
||||
var forStatements = ArrayList<Statement>()
|
||||
@@ -114,11 +116,9 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() {
|
||||
val bodyAndUpdate = listOf(converter.convertStatement(body),
|
||||
Block(listOf(converter.convertStatement(update))))
|
||||
forStatements.add(WhileStatement(
|
||||
if (condition == null)
|
||||
LiteralExpression("true")
|
||||
else
|
||||
converter.convertExpression(condition),
|
||||
Block(bodyAndUpdate)))
|
||||
if (condition == null) LiteralExpression("true") else converter.convertExpression(condition),
|
||||
Block(bodyAndUpdate),
|
||||
statement.isInSingleLine()))
|
||||
result = Block(forStatements)
|
||||
}
|
||||
}
|
||||
@@ -133,15 +133,17 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() {
|
||||
}
|
||||
result = ForeachStatement(converter.convertParameter(statement.getIterationParameter()),
|
||||
iterator,
|
||||
converter.convertStatement(statement.getBody()))
|
||||
converter.convertStatement(statement.getBody()),
|
||||
statement.isInSingleLine())
|
||||
}
|
||||
|
||||
override fun visitIfStatement(statement: PsiIfStatement) {
|
||||
val condition = statement.getCondition()
|
||||
val expression = converter.convertExpression(condition, PsiType.BOOLEAN)
|
||||
result = IfStatement(expression,
|
||||
converter.convertStatement(statement.getThenBranch()),
|
||||
converter.convertStatement(statement.getElseBranch()))
|
||||
converter.convertStatement(statement.getThenBranch()),
|
||||
converter.convertStatement(statement.getElseBranch()),
|
||||
statement.isInSingleLine())
|
||||
}
|
||||
|
||||
override fun visitLabeledStatement(statement: PsiLabeledStatement) {
|
||||
@@ -172,7 +174,6 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() {
|
||||
var i = 0
|
||||
var hasDefaultCase: Boolean = false
|
||||
for (ls in cases) {
|
||||
// TODO assert {(ls?.size()).sure() > 0}
|
||||
if (ls.size() > 0) {
|
||||
var label = ls[0] as PsiSwitchLabelStatement
|
||||
hasDefaultCase = hasDefaultCase || label.isDefaultCase()
|
||||
@@ -181,14 +182,18 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() {
|
||||
if (ls.size() > 1) {
|
||||
pendingLabels.add(converter.convertStatement(label))
|
||||
val slice: List<PsiElement> = ls.subList(1, (ls.size()))
|
||||
|
||||
fun convertStatements(elements: List<PsiElement>): List<Statement>
|
||||
= elements.map { if (it is PsiStatement) converter.convertStatement(it) else null }.filterNotNull()
|
||||
|
||||
if (!containsBreak(slice)) {
|
||||
val statements = ArrayList(converter.convertStatements(slice).statements)
|
||||
statements.addAll(converter.convertStatements(getAllToNextBreak(allSwitchStatements, i + ls.size())).statements)
|
||||
val statements = ArrayList(convertStatements(slice))
|
||||
statements.addAll(convertStatements(getAllToNextBreak(allSwitchStatements, i + ls.size())))
|
||||
result.add(CaseContainer(pendingLabels, statements))
|
||||
pendingLabels = ArrayList()
|
||||
}
|
||||
else {
|
||||
result.add(CaseContainer(pendingLabels, converter.convertStatements(slice).statements))
|
||||
result.add(CaseContainer(pendingLabels, convertStatements(slice)))
|
||||
pendingLabels = ArrayList()
|
||||
}
|
||||
}
|
||||
@@ -225,12 +230,12 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() {
|
||||
}
|
||||
|
||||
override fun visitWhileStatement(statement: PsiWhileStatement) {
|
||||
var condition: PsiExpression? = statement.getCondition()
|
||||
val expression: Expression = (if (condition != null && condition?.getType() != null)
|
||||
converter.convertExpression(condition, condition?.getType())
|
||||
val condition = statement.getCondition()
|
||||
val expression = if (condition?.getType() != null)
|
||||
converter.convertExpression(condition, condition!!.getType())
|
||||
else
|
||||
converter.convertExpression(condition))
|
||||
result = WhileStatement(expression, converter.convertStatement(statement.getBody()))
|
||||
converter.convertExpression(condition)
|
||||
result = WhileStatement(expression, converter.convertStatement(statement.getBody()), statement.isInSingleLine())
|
||||
}
|
||||
|
||||
override fun visitReturnStatement(statement: PsiReturnStatement) {
|
||||
|
||||
@@ -712,9 +712,14 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("j2k/tests/testData/ast/conditionalExpression"), Pattern.compile("^(.+)\\.java$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleConditionalExpression.java")
|
||||
public void testSimpleConditionalExpression() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/conditionalExpression/simpleConditionalExpression.java");
|
||||
@TestMetadata("multiline.java")
|
||||
public void testMultiline() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/conditionalExpression/multiline.java");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.java")
|
||||
public void testSimple() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/conditionalExpression/simple.java");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1316,24 +1321,29 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("j2k/tests/testData/ast/ifStatement"), Pattern.compile("^(.+)\\.java$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("ifStatementWithEmptyBlocks.java")
|
||||
public void testIfStatementWithEmptyBlocks() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/ifStatement/ifStatementWithEmptyBlocks.java");
|
||||
@TestMetadata("multiLine.java")
|
||||
public void testMultiLine() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/ifStatement/multiLine.java");
|
||||
}
|
||||
|
||||
@TestMetadata("ifStatementWithMultilineBlocks.java")
|
||||
public void testIfStatementWithMultilineBlocks() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/ifStatement/ifStatementWithMultilineBlocks.java");
|
||||
@TestMetadata("singleLine.java")
|
||||
public void testSingleLine() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/ifStatement/singleLine.java");
|
||||
}
|
||||
|
||||
@TestMetadata("ifStatementWithOneLineBlocks.java")
|
||||
public void testIfStatementWithOneLineBlocks() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/ifStatement/ifStatementWithOneLineBlocks.java");
|
||||
@TestMetadata("withBlocks.java")
|
||||
public void testWithBlocks() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/ifStatement/withBlocks.java");
|
||||
}
|
||||
|
||||
@TestMetadata("ifStatementWithoutElse.java")
|
||||
public void testIfStatementWithoutElse() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/ifStatement/ifStatementWithoutElse.java");
|
||||
@TestMetadata("withEmptyBlocks.java")
|
||||
public void testWithEmptyBlocks() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/ifStatement/withEmptyBlocks.java");
|
||||
}
|
||||
|
||||
@TestMetadata("withoutElse.java")
|
||||
public void testWithoutElse() throws Exception {
|
||||
doTest("j2k/tests/testData/ast/ifStatement/withoutElse.java");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
//method
|
||||
void foo(boolean b) {
|
||||
if (b)
|
||||
System.out.println("true")
|
||||
else
|
||||
System.out.println("false")
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(b: Boolean) {
|
||||
if (b)
|
||||
System.out.println("true")
|
||||
else
|
||||
System.out.println("false")
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
if (a.isEmpty()) 0 else 1
|
||||
@@ -1,4 +0,0 @@
|
||||
if (a.isEmpty())
|
||||
0
|
||||
else
|
||||
1
|
||||
@@ -1,3 +1 @@
|
||||
do
|
||||
i = i + 1
|
||||
while (true)
|
||||
do i = i + 1 while (true)
|
||||
@@ -1,3 +1 @@
|
||||
do
|
||||
return 1
|
||||
while (true)
|
||||
do return 1 while (true)
|
||||
@@ -1,2 +1 @@
|
||||
for (n in list)
|
||||
i++
|
||||
for (n in list) i++
|
||||
@@ -1,2 +1 @@
|
||||
for (n in list)
|
||||
return n
|
||||
for (n in list) return n
|
||||
@@ -1,4 +0,0 @@
|
||||
if (true)
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
@@ -0,0 +1,3 @@
|
||||
//statement
|
||||
if (true)
|
||||
return 1;
|
||||
@@ -0,0 +1,2 @@
|
||||
if (true)
|
||||
return 1
|
||||
@@ -0,0 +1 @@
|
||||
if (true) return 1 else return 0
|
||||
@@ -15,10 +15,7 @@ class Test() {
|
||||
if (One.myContainer.myBoolean)
|
||||
System.out.println("Ok")
|
||||
|
||||
val s = if (One.myContainer.myBoolean)
|
||||
"YES"
|
||||
else
|
||||
"NO"
|
||||
val s = if (One.myContainer.myBoolean) "YES" else "NO"
|
||||
|
||||
while (One.myContainer.myBoolean)
|
||||
System.out.println("Ok")
|
||||
|
||||
@@ -10,7 +10,4 @@
|
||||
foundIt = true
|
||||
break@test
|
||||
}
|
||||
System.out.println(if (foundIt)
|
||||
"Found it"
|
||||
else
|
||||
"Didn't find it")
|
||||
System.out.println(if (foundIt) "Found it" else "Didn't find it")
|
||||
@@ -3,9 +3,6 @@ class C() {
|
||||
}
|
||||
|
||||
fun bar(b: Boolean) {
|
||||
foo(if (b)
|
||||
"a"
|
||||
else
|
||||
null)
|
||||
foo(if (b) "a" else null)
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,7 @@ trait I {
|
||||
class C() {
|
||||
fun foo(i: I, b: Boolean) {
|
||||
val result = i.getString()
|
||||
if (b)
|
||||
result = null
|
||||
if (b) result = null
|
||||
if (result != null) {
|
||||
print(result)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
class C() {
|
||||
fun foo(b: Boolean): String? {
|
||||
return if (b)
|
||||
"abc"
|
||||
else
|
||||
null
|
||||
return if (b) "abc" else null
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
class C() {
|
||||
fun getString(b: Boolean): String? {
|
||||
return if (b)
|
||||
"a"
|
||||
else
|
||||
null
|
||||
return if (b) "a" else null
|
||||
}
|
||||
|
||||
fun foo(): Int {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
fun foo(s: String?, b: Boolean): Int {
|
||||
if (s == null)
|
||||
System.out.println("null")
|
||||
if (b)
|
||||
return s?.length()!!
|
||||
if (s == null) System.out.println("null")
|
||||
if (b) return s?.length()!!
|
||||
return 10
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
// !specifyLocalVariableTypeByDefault: true
|
||||
fun foo(b: Boolean) {
|
||||
val s: String? = (if (b)
|
||||
"abc"
|
||||
else
|
||||
null)
|
||||
val s: String? = (if (b) "abc" else null)
|
||||
}
|
||||
@@ -11,7 +11,9 @@ public class Client() : Frame() {
|
||||
override fun windowClosing() {
|
||||
}
|
||||
}
|
||||
|
||||
addWindowListener(a)
|
||||
|
||||
addWindowListener(object : WindowAdapter() {
|
||||
override fun windowClosing() {
|
||||
}
|
||||
|
||||
@@ -84,7 +84,8 @@ public class SwitchDemo() {
|
||||
}
|
||||
|
||||
public fun main(args: Array<String>) {
|
||||
for (i in 1..12) test(i)
|
||||
for (i in 1..12)
|
||||
test(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
while (true)
|
||||
i = i + 1
|
||||
while (true) i = i + 1
|
||||
@@ -1,2 +1 @@
|
||||
while (true)
|
||||
return 1
|
||||
while (true) return 1
|
||||
Reference in New Issue
Block a user