diff --git a/j2k/src/org/jetbrains/jet/j2k/CodeBuilder.kt b/j2k/src/org/jetbrains/jet/j2k/CodeBuilder.kt new file mode 100644 index 00000000000..b47da7a9bc8 --- /dev/null +++ b/j2k/src/org/jetbrains/jet/j2k/CodeBuilder.kt @@ -0,0 +1,222 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.j2k + +import com.intellij.psi.* +import java.util.HashSet +import org.jetbrains.jet.lang.psi.psiUtil.isAncestor +import java.util.ArrayList +import org.jetbrains.jet.j2k.ast.Element +import kotlin.platform.platformName + +fun CodeBuilder.append(generators: Collection<() -> T>, separator: String, prefix: String = "", suffix: String = ""): CodeBuilder { + if (generators.isNotEmpty()) { + append(prefix) + var first = true + for (generator in generators) { + if (!first) { + append(separator) + } + generator() + first = false + } + append(suffix) + } + return this +} + +platformName("appendElements") +fun CodeBuilder.append(elements: Collection, separator: String, prefix: String = "", suffix: String = ""): CodeBuilder { + return append(elements.filter { !it.isEmpty }.map { { append(it) } }, separator, prefix, suffix) +} + +class CodeBuilder(private val topElement: PsiElement?) { + private val builder = StringBuilder() + private var endOfLineCommentAtEnd = false + + private val commentsAndSpacesUsed = HashSet() + + public fun append(text: String, endOfLineComment: Boolean = false): CodeBuilder { + if (text.isEmpty()) { + assert(!endOfLineComment) + return this + } + + if (endOfLineCommentAtEnd) { + if (text[0] != '\n' && text[0] != '\r') builder.append('\n') + endOfLineCommentAtEnd = false + } + + builder.append(text) + endOfLineCommentAtEnd = endOfLineComment + return this + } + + public val result: String + get() = builder.toString() + + public fun append(element: Element): CodeBuilder { + if (element.isEmpty) return this // do not insert comment and spaces for empty elements to avoid multiple blank lines + + if (element.prototypes.isEmpty() || topElement == null) { + element.generateCode(this) + return this + } + + val prefixElements = ArrayList(2) + val postfixElements = ArrayList(2) + for ((prototype, inheritBlankLinesBefore) in element.prototypes) { + assert(prototype !is PsiComment) + assert(prototype !is PsiWhiteSpace) + assert(topElement.isAncestor(prototype)) + prefixElements.collectPrefixElements(prototype, inheritBlankLinesBefore) + postfixElements.collectPostfixElements(prototype) + } + + commentsAndSpacesUsed.addAll(prefixElements) + commentsAndSpacesUsed.addAll(postfixElements) + + for (i in prefixElements.indices) { + val e = prefixElements[i] + if (i == 0 && e is PsiWhiteSpace) { + if (e.newLinesCount() > 1) { // insert at maximum one blank line + append("\n") + } + } + else { + append(e.getText()!!, e.isEndOfLineComment()) + } + } + + element.generateCode(this) + + postfixElements.forEach { append(it.getText()!!, it.isEndOfLineComment()) } + + return this + } + + private fun MutableList.collectPrefixElements(element: PsiElement, allowBlankLinesBefore: Boolean) { + val atStart = ArrayList(2).collectCommentsAndSpacesAtStart(element) + + val before = ArrayList(2).collectCommentsAndSpacesBefore(element) + if (!allowBlankLinesBefore && before.lastOrNull() is PsiWhiteSpace) { + before.remove(before.size - 1) + } + + addAll(before.reverse()) + addAll(atStart) + } + + private fun MutableList.collectPostfixElements(element: PsiElement) { + val atEnd = ArrayList(2).collectCommentsAndSpacesAtEnd(element) + + val after = ArrayList(2).collectCommentsAndSpacesAfter(element) + if (after.isNotEmpty()) { + val last = after.last() + if (last is PsiWhiteSpace) { + after.remove(after.size - 1) + } + } + + addAll(atEnd.reverse()) + addAll(after) + } + + private fun MutableList.collectCommentsAndSpacesBefore(element: PsiElement): MutableList { + if (element == topElement) return this + + val prev = element.getPrevSibling() + if (prev != null) { + if (prev.isCommentOrSpace()) { + if (prev !in commentsAndSpacesUsed) { + add(prev) + collectCommentsAndSpacesBefore(prev) + } + } + else if (prev.isEmptyElement()){ + collectCommentsAndSpacesBefore(prev) + } + } + else { + collectCommentsAndSpacesBefore(element.getParent()!!) + } + return this + } + + private fun MutableList.collectCommentsAndSpacesAfter(element: PsiElement): MutableList { + if (element == topElement) return this + + val next = element.getNextSibling() + if (next != null) { + if (next.isCommentOrSpace()) { + if (next is PsiWhiteSpace && next.hasNewLines()) return this // do not attach anything on next line after element + if (next !in commentsAndSpacesUsed) { + add(next) + collectCommentsAndSpacesAfter(next) + } + } + else if (next.isEmptyElement()){ + collectCommentsAndSpacesAfter(next) + } + } + else { + collectCommentsAndSpacesAfter(element.getParent()!!) + } + return this + } + + private fun MutableList.collectCommentsAndSpacesAtStart(element: PsiElement): MutableList { + var child = element.getFirstChild() + while(child != null) { + if (child!!.isCommentOrSpace()) { + if (child !in commentsAndSpacesUsed) add(child!!) else break + } + else if (!child!!.isEmptyElement()) { + collectCommentsAndSpacesAtStart(child!!) + break + } + child = child!!.getNextSibling() + } + return this + } + + private fun MutableList.collectCommentsAndSpacesAtEnd(element: PsiElement): MutableList { + var child = element.getLastChild() + while(child != null) { + if (child!!.isCommentOrSpace()) { + if (child !in commentsAndSpacesUsed) add(child!!) else break + } + else if (!child!!.isEmptyElement()) { + collectCommentsAndSpacesAtEnd(child!!) + break + } + child = child!!.getPrevSibling() + } + return this + } + + private fun PsiElement.isCommentOrSpace() = this is PsiComment || this is PsiWhiteSpace + + private fun PsiElement.isEndOfLineComment() = this is PsiComment && getTokenType() == JavaTokenType.END_OF_LINE_COMMENT + + private fun PsiElement.isEmptyElement() = getFirstChild() == null && getTextLength() == 0 + + private fun PsiWhiteSpace.newLinesCount() = getText()!!.count { it == '\n' } //TODO: this is not correct!! + + private fun PsiWhiteSpace.hasNewLines() = getText()!!.any { it == '\n' || it == '\r' } +} + diff --git a/j2k/src/org/jetbrains/jet/j2k/CommentsAndSpaces.kt b/j2k/src/org/jetbrains/jet/j2k/CommentsAndSpaces.kt deleted file mode 100644 index 7d306d1d5a3..00000000000 --- a/j2k/src/org/jetbrains/jet/j2k/CommentsAndSpaces.kt +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2010-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.jet.j2k - -import com.intellij.psi.* -import java.util.HashSet -import org.jetbrains.jet.lang.psi.psiUtil.isAncestor -import java.util.ArrayList -import org.jetbrains.jet.j2k.ast.PrototypeInfo - -class CommentsAndSpaces(private val topElement: PsiElement?) { - private val commentsAndSpacesUsed = HashSet() - - public fun wrapElement(elementToKotlin: () -> String, prototypes: List): String { - if (prototypes.isEmpty() || topElement == null) return elementToKotlin() - - val prefix = StringBuilder() - val postfix = StringBuilder() - for ((element, inheritBlankLinesBefore) in prototypes) { - assert(element !is PsiComment) - assert(element !is PsiWhiteSpace) - assert(topElement.isAncestor(element)) - prefix.collectPrefix(element, inheritBlankLinesBefore) - postfix.collectPostfix(element) - } - - return prefix.toString() + elementToKotlin() + postfix.toString() - } - - private fun StringBuilder.collectPrefix(element: PsiElement, allowBlankLinesBefore: Boolean) { - val atStart = ArrayList(2).addCommentsAndSpacesAtStart(element) - - val before = ArrayList(2).addCommentsAndSpacesBefore(element) - if (before.isNotEmpty()) { - val last = before.last() - if (last is PsiWhiteSpace) { - if (allowBlankLinesBefore) { - val blankLines = last.newLinesCount() - 1 - if (blankLines > 0) { - append("\n") // insert at maximum one blank line - commentsAndSpacesUsed.add(last) - } - } - before.remove(before.size - 1) - } - } - - if (before.isEmpty() && atStart.isEmpty()) return - - for (e in before.reverse() + atStart) { - append(e.getText()) - commentsAndSpacesUsed.add(e) - } - } - - private fun StringBuilder.collectPostfix(element: PsiElement) { - val atEnd = ArrayList(2).addCommentsAndSpacesAtEnd(element) - - val after = ArrayList(2).addCommentsAndSpacesAfter(element) - if (after.isNotEmpty()) { - val last = after.last() - if (last is PsiWhiteSpace) { - after.remove(after.size - 1) - } - } - - if (after.isEmpty() && atEnd.isEmpty()) return - - for (e in atEnd.reverse() + after) { - append(e.getText()) - commentsAndSpacesUsed.add(e) - } - } - - private fun MutableList.addCommentsAndSpacesBefore(element: PsiElement): MutableList { - if (element == topElement) return this - - val prev = element.getPrevSibling() - if (prev != null) { - if (prev.isCommentOrSpace()) { - if (prev !in commentsAndSpacesUsed) { - add(prev) - addCommentsAndSpacesBefore(prev) - } - } - else if (prev.isEmptyElement()){ - addCommentsAndSpacesBefore(prev) - } - } - else { - addCommentsAndSpacesBefore(element.getParent()!!) - } - return this - } - - private fun MutableList.addCommentsAndSpacesAfter(element: PsiElement): MutableList { - if (element == topElement) return this - - val next = element.getNextSibling() - if (next != null) { - if (next.isCommentOrSpace()) { - if (next is PsiWhiteSpace && next.hasNewLines()) return this // do not attach anything on next line after element - if (next !in commentsAndSpacesUsed) { - add(next) - addCommentsAndSpacesAfter(next) - } - } - else if (next.isEmptyElement()){ - addCommentsAndSpacesAfter(next) - } - } - else { - addCommentsAndSpacesAfter(element.getParent()!!) - } - return this - } - - private fun MutableList.addCommentsAndSpacesAtStart(element: PsiElement): MutableList { - var child = element.getFirstChild() - while(child != null) { - if (child!!.isCommentOrSpace()) { - if (child !in commentsAndSpacesUsed) add(child!!) else break - } - else if (!child!!.isEmptyElement()) { - addCommentsAndSpacesAtStart(child!!) - break - } - child = child!!.getNextSibling() - } - return this - } - - private fun MutableList.addCommentsAndSpacesAtEnd(element: PsiElement): MutableList { - var child = element.getLastChild() - while(child != null) { - if (child!!.isCommentOrSpace()) { - if (child !in commentsAndSpacesUsed) add(child!!) else break - } - else if (!child!!.isEmptyElement()) { - addCommentsAndSpacesAtEnd(child!!) - break - } - child = child!!.getPrevSibling() - } - return this - } - - private fun PsiElement.isCommentOrSpace() = this is PsiComment || this is PsiWhiteSpace - - private fun PsiElement.isEmptyElement() = getFirstChild() == null && getTextLength() == 0 - - private fun PsiWhiteSpace.newLinesCount() = getText()!!.count { it == '\n' } //TODO: this is not correct!! - - private fun PsiWhiteSpace.hasNewLines() = getText()!!.any { it == '\n' || it == '\r' } - - class object { - val None: CommentsAndSpaces = CommentsAndSpaces(null) - } -} - diff --git a/j2k/src/org/jetbrains/jet/j2k/Converter.kt b/j2k/src/org/jetbrains/jet/j2k/Converter.kt index c23b8512880..e36c812a901 100644 --- a/j2k/src/org/jetbrains/jet/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/Converter.kt @@ -59,8 +59,12 @@ public class Converter private(val project: Project, val settings: ConverterSett fun withStatementVisitor(factory: (Converter) -> StatementVisitor): Converter = Converter(project, settings, conversionScope, State(typeConverter, state.methodReturnType, state.expressionVisitorFactory, factory)) - public fun elementToKotlin(element: PsiElement): String - = convertTopElement(element)?.toKotlin(CommentsAndSpaces(element)) ?: "" + public fun elementToKotlin(element: PsiElement): String { + val converted = convertTopElement(element) ?: return "" + val builder = CodeBuilder(element) + builder.append(converted) + return builder.result + } private fun convertTopElement(element: PsiElement?): Element? = when(element) { is PsiJavaFile -> convertFile(element) @@ -519,7 +523,7 @@ public class Converter private(val project: Project, val settings: ConverterSett fun convertParameter(parameter: PsiParameter, nullability: Nullability = Nullability.Default, varValModifier: Parameter.VarValModifier = Parameter.VarValModifier.None, - modifiers: Collection = listOf()): Parameter { + modifiers: List = listOf()): Parameter { var `type` = typeConverter.convertVariableType(parameter) when (nullability) { Nullability.NotNull -> `type` = `type`.toNotNullType() diff --git a/j2k/src/org/jetbrains/jet/j2k/Utils.kt b/j2k/src/org/jetbrains/jet/j2k/Utils.kt index 90dfa838385..13b83b6e1ab 100644 --- a/j2k/src/org/jetbrains/jet/j2k/Utils.kt +++ b/j2k/src/org/jetbrains/jet/j2k/Utils.kt @@ -64,7 +64,7 @@ fun getDefaultInitializer(field: Field): Expression { } if (t is PrimitiveType) { - when(t.`type`.name) { + when(t.name.name) { "Boolean" -> return LiteralExpression("false") "Char" -> return LiteralExpression("' '") "Double" -> return MethodCallExpression.buildNotNull(LiteralExpression("0"), OperatorConventions.DOUBLE.toString()) diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Annotation.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Annotation.kt index a96ed874910..57b51cc5775 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Annotation.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Annotation.kt @@ -16,31 +16,46 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* class Annotation(val name: Identifier, val arguments: List>, val brackets: Boolean) : Element() { - private fun surroundWithBrackets(text: String) = if (brackets) "[$text]" else text + private fun CodeBuilder.surroundWithBrackets(action: () -> Unit) { + if (brackets) append("[") + action() + if (brackets) append("]") + } - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { + override fun generateCode(builder: CodeBuilder) { if (arguments.isEmpty()) { - return surroundWithBrackets(name.toKotlin(commentsAndSpaces)) + builder.surroundWithBrackets { builder.append(name) } + return } - val argsText = arguments.map { - if (it.first != null) - it.first!!.toKotlin(commentsAndSpaces) + " = " + it.second.toKotlin(commentsAndSpaces) - else - it.second.toKotlin(commentsAndSpaces) - }.makeString(", ") - return surroundWithBrackets(name.toKotlin(commentsAndSpaces) + "(" + argsText + ")") + builder.surroundWithBrackets { + builder.append(name) + .append("(") + .append(arguments.map { + { + if (it.first != null) { + builder append it.first!! append " = " append it.second + } + else { + builder append it.second + } + } + }, ", ") + .append(")") + } } } class Annotations(val annotations: List, val newLines: Boolean) : Element() { private val br = if (newLines) "\n" else " " - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - return if (annotations.isNotEmpty()) annotations.map { it.toKotlin(commentsAndSpaces) }.makeString(br) + br else "" + override fun generateCode(builder: CodeBuilder) { + if (annotations.isNotEmpty()) { + builder.append(annotations, br, "", br) + } } fun plus(other: Annotations) = Annotations(annotations + other.annotations, newLines || other.newLines) diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt b/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt index 8ea24070061..738c22cc822 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt @@ -16,9 +16,11 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.CodeBuilder class AnonymousClassBody(body: ClassBody, val extendsTrait: Boolean) : Class(Identifier.Empty, Annotations.Empty, setOf(), TypeParameterList.Empty, listOf(), listOf(), listOf(), body) { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = body.toKotlin(null, commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + body.append(builder, null) + } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/ArrayWithoutInitializationExpression.kt b/j2k/src/org/jetbrains/jet/j2k/ast/ArrayWithoutInitializationExpression.kt index 30519ce89bc..c9f5f003f75 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/ArrayWithoutInitializationExpression.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/ArrayWithoutInitializationExpression.kt @@ -16,41 +16,51 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.CodeBuilder +import org.jetbrains.jet.j2k.append class ArrayWithoutInitializationExpression(val `type`: ArrayType, val expressions: List) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - fun getConstructorName(`type`: ArrayType, hasInit: Boolean): String { - return when (`type`.elementType) { - is PrimitiveType -> - `type`.toNotNullType().toKotlin(commentsAndSpaces) - is ArrayType -> - if (hasInit) - `type`.toNotNullType().toKotlin(commentsAndSpaces) - else - "arrayOfNulls<" + `type`.elementType.toKotlin(commentsAndSpaces) + ">" - else -> - "arrayOfNulls<" + `type`.elementType.toKotlin(commentsAndSpaces) + ">" + override fun generateCode(builder: CodeBuilder) { + fun appendConstructorName(`type`: ArrayType, hasInit: Boolean): CodeBuilder = when (`type`.elementType) { + is PrimitiveType -> builder.append(`type`.toNotNullType()) + + is ArrayType -> + if (hasInit) { + builder.append(`type`.toNotNullType()) + } + else { + builder.append("arrayOfNulls<").append(`type`.elementType).append(">") + } + + else -> builder.append("arrayOfNulls<").append(`type`.elementType).append(">") + } + + fun oneDim(`type`: ArrayType, size: Expression, init: (() -> Unit)? = null): CodeBuilder { + appendConstructorName(`type`, init != null).append("(").append(size) + if (init != null) { + builder.append(", ") + init() } + return builder.append(")") } - fun oneDim(`type`: ArrayType, size: Expression, init: String = ""): String { - return getConstructorName(`type`, !init.isEmpty()) + "(" + size.toKotlin(commentsAndSpaces) + init.withPrefix(", ") + ")" - } - - fun constructInnerType(hostType: ArrayType, expressions: List): String { + fun constructInnerType(hostType: ArrayType, expressions: List): CodeBuilder { if (expressions.size() == 1) { return oneDim(hostType, expressions[0]) } val innerType = hostType.elementType if (expressions.size() > 1 && innerType is ArrayType) { - return oneDim(hostType, expressions[0], "{" + constructInnerType(innerType, expressions.subList(1, expressions.size())) + "}") + return oneDim(hostType, expressions[0], { + builder.append("{") + constructInnerType(innerType, expressions.subList(1, expressions.size())) + builder.append("}") + }) } - return getConstructorName(hostType, expressions.size() != 0) + return appendConstructorName(hostType, expressions.isNotEmpty()) } - return constructInnerType(`type`, expressions) + constructInnerType(`type`, expressions) } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Block.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Block.kt index 1a85bf35949..15b417ffe06 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Block.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Block.kt @@ -16,21 +16,19 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* class Block(val statements: List, val lBrace: LBrace, val rBrace: RBrace, val notEmpty: Boolean = false) : Statement() { override val isEmpty: Boolean get() = !notEmpty && statements.all { it.isEmpty } - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { + override fun generateCode(builder: CodeBuilder) { if (statements.all { it.isEmpty }) { - return if (isEmpty) "" else lBrace.toKotlin(commentsAndSpaces) + rBrace.toKotlin(commentsAndSpaces) + if (!isEmpty) builder.append(lBrace).append(rBrace) + return } - return lBrace.toKotlin(commentsAndSpaces) + - "\n" + - statements.toKotlin(commentsAndSpaces, "\n") + - "\n" + - rBrace.toKotlin(commentsAndSpaces) + + builder.append(lBrace).append(statements, "\n", "\n", "\n").append(rBrace) } class object { @@ -40,9 +38,13 @@ class Block(val statements: List, val lBrace: LBrace, val rBrace: RBr // we use LBrace and RBrace elements to better handle comments around them class LBrace() : Element() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "{" + override fun generateCode(builder: CodeBuilder) { + builder.append("{") + } } class RBrace() : Element() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "}" + override fun generateCode(builder: CodeBuilder) { + builder.append("}") + } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Class.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Class.kt index 7ca280e905a..2b04dfbe1fd 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Class.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Class.kt @@ -17,7 +17,7 @@ package org.jetbrains.jet.j2k.ast import java.util.ArrayList -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* open class Class( val name: Identifier, @@ -30,41 +30,38 @@ open class Class( val body: ClassBody ) : Member(annotations, modifiers) { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String = - annotations.toKotlin(commentsAndSpaces) + - modifiersToKotlin() + - keyword + " " + name.toKotlin(commentsAndSpaces) + - typeParameterList.toKotlin(commentsAndSpaces) + - primaryConstructorSignatureToKotlin(commentsAndSpaces) + - implementTypesToKotlin(commentsAndSpaces) + - typeParameterList.whereToKotlin(commentsAndSpaces).withPrefix(" ") + - body.toKotlin(this, commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.append(annotations) + appendModifiers(builder).append(keyword).append(" ").append(name).append(typeParameterList) + appendPrimaryConstructorSignature(builder) + appendBaseTypes(builder) + typeParameterList.appendWhere(builder) + body.append(builder, this) + } protected open val keyword: String get() = "class" - protected open fun primaryConstructorSignatureToKotlin(commentsAndSpaces: CommentsAndSpaces): String - = body.primaryConstructor?.signatureToKotlin(commentsAndSpaces) ?: "()" + protected open fun appendPrimaryConstructorSignature(builder: CodeBuilder) { + body.primaryConstructor?.appendSignature(builder) ?: builder.append("()") + } - private fun baseClassSignatureWithParams(commentsAndSpaces: CommentsAndSpaces): List { + protected fun appendBaseTypes(builder: CodeBuilder) { + builder.append(baseClassSignatureWithParams(builder) + implementsTypes.map { { builder.append(it) } }, ", ", ":") + } + + private fun baseClassSignatureWithParams(builder: CodeBuilder): List<() -> CodeBuilder> { if (keyword.equals("class") && extendsTypes.size() == 1) { - val baseParams = baseClassParams.toKotlin(commentsAndSpaces, ", ") - return arrayListOf(extendsTypes[0].toKotlin(commentsAndSpaces) + "(" + baseParams + ")") + return listOf({ + builder append extendsTypes[0] append "(" + builder.append(baseClassParams, ", ") + builder append ")" + }) } - return extendsTypes.map { it.toKotlin(commentsAndSpaces) } + return extendsTypes.map { { builder.append(it) } } } - protected fun implementTypesToKotlin(commentsAndSpaces: CommentsAndSpaces): String { - val allTypes = ArrayList() - allTypes.addAll(baseClassSignatureWithParams(commentsAndSpaces)) - allTypes.addAll(implementsTypes.map { it.toKotlin(commentsAndSpaces) }) - return if (allTypes.size() == 0) - "" - else - " : " + allTypes.makeString(", ") - } - - protected open fun modifiersToKotlin(): String { + protected open fun appendModifiers(builder: CodeBuilder): CodeBuilder { val modifierList = ArrayList() modifiers.accessModifier()?.let { modifierList.add(it) } @@ -80,6 +77,6 @@ open class Class( modifierList.add(Modifier.INNER) } - return modifierList.toKotlin() + return builder.append(modifierList) } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/ClassBody.kt b/j2k/src/org/jetbrains/jet/j2k/ast/ClassBody.kt index 05b5bdfe2db..33319fe2640 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/ClassBody.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/ClassBody.kt @@ -16,7 +16,7 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* abstract class Member(val annotations: Annotations, val modifiers: Set) : Element() @@ -28,37 +28,33 @@ class ClassBody ( val lBrace: LBrace, val rBrace: RBrace) { - fun toKotlin(containingClass: Class?, commentsAndSpaces: CommentsAndSpaces): String { - val builder = StringBuilder() - builder.append(normalMembers.toKotlin(commentsAndSpaces, "\n")) + fun append(builder: CodeBuilder, containingClass: Class?) { + if (normalMembers.isEmpty() && classObjectMembers.isEmpty() && secondaryConstructors.isEmpty() && (primaryConstructor?.block?.isEmpty ?: true)) return - val primaryConstructor = primaryConstructorBodyToKotlin(commentsAndSpaces) - if (primaryConstructor.isNotEmpty() && builder.length() > 0) { - builder.append("\n\n") // blank line before constructor body - } - builder.append(primaryConstructor) + builder append " " append lBrace append "\n" - val classObject = classObjectToKotlin(containingClass, commentsAndSpaces) - if (classObject.isNotEmpty() && builder.length() > 0) { - builder.append("\n\n") // blank line before class object - } - builder.append(classObject) + builder.append(normalMembers, "\n") + var notEmpty = normalMembers.isNotEmpty() - return if (builder.length() > 0) - " " + lBrace.toKotlin(commentsAndSpaces) + "\n" + builder + "\n" + rBrace.toKotlin(commentsAndSpaces) - else - "" + notEmpty = appendPrimaryConstructorBody(builder, notEmpty) || notEmpty + + appendClassObject(builder, containingClass, notEmpty) + + builder append "\n" append rBrace } - private fun primaryConstructorBodyToKotlin(commentsAndSpaces: CommentsAndSpaces): String { + private fun appendPrimaryConstructorBody(builder: CodeBuilder, blankLineBefore: Boolean): Boolean { val constructor = primaryConstructor - if (constructor == null || constructor.block?.isEmpty ?: true) return "" - return constructor.bodyToKotlin(commentsAndSpaces) + if (constructor == null || constructor.block?.isEmpty ?: true) return false + if (blankLineBefore) builder.append("\n\n") + constructor.appendBody(builder) + return true } - private fun classObjectToKotlin(containingClass: Class?, commentsAndSpaces: CommentsAndSpaces): String { - if (secondaryConstructors.isEmpty() && classObjectMembers.isEmpty()) return "" + private fun appendClassObject(builder: CodeBuilder, containingClass: Class?, blankLineBefore: Boolean) { + if (secondaryConstructors.isEmpty() && classObjectMembers.isEmpty()) return + if (blankLineBefore) builder.append("\n\n") val factoryFunctions = secondaryConstructors.map { it.toFactoryFunction(containingClass) } - return "class object {\n" + (factoryFunctions + classObjectMembers).toKotlin(commentsAndSpaces, "\n") + "\n}" + builder.append(factoryFunctions + classObjectMembers, "\n", "class object {\n", "\n}") } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt index 2a0964dd8ca..4cb3dc0c8bf 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt @@ -16,10 +16,9 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.Converter import java.util.HashSet import java.util.ArrayList -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* abstract class Constructor( converter: Converter, @@ -36,13 +35,13 @@ class PrimaryConstructor(converter: Converter, block: Block) : Constructor(converter, annotations, modifiers, parameterList, block) { - public fun signatureToKotlin(commentsAndSpaces: CommentsAndSpaces): String { + public fun appendSignature(builder: CodeBuilder): CodeBuilder { val accessModifier = modifiers.accessModifier() val modifiersString = if (accessModifier != null && accessModifier != Modifier.PUBLIC) " " + accessModifier.toKotlin() else "" - return modifiersString + "(" + parameterList.toKotlin(commentsAndSpaces) + ")" + return builder.append(modifiersString).append("(").append(parameterList).append(")") } - public fun bodyToKotlin(commentsAndSpaces: CommentsAndSpaces): String = block!!.toKotlin(commentsAndSpaces) + public fun appendBody(builder: CodeBuilder): CodeBuilder = builder.append(block!!) } class SecondaryConstructor(converter: Converter, diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/DummyStringExpression.kt b/j2k/src/org/jetbrains/jet/j2k/ast/DummyStringExpression.kt index dbd7c03f1fe..106e841a940 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/DummyStringExpression.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/DummyStringExpression.kt @@ -16,8 +16,10 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.CodeBuilder class DummyStringExpression(val string: String) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String = string + override fun generateCode(builder: CodeBuilder) { + builder.append(string) + } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Element.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Element.kt index 2a61fc6a7e6..3fb80b56a23 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Element.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Element.kt @@ -16,7 +16,7 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* import com.intellij.psi.PsiElement fun TElement.assignPrototype(prototype: PsiElement?): TElement { @@ -36,6 +36,12 @@ fun TElement.assignPrototypesFrom(element: Element): TElemen data class PrototypeInfo(val element: PsiElement, val inheritBlankLinesBefore: Boolean) +fun Element.canonicalCode(): String { + val builder = CodeBuilder(null) + builder.append(this) + return builder.result +} + abstract class Element { public var prototypes: List = listOf() private set @@ -44,19 +50,13 @@ abstract class Element { this.prototypes = prototypes } - public fun toKotlin(commentsAndSpaces: CommentsAndSpaces): String { - return if (isEmpty) // do not insert comment and spaces for empty elements to avoid multiple blank lines - "" - else - commentsAndSpaces.wrapElement({ toKotlinImpl(commentsAndSpaces) }, prototypes) - } - - protected abstract fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String + /** This method should not be used anywhere except for CodeBuilder! Use CodeBuilder.append instead. */ + public abstract fun generateCode(builder: CodeBuilder) public open val isEmpty: Boolean get() = false object Empty : Element() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "" + override fun generateCode(builder: CodeBuilder) { } override val isEmpty: Boolean get() = true } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Enum.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Enum.kt index baa37ee79a7..6c5f33edd27 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Enum.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Enum.kt @@ -16,7 +16,7 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* class Enum( name: Identifier, @@ -30,16 +30,16 @@ class Enum( ) : Class(name, annotations, modifiers, typeParameterList, extendsTypes, baseClassParams, implementsTypes, body) { - override fun primaryConstructorSignatureToKotlin(commentsAndSpaces: CommentsAndSpaces): String - = body.primaryConstructor?.signatureToKotlin(commentsAndSpaces) ?: "" + override fun appendPrimaryConstructorSignature(builder: CodeBuilder) { + body.primaryConstructor?.appendSignature(builder) + } - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - return annotations.toKotlin(commentsAndSpaces) + - modifiersToKotlin() + - "enum class " + name.toKotlin(commentsAndSpaces) + - primaryConstructorSignatureToKotlin(commentsAndSpaces) + - typeParameterList.toKotlin(commentsAndSpaces) + - implementTypesToKotlin(commentsAndSpaces) + - body.toKotlin(this, commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append annotations + appendModifiers(builder) append "enum class " append name + appendPrimaryConstructorSignature(builder) + builder append typeParameterList + appendBaseTypes(builder) + body.append(builder, this) } } \ No newline at end of file diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/EnumConstant.kt b/j2k/src/org/jetbrains/jet/j2k/ast/EnumConstant.kt index 0c70faebecf..296b7c9282a 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/EnumConstant.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/EnumConstant.kt @@ -16,7 +16,7 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* class EnumConstant( identifier: Identifier, @@ -26,12 +26,13 @@ class EnumConstant( params: Element ) : Field(identifier, annotations, modifiers, `type`.toNotNullType(), params, true, false) { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - if (initializer.toKotlin(commentsAndSpaces).isEmpty()) { - return annotations.toKotlin(commentsAndSpaces) + identifier.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + if (initializer.isEmpty) { + builder append annotations append identifier + return } - return annotations.toKotlin(commentsAndSpaces) + identifier.toKotlin(commentsAndSpaces) + " : " + `type`.toKotlin(commentsAndSpaces) + "(" + initializer.toKotlin(commentsAndSpaces) + ")" + builder append annotations append identifier append " : " append `type` append "(" append initializer append ")" } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Expression.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Expression.kt index b8f65aa4fee..e9fa4713ae3 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Expression.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Expression.kt @@ -16,14 +16,14 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.CodeBuilder abstract class Expression() : Statement() { open val isNullable: Boolean get() = false object Empty : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "" + override fun generateCode(builder: CodeBuilder) {} override val isEmpty: Boolean get() = true } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/ExpressionList.kt b/j2k/src/org/jetbrains/jet/j2k/ast/ExpressionList.kt index 6c1a9ba657a..eaee0fe4c92 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/ExpressionList.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/ExpressionList.kt @@ -16,10 +16,12 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* class ExpressionList(val expressions: List) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String = expressions.map { it.toKotlin(commentsAndSpaces) }.makeString(", ") + override fun generateCode(builder: CodeBuilder) { + builder.append(expressions, ", ") + } override val isEmpty: Boolean get() = expressions.isEmpty() diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Expressions.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Expressions.kt index 3baccc78413..efd2a66692d 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Expressions.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Expressions.kt @@ -17,115 +17,144 @@ package org.jetbrains.jet.j2k.ast import org.jetbrains.jet.lang.types.expressions.OperatorConventions -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* class ArrayAccessExpression(val expression: Expression, val index: Expression, val lvalue: Boolean) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = operandToKotlin(expression, commentsAndSpaces) + - (if (!lvalue && expression.isNullable) "!!" else "") + - "[" + index.toKotlin(commentsAndSpaces) + "]" + override fun generateCode(builder: CodeBuilder) { + builder.appendOperand(this, expression) + if (!lvalue && expression.isNullable) builder.append("!!") + builder append "[" append index append "]" + } } class AssignmentExpression(val left: Expression, val right: Expression, val op: String) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = operandToKotlin(left, commentsAndSpaces) + " " + op + " " + operandToKotlin(right, commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.appendOperand(this, left).append(op).appendOperand(this, right) + } } class BangBangExpression(val expr: Expression) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = operandToKotlin(expr, commentsAndSpaces) + "!!" + override fun generateCode(builder: CodeBuilder) { + builder.appendOperand(this, expr).append("!!") + } } class BinaryExpression(val left: Expression, val right: Expression, val op: String) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = operandToKotlin(left, commentsAndSpaces, false) + " " + op + " " + operandToKotlin(right, commentsAndSpaces, true) + override fun generateCode(builder: CodeBuilder) { + builder.appendOperand(this, left, false).append(" ").append(op).append(" ").appendOperand(this, right, true) + } } class IsOperator(val expression: Expression, val typeElement: TypeElement) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = operandToKotlin(expression, commentsAndSpaces) + " is " + typeElement.`type`.toNotNullType().toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.appendOperand(this, expression).append(" is ").append(typeElement.`type`.toNotNullType()) + } } class TypeCastExpression(val `type`: Type, val expression: Expression) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = operandToKotlin(expression, commentsAndSpaces) + " as " + `type`.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.appendOperand(this, expression).append(" as ").append(`type`) + } } class LiteralExpression(val literalText: String) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = literalText + override fun generateCode(builder: CodeBuilder) { + builder.append(literalText) + } } class ParenthesizedExpression(val expression: Expression) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "(" + expression.toKotlin(commentsAndSpaces) + ")" + override fun generateCode(builder: CodeBuilder) { + builder append "(" append expression append ")" + } } class PrefixOperator(val op: String, val expression: Expression) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = op + operandToKotlin(expression, commentsAndSpaces) + override fun generateCode(builder: CodeBuilder){ + builder.append(op).appendOperand(this, expression) + } override val isNullable: Boolean get() = expression.isNullable } class PostfixOperator(val op: String, val expression: Expression) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = operandToKotlin(expression, commentsAndSpaces) + op + override fun generateCode(builder: CodeBuilder) { + builder.appendOperand(this, expression) append op + } } class ThisExpression(val identifier: Identifier) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "this" + identifier.withPrefix("@", commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.append("this").appendWithPrefix(identifier, "@") + } } class SuperExpression(val identifier: Identifier) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "super" + identifier.withPrefix("@", commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.append("super").appendWithPrefix(identifier, "@") + } } class QualifiedExpression(val qualifier: Expression, val identifier: Expression) : Expression() { override val isNullable: Boolean get() = identifier.isNullable - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { + override fun generateCode(builder: CodeBuilder) { if (!qualifier.isEmpty) { - return operandToKotlin(qualifier, commentsAndSpaces) + (if (qualifier.isNullable) "!!." else ".") + identifier.toKotlin(commentsAndSpaces) + builder.appendOperand(this, qualifier).append(if (qualifier.isNullable) "!!." else ".") } - return identifier.toKotlin(commentsAndSpaces) + builder.append(identifier) } } class PolyadicExpression(val expressions: List, val token: String) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - val expressionsWithConversions = expressions.map { it.toKotlin(commentsAndSpaces) } - return expressionsWithConversions.makeString(" " + token + " ") + override fun generateCode(builder: CodeBuilder) { + builder.append(expressions, " " + token + " ") } } class LambdaExpression(val arguments: String?, val block: Block) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - val statementsText = block.statements.toKotlin(commentsAndSpaces, "\n").trim() - val innerBody = if (arguments != null) { - val br = if (statementsText.indexOf('\n') < 0 && statementsText.indexOf('\r') < 0) " " else "\n" - "$arguments ->$br$statementsText" + override fun generateCode(builder: CodeBuilder) { + builder append block.lBrace append " " + + if (arguments != null) { + builder.append(arguments) + .append("->") + .append(if (block.statements.size > 1) "\n" else " ") + .append(block.statements, "\n") } else { - statementsText + builder.append(block.statements, "\n") } - return block.lBrace.toKotlin(commentsAndSpaces) + " " + innerBody + " " + block.rBrace.toKotlin(commentsAndSpaces) + + builder append " " append block.rBrace } } class StarExpression(val methodCall: MethodCallExpression) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "*" + methodCall.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append "*" append methodCall + } } fun createArrayInitializerExpression(arrayType: ArrayType, initializers: List, needExplicitType: Boolean) : MethodCallExpression { val elementType = arrayType.elementType val createArrayFunction = if (elementType is PrimitiveType) - (elementType.toNotNullType().toKotlin(CommentsAndSpaces.None) + "Array").decapitalize() + (elementType.toNotNullType().canonicalCode() + "Array").decapitalize() else if (needExplicitType) - arrayType.toNotNullType().toKotlin(CommentsAndSpaces.None).decapitalize() + arrayType.toNotNullType().canonicalCode().decapitalize() else "array" val doubleOrFloatTypes = setOf("double", "float", "java.lang.double", "java.lang.float") - val afterReplace = arrayType.toNotNullType().toKotlin(CommentsAndSpaces.None).replace("Array", "").toLowerCase().replace(">", "").replace("<", "").replace("?", "") + val afterReplace = arrayType.toNotNullType().canonicalCode().replace("Array", "").toLowerCase().replace(">", "").replace("<", "").replace("?", "") fun explicitConvertIfNeeded(initializer: Expression): Expression { if (doubleOrFloatTypes.contains(afterReplace)) { if (initializer is LiteralExpression) { - if (!initializer.toKotlin(CommentsAndSpaces.None).contains(".")) { + if (!initializer.canonicalCode().contains(".")) { return LiteralExpression(initializer.literalText + ".0") } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt index 20f27b093fa..495153bf717 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt @@ -29,20 +29,24 @@ open class Field( private val hasWriteAccesses: Boolean ) : Member(annotations, modifiers) { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - val declaration = annotations.toKotlin(commentsAndSpaces) + - modifiersToKotlin() + - (if (isVal) "val " else "var ") + - identifier.toKotlin(commentsAndSpaces) + - " : " + - `type`.toKotlin(commentsAndSpaces) - return if (initializer.isEmpty) - declaration + (if (isVal && hasWriteAccesses) "" else " = " + getDefaultInitializer(this).toKotlin(commentsAndSpaces)) - else - declaration + " = " + initializer.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.append(annotations) + .appendModifiers() + .append(if (isVal) "val " else "var ") + .append(identifier) + .append(" : ") + .append(`type`) + + var initializerToUse = initializer + if (initializerToUse.isEmpty && !(isVal && hasWriteAccesses)) { + initializerToUse = getDefaultInitializer(this) + } + if (!initializerToUse.isEmpty) { + builder append "=" append initializerToUse + } } - private fun modifiersToKotlin(): String { + private fun CodeBuilder.appendModifiers(): CodeBuilder { val modifierList = ArrayList() if (modifiers.contains(Modifier.ABSTRACT)) { modifierList.add(Modifier.ABSTRACT) @@ -50,6 +54,6 @@ open class Field( modifiers.accessModifier()?.let { modifierList.add(it) } - return modifierList.toKotlin() + return append(modifierList) } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/File.kt b/j2k/src/org/jetbrains/jet/j2k/ast/File.kt index e4173edd52c..01ec9127bab 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/File.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/File.kt @@ -16,10 +16,13 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.CodeBuilder +import org.jetbrains.jet.j2k.append class PackageStatement(val packageName: String) : Element() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String = "package " + packageName + override fun generateCode(builder: CodeBuilder) { + builder append "package " append packageName + } } class File( @@ -27,7 +30,7 @@ class File( val mainFunction: String ) : Element() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - return elements.toKotlin(commentsAndSpaces, "\n") + mainFunction + override fun generateCode(builder: CodeBuilder) { + builder.append(elements, "\n").append(mainFunction) } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt index 3298a2ffd84..e12535a2554 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt @@ -17,8 +17,7 @@ package org.jetbrains.jet.j2k.ast import java.util.ArrayList -import org.jetbrains.jet.j2k.Converter -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* open class Function( val converter: Converter, @@ -32,7 +31,7 @@ open class Function( val isInTrait: Boolean ) : Member(annotations, modifiers) { - private fun modifiersToKotlin(): String { + private fun CodeBuilder.appendModifiers(): CodeBuilder { val resultingModifiers = ArrayList() val isOverride = modifiers.contains(Modifier.OVERRIDE) if (isOverride) { @@ -52,16 +51,27 @@ open class Function( resultingModifiers.add(Modifier.OPEN) } - return resultingModifiers.toKotlin() + return append(resultingModifiers) } - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - return annotations.toKotlin(commentsAndSpaces) + - modifiersToKotlin() + - "fun ${typeParameterList.toKotlin(commentsAndSpaces).withSuffix(" ")}${name.toKotlin(commentsAndSpaces)}" + - "(${parameterList.toKotlin(commentsAndSpaces)})" + - (if (!`type`.isUnit()) " : " + `type`.toKotlin(commentsAndSpaces) + " " else " ") + - typeParameterList.whereToKotlin(commentsAndSpaces) + - block?.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.append(annotations) + .appendModifiers() + .append(" fun ") + .appendWithSuffix(typeParameterList, " ") + .append(name) + .append("(") + .append(parameterList) + .append(")") + + if (!`type`.isUnit()) { + builder append ":" append `type` + } + + typeParameterList.appendWhere(builder) + + if (block != null) { + builder append " " append block!! + } } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Identifier.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Identifier.kt index 4361838b0b6..6066082eda3 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Identifier.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Identifier.kt @@ -16,7 +16,7 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.CodeBuilder import com.intellij.psi.PsiNameIdentifierOwner fun PsiNameIdentifierOwner.declarationIdentifier(): Identifier { @@ -41,7 +41,9 @@ class Identifier( return name } - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String = toKotlin() + override fun generateCode(builder: CodeBuilder) { + builder.append(toKotlin()) + } private fun quote(str: String): String = "`" + str + "`" diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt index 50007b9a8c7..70d363d442f 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt @@ -25,14 +25,18 @@ import com.intellij.psi.PsiJavaCodeReferenceElement import org.jetbrains.jet.asJava.KotlinLightClassForPackage class Import(val name: String) : Element() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "import " + name + override fun generateCode(builder: CodeBuilder) { + builder append "import " append name + } } class ImportList(public val imports: List) : Element() { override val isEmpty: Boolean get() = imports.isEmpty() - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = imports.toKotlin(commentsAndSpaces, "\n") + override fun generateCode(builder: CodeBuilder) { + builder.append(imports, "\n") + } } public fun Converter.convertImportList(importList: PsiImportList): ImportList = diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Initializer.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Initializer.kt index 3386c36a33a..b2a6a317ad0 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Initializer.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Initializer.kt @@ -16,10 +16,10 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* class Initializer(val block: Block, modifiers: Set) : Member(Annotations.Empty, modifiers) { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - return block.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.append(block) } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/LocalVariable.kt b/j2k/src/org/jetbrains/jet/j2k/ast/LocalVariable.kt index ba225477abc..b80f19f9691 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/LocalVariable.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/LocalVariable.kt @@ -17,7 +17,8 @@ package org.jetbrains.jet.j2k.ast import org.jetbrains.jet.j2k.ConverterSettings -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.CodeBuilder +import org.jetbrains.jet.j2k.append class LocalVariable( private val identifier: Identifier, @@ -29,17 +30,17 @@ class LocalVariable( private val settings: ConverterSettings ) : Element() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - val start = annotations.toKotlin(commentsAndSpaces) + if (isVal) "val" else "var" - return if (initializer.isEmpty) { - "$start ${identifier.toKotlin(commentsAndSpaces)} : ${typeCalculator().toKotlin(commentsAndSpaces)}" + override fun generateCode(builder: CodeBuilder) { + builder.append(annotations).append(if (isVal) "val " else "var ") + if (initializer.isEmpty) { + builder append identifier append ":" append typeCalculator() } else { val shouldSpecifyType = settings.specifyLocalVariableTypeByDefault if (shouldSpecifyType) - "$start ${identifier.toKotlin(commentsAndSpaces)} : ${typeCalculator().toKotlin(commentsAndSpaces)} = ${initializer.toKotlin(commentsAndSpaces)}" + builder append identifier append ":" append typeCalculator() append "=" append initializer else - "$start ${identifier.toKotlin(commentsAndSpaces)} = ${initializer.toKotlin(commentsAndSpaces)}" + builder append identifier append "=" append initializer } } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/MethodCallExpression.kt b/j2k/src/org/jetbrains/jet/j2k/ast/MethodCallExpression.kt index 4cdeb67142f..2ef4af8227a 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/MethodCallExpression.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/MethodCallExpression.kt @@ -16,7 +16,7 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* class MethodCallExpression( val methodExpression: Expression, @@ -26,17 +26,14 @@ class MethodCallExpression( val lambdaArgument: LambdaExpression? = null ) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - val builder = StringBuilder() - builder.append(operandToKotlin(methodExpression, commentsAndSpaces)) - builder.append(typeArguments.toKotlin(commentsAndSpaces, ", ", "<", ">")) + override fun generateCode(builder: CodeBuilder) { + builder.appendOperand(this, methodExpression).append(typeArguments, ", ", "<", ">") if (arguments.isNotEmpty() || lambdaArgument == null) { - builder.append("(").append(arguments.map { it.toKotlin(commentsAndSpaces) }.makeString(", ")).append(")") + builder.append("(").append(arguments, ", ").append(")") } if (lambdaArgument != null) { - builder.append(lambdaArgument.toKotlin(commentsAndSpaces)) + builder.append(lambdaArgument) } - return builder.toString() } class object { diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Modifier.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Modifier.kt index 9baec7f14fd..985b646e510 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Modifier.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Modifier.kt @@ -16,7 +16,7 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.CodeBuilder enum class Modifier(val name: String) { PUBLIC: Modifier("public") @@ -36,7 +36,13 @@ fun Collection.accessModifier(): Modifier? { return firstOrNull { ACCESS_MODIFIERS.contains(it) } } -fun Collection.toKotlin(): String - = if (isNotEmpty()) map { it.toKotlin() }.makeString(" ") + " " else "" +fun CodeBuilder.append(modifiers: List): CodeBuilder { + if (!modifiers.isEmpty()) { + for (modifier in modifiers) { + append(modifier.toKotlin()).append(" ") + } + } + return this +} diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/NewClassExpression.kt b/j2k/src/org/jetbrains/jet/j2k/ast/NewClassExpression.kt index 0ffc786bfa8..bf4b77386c8 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/NewClassExpression.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/NewClassExpression.kt @@ -16,7 +16,7 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* class NewClassExpression( val name: Element, @@ -25,18 +25,23 @@ class NewClassExpression( val anonymousClass: AnonymousClassBody? = null ) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - val callOperator = if (qualifier.isNullable) "!!." else "." - val qualifier = if (qualifier.isEmpty) "" else qualifier.toKotlin(commentsAndSpaces) + callOperator - val appliedArguments = arguments.toKotlin(commentsAndSpaces, ", ") - return if (anonymousClass != null) { - if (anonymousClass.extendsTrait) - "object : " + qualifier + name.toKotlin(commentsAndSpaces) + anonymousClass.toKotlin(commentsAndSpaces) - else - "object : " + qualifier + name.toKotlin(commentsAndSpaces) + "(" + appliedArguments + ")" + anonymousClass.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + if (anonymousClass != null) { + builder.append("object:") } - else{ - qualifier + name.toKotlin(commentsAndSpaces) + "(" + appliedArguments + ")" + + if (!qualifier.isEmpty) { + builder.append(qualifier).append(if (qualifier.isNullable) "!!." else ".") + } + + builder.append(name) + + if (anonymousClass == null || !anonymousClass.extendsTrait) { + builder.append("(").append(arguments, ", ").append(")") + } + + if (anonymousClass != null) { + builder.append(anonymousClass) } } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Parameter.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Parameter.kt index 83d4ad89d9c..103ba544ff8 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Parameter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Parameter.kt @@ -16,24 +16,21 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* class Parameter(val identifier: Identifier, val `type`: Type, val varVal: Parameter.VarValModifier, val annotations: Annotations, - val modifiers: Collection) : Element() { + val modifiers: List) : Element() { public enum class VarValModifier { None Val Var } - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - val builder = StringBuilder() - - builder.append(annotations.toKotlin(commentsAndSpaces)) - builder.append(modifiers.toKotlin()) + override fun generateCode(builder: CodeBuilder) { + builder.append(annotations).append(modifiers) if (`type` is VarArgType) { assert(varVal == VarValModifier.None) @@ -45,7 +42,6 @@ class Parameter(val identifier: Identifier, VarValModifier.Val -> builder.append("val ") } - builder.append(identifier.toKotlin(commentsAndSpaces)).append(": ").append(`type`.toKotlin(commentsAndSpaces)) - return builder.toString() + builder.append(identifier).append(":").append(`type`) } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/ParameterList.kt b/j2k/src/org/jetbrains/jet/j2k/ast/ParameterList.kt index 096a58850b0..36d62b48e87 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/ParameterList.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/ParameterList.kt @@ -16,9 +16,11 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* class ParameterList(val parameters: List) : Element() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = parameters.map { it.toKotlin(commentsAndSpaces) }.makeString(", ") + override fun generateCode(builder: CodeBuilder) { + builder.append(parameters, ", ") + } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/ReferenceElement.kt b/j2k/src/org/jetbrains/jet/j2k/ast/ReferenceElement.kt index b9e5f1f4f5b..9e03f71646f 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/ReferenceElement.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/ReferenceElement.kt @@ -16,8 +16,10 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* class ReferenceElement(val reference: Identifier, val types: List) : Element() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = reference.toKotlin(commentsAndSpaces) + types.toKotlin(commentsAndSpaces, ", ", "<", ">") + override fun generateCode(builder: CodeBuilder) { + builder.append(reference).append(types, ", ", "<", ">") + } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Statements.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Statements.kt index e4bd15dbea4..91956cb8c85 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Statements.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Statements.kt @@ -16,12 +16,12 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* abstract class Statement() : Element() { object Empty : Statement() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "" + override fun generateCode(builder: CodeBuilder) { } override val isEmpty: Boolean get() = true @@ -29,20 +29,27 @@ abstract class Statement() : Element() { } class DeclarationStatement(val elements: List) : Statement() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String - = elements.filterIsInstance(javaClass()).map { it.toKotlin(commentsAndSpaces) }.makeString("\n") + override fun generateCode(builder: CodeBuilder) { + builder.append(elements, "\n") + } } class ExpressionListStatement(val expressions: List) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = expressions.toKotlin(commentsAndSpaces, "\n") + override fun generateCode(builder: CodeBuilder) { + builder.append(expressions, "\n") + } } class LabelStatement(val name: Identifier, val statement: Element) : Statement() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String = "@" + name.toKotlin(commentsAndSpaces) + " " + statement.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append "@" append name append " " append statement + } } class ReturnStatement(val expression: Expression) : Statement() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "return " + expression.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append "return " append expression + } } class IfStatement( @@ -53,12 +60,11 @@ class IfStatement( ) : Expression() { private val br = if (singleLine) " " else "\n" - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - val result = "if (" + condition.toKotlin(commentsAndSpaces) + ")$br" + thenStatement.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append "if (" append condition append ")" append br append thenStatement if (!elseStatement.isEmpty) { - return "$result${br}else$br${elseStatement.toKotlin(commentsAndSpaces)}" + builder append br append "else" append br append elseStatement } - return result } } @@ -67,13 +73,17 @@ class IfStatement( class WhileStatement(val condition: Expression, val body: Element, singleLine: Boolean) : Statement() { private val br = if (singleLine) " " else "\n" - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "while (" + condition.toKotlin(commentsAndSpaces) + ")$br" + body.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append "while (" append condition append ")" append br append body + } } class DoWhileStatement(val condition: Expression, val body: Element, singleLine: Boolean) : Statement() { private val br = if (singleLine) " " else "\n" - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "do$br" + body.toKotlin(commentsAndSpaces) + "${br}while (" + condition.toKotlin(commentsAndSpaces) + ")" + override fun generateCode(builder: CodeBuilder) { + builder append "do" append br append body append br append "while (" append condition append ")" + } } class ForeachStatement( @@ -85,7 +95,9 @@ class ForeachStatement( private val br = if (singleLine) " " else "\n" - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "for (" + variable.identifier.toKotlin(commentsAndSpaces) + " in " + expression.toKotlin(commentsAndSpaces) + ")$br" + body.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append "for (" append variable.identifier append " in " append expression append ")" append br append body + } } class ForeachWithRangeStatement(val identifier: Identifier, @@ -95,64 +107,78 @@ class ForeachWithRangeStatement(val identifier: Identifier, singleLine: Boolean) : Statement() { private val br = if (singleLine) " " else "\n" - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "for (" + identifier.toKotlin(commentsAndSpaces) + " in " + start.toKotlin(commentsAndSpaces) + ".." + end.toKotlin(commentsAndSpaces) + ")$br" + body.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append "for (" append identifier append " in " append start append ".." append end append ")" append br append body + } } class BreakStatement(val label: Identifier = Identifier.Empty) : Statement() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "break" + label.withPrefix("@", commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.append("break").appendWithPrefix(label, "@") + } } class ContinueStatement(val label: Identifier = Identifier.Empty) : Statement() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "continue" + label.withPrefix("@", commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.append("continue").appendWithPrefix(label, "@") + } } // Exceptions ---------------------------------------------------------------------------------------------- class TryStatement(val block: Block, val catches: List, val finallyBlock: Block) : Statement() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - val builder = StringBuilder() - .append("try\n") - .append(block.toKotlin(commentsAndSpaces)) - .append("\n") - .append(catches.toKotlin(commentsAndSpaces, "\n")) - .append("\n") + override fun generateCode(builder: CodeBuilder) { + builder.append("try\n").append(block).append("\n").append(catches, "\n").append("\n") if (!finallyBlock.isEmpty) { - builder.append("finally\n").append(finallyBlock.toKotlin(commentsAndSpaces)) + builder append "finally\n" append finallyBlock } - return builder.toString() } } class ThrowStatement(val expression: Expression) : Expression() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "throw " + expression.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append "throw " append expression + } } class CatchStatement(val variable: Parameter, val block: Block) : Statement() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String = "catch (" + variable.toKotlin(commentsAndSpaces) + ") " + block.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append "catch (" append variable append ") " append block + } } // Switch -------------------------------------------------------------------------------------------------- class SwitchContainer(val expression: Expression, val caseContainers: List) : Statement() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "when (" + expression.toKotlin(commentsAndSpaces) + ") {\n" + caseContainers.toKotlin(commentsAndSpaces, "\n") + "\n}" + override fun generateCode(builder: CodeBuilder) { + builder.append("when (").append(expression).append(") {\n").append(caseContainers, "\n").append("\n}") + } } class CaseContainer(val caseStatement: List, statements: List) : Statement() { private val block = Block(statements.filterNot { it is BreakStatement || it is ContinueStatement }, LBrace(), RBrace(), true) - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = caseStatement.toKotlin(commentsAndSpaces, ", ") + " -> " + block.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.append(caseStatement, ", ").append(" -> ").append(block) + } } class SwitchLabelStatement(val expression: Expression) : Statement() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = expression.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.append(expression) + } } class DefaultSwitchLabelStatement() : Statement() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "else" + override fun generateCode(builder: CodeBuilder) { + builder.append("else") + } } // Other ------------------------------------------------------------------------------------------------------ class SynchronizedStatement(val expression: Expression, val block: Block) : Statement() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "synchronized (" + expression.toKotlin(commentsAndSpaces) + ") " + block.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append "synchronized (" append expression append ") " append block + } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Trait.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Trait.kt index 462e9dc0477..f174b7f7c8e 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Trait.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Trait.kt @@ -17,7 +17,7 @@ package org.jetbrains.jet.j2k.ast import java.util.ArrayList -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.CodeBuilder class Trait(name: Identifier, annotations: Annotations, @@ -32,12 +32,12 @@ class Trait(name: Identifier, override val keyword: String get() = "trait" - override fun primaryConstructorSignatureToKotlin(commentsAndSpaces: CommentsAndSpaces) = "" + override fun appendPrimaryConstructorSignature(builder: CodeBuilder) { } - override fun modifiersToKotlin(): String { + override fun appendModifiers(builder: CodeBuilder): CodeBuilder { val modifierList = ArrayList() modifiers.accessModifier()?.let { modifierList.add(it) } - return modifierList.toKotlin() + return builder.append(modifierList) } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/TypeElement.kt b/j2k/src/org/jetbrains/jet/j2k/ast/TypeElement.kt index 4259c1804cb..b33e322a564 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/TypeElement.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/TypeElement.kt @@ -16,8 +16,10 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* class TypeElement(val `type`: Type) : Element() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = `type`.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.append(`type`) + } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/TypeParameters.kt b/j2k/src/org/jetbrains/jet/j2k/ast/TypeParameters.kt index b77cca56b31..049d69702ee 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/TypeParameters.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/TypeParameters.kt @@ -17,45 +17,37 @@ package org.jetbrains.jet.j2k.ast import com.intellij.psi.PsiTypeParameter -import org.jetbrains.jet.j2k.Converter +import org.jetbrains.jet.j2k.* import com.intellij.psi.PsiTypeParameterList import java.util.ArrayList -import org.jetbrains.jet.j2k.CommentsAndSpaces class TypeParameter(val name: Identifier, val extendsTypes: List) : Element() { fun hasWhere(): Boolean = extendsTypes.size() > 1 - fun whereToKotlin(commentsAndSpaces: CommentsAndSpaces): String { + fun whereToKotlin(builder: CodeBuilder) { if (hasWhere()) { - return name.toKotlin(commentsAndSpaces) + " : " + extendsTypes[1].toKotlin(commentsAndSpaces) + builder append name append " : " append extendsTypes[1] } - - return "" } - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - if (extendsTypes.size() > 0) { - return name.toKotlin(commentsAndSpaces) + " : " + extendsTypes[0].toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append name + if (extendsTypes.isNotEmpty()) { + builder append " : " append extendsTypes[0] } - - return name.toKotlin(commentsAndSpaces) } } class TypeParameterList(val parameters: List) : Element() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - return if (parameters.isNotEmpty()) - parameters.map { it.toKotlin(commentsAndSpaces) }.makeString(", ", "<", ">") - else - "" + override fun generateCode(builder: CodeBuilder) { + if (parameters.isNotEmpty()) builder.append(parameters, ", ", "<", ">") } - fun whereToKotlin(commentsAndSpaces: CommentsAndSpaces): String { + fun appendWhere(builder: CodeBuilder): CodeBuilder { if (hasWhere()) { - val wheres = parameters.map { it.whereToKotlin(commentsAndSpaces) } - return "where " + wheres.makeString(", ") + builder.append( parameters.map { { it.whereToKotlin(builder) } }, ", ", " where ", "") } - return "" + return builder } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Types.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Types.kt index 4894ee96bbd..6acfa64caf2 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Types.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Types.kt @@ -16,8 +16,7 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.ConverterSettings -import org.jetbrains.jet.j2k.CommentsAndSpaces +import org.jetbrains.jet.j2k.* fun Type.isUnit(): Boolean = this == Type.Unit @@ -59,42 +58,45 @@ abstract class Type() : Element() { } object Empty : NotNullType() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String = "UNRESOLVED_TYPE" + override fun generateCode(builder: CodeBuilder) { + builder.append("UNRESOLVED_TYPE") + } } object Unit: NotNullType() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces) = "Unit" + override fun generateCode(builder: CodeBuilder) { + builder.append("Unit") + } } - override fun equals(other: Any?): Boolean = other is Type && other.toKotlin(CommentsAndSpaces.None) == this.toKotlin(CommentsAndSpaces.None) + override fun equals(other: Any?): Boolean = other is Type && other.canonicalCode() == this.canonicalCode() - override fun hashCode(): Int = toKotlin(CommentsAndSpaces.None).hashCode() + override fun hashCode(): Int = canonicalCode().hashCode() - override fun toString(): String = toKotlin(CommentsAndSpaces.None) + override fun toString(): String = canonicalCode() } -class ClassType(val `type`: Identifier, val typeArgs: List, nullability: Nullability, settings: ConverterSettings) +class ClassType(val name: Identifier, val typeArgs: List, nullability: Nullability, settings: ConverterSettings) : MayBeNullableType(nullability, settings) { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { - var params = if (typeArgs.isEmpty()) "" else typeArgs.map { it.toKotlin(commentsAndSpaces) }.makeString(", ", "<", ">") - return `type`.toKotlin(commentsAndSpaces) + params + isNullableStr + override fun generateCode(builder: CodeBuilder) { + builder.append(name).append(typeArgs, ", ", "<", ">").append(isNullableStr) } - - override fun toNotNullType(): Type = ClassType(`type`, typeArgs, Nullability.NotNull, settings) - override fun toNullableType(): Type = ClassType(`type`, typeArgs, Nullability.Nullable, settings) + override fun toNotNullType(): Type = ClassType(name, typeArgs, Nullability.NotNull, settings) + override fun toNullableType(): Type = ClassType(name, typeArgs, Nullability.Nullable, settings) } class ArrayType(val elementType: Type, nullability: Nullability, settings: ConverterSettings) : MayBeNullableType(nullability, settings) { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String { + override fun generateCode(builder: CodeBuilder) { if (elementType is PrimitiveType) { - return elementType.toKotlin(commentsAndSpaces) + "Array" + isNullableStr + builder append elementType append "Array" append isNullableStr + } + else { + builder append "Array<" append elementType append ">" append isNullableStr } - - return "Array<" + elementType.toKotlin(commentsAndSpaces) + ">" + isNullableStr } override fun toNotNullType(): Type = ArrayType(elementType, Nullability.NotNull, settings) @@ -102,21 +104,31 @@ class ArrayType(val elementType: Type, nullability: Nullability, settings: Conve } class InProjectionType(val bound: Type) : NotNullType() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String = "in " + bound.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append "in " append bound + } } class OutProjectionType(val bound: Type) : NotNullType() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String = "out " + bound.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder append "out " append bound + } } class StarProjectionType() : NotNullType() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String = "*" + override fun generateCode(builder: CodeBuilder) { + builder.append("*") + } } -class PrimitiveType(val `type`: Identifier) : NotNullType() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String = `type`.toKotlin(commentsAndSpaces) +class PrimitiveType(val name: Identifier) : NotNullType() { + override fun generateCode(builder: CodeBuilder) { + builder.append(name) + } } class VarArgType(val `type`: Type) : NotNullType() { - override fun toKotlinImpl(commentsAndSpaces: CommentsAndSpaces): String = `type`.toKotlin(commentsAndSpaces) + override fun generateCode(builder: CodeBuilder) { + builder.append(`type`) + } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Util.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Util.kt index 4f719813a55..f42384e21f6 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Util.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Util.kt @@ -16,23 +16,23 @@ package org.jetbrains.jet.j2k.ast -import org.jetbrains.jet.j2k.CommentsAndSpaces - -fun List.toKotlin(commentsAndSpaces: CommentsAndSpaces, separator: String, prefix: String = "", postfix: String = ""): String { - val texts = map { it.toKotlin(commentsAndSpaces) }.filter { it.isNotEmpty() } - return if (texts.isNotEmpty()) texts.makeString(separator, prefix, postfix) else "" -} +import org.jetbrains.jet.j2k.* fun String.withSuffix(suffix: String): String = if (isEmpty()) "" else this + suffix fun String.withPrefix(prefix: String): String = if (isEmpty()) "" else prefix + this -fun Expression.withPrefix(prefix: String, commentsAndSpaces: CommentsAndSpaces): String = if (isEmpty) "" else prefix + toKotlin(commentsAndSpaces) -fun Expression.operandToKotlin(operand: Expression, commentsAndSpaces: CommentsAndSpaces, parenthesisForSamePrecedence: Boolean = false): String { - val parentPrecedence = precedence() ?: throw IllegalArgumentException("Unknown precendence for $this") - val kotlinCode = operand.toKotlin(commentsAndSpaces) - val operandPrecedence = operand.precedence() ?: return kotlinCode - val needParenthesis = parentPrecedence < operandPrecedence || parentPrecedence == operandPrecedence && parenthesisForSamePrecedence - return if (needParenthesis) "($kotlinCode)" else kotlinCode +fun CodeBuilder.appendWithPrefix(element: Element, prefix: String): CodeBuilder = if (!element.isEmpty) this append prefix append element else this +fun CodeBuilder.appendWithSuffix(element: Element, suffix: String): CodeBuilder = if (!element.isEmpty) this append element append suffix else this + +fun CodeBuilder.appendOperand(expression: Expression, operand: Expression, parenthesisForSamePrecedence: Boolean = false): CodeBuilder { + val parentPrecedence = expression.precedence() ?: throw IllegalArgumentException("Unknown precendence for $this") + val operandPrecedence = operand.precedence() + val needParenthesis = operandPrecedence != null && + (parentPrecedence < operandPrecedence || parentPrecedence == operandPrecedence && parenthesisForSamePrecedence) + if (needParenthesis) append("(") + append(operand) + if (needParenthesis) append(")") + return this } private fun Expression.precedence(): Int? { diff --git a/j2k/src/org/jetbrains/jet/j2k/visitors/TypeVisitor.kt b/j2k/src/org/jetbrains/jet/j2k/visitors/TypeVisitor.kt index 9ab3db30cec..39f6ef5a425 100644 --- a/j2k/src/org/jetbrains/jet/j2k/visitors/TypeVisitor.kt +++ b/j2k/src/org/jetbrains/jet/j2k/visitors/TypeVisitor.kt @@ -51,7 +51,7 @@ class TypeVisitor(private val converter: TypeConverter, private val classesToImp if (classType.getParameterCount() == 0 && resolvedClassTypeParams.size() > 0) { val starParamList = ArrayList() if (resolvedClassTypeParams.size() == 1) { - if ((resolvedClassTypeParams.single() as ClassType).`type`.name == "Any") { + if ((resolvedClassTypeParams.single() as ClassType).name.name == "Any") { starParamList.add(StarProjectionType()) return ClassType(identifier, starParamList, Nullability.Default, converter.settings) } diff --git a/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt b/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt index 664f8435fb2..859f5c5ea88 100644 --- a/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt +++ b/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt @@ -145,7 +145,7 @@ abstract class AbstractJavaToKotlinConverterTest() : LightIdeaTestCase() { private fun expressionToKotlin(code: String, settings: ConverterSettings, project: Project): String { val result = statementToKotlin("final Object o =" + code + "}", settings, project) - return result.replaceFirst("val o : Any\\? =", "").replaceFirst("val o : Any = ", "").replaceFirst("val o = ", "").trim() + return result.replaceFirst("val o:Any\\?=", "").replaceFirst("val o:Any=", "").replaceFirst("val o=", "").trim() } override fun getProjectJDK(): Sdk? { diff --git a/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java b/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java index b01a2402ac0..3f394093cb3 100644 --- a/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java +++ b/j2k/tests/test/org/jetbrains/jet/j2k/test/JavaToKotlinConverterTestGenerated.java @@ -737,9 +737,19 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("j2k/tests/testData/ast/comments"), Pattern.compile("^(.+)\\.java$"), true); } - @TestMetadata("Comments.java") + @TestMetadata("comments.java") public void testComments() throws Exception { - doTest("j2k/tests/testData/ast/comments/Comments.java"); + doTest("j2k/tests/testData/ast/comments/comments.java"); + } + + @TestMetadata("fieldWithEndOfLineComment.java") + public void testFieldWithEndOfLineComment() throws Exception { + doTest("j2k/tests/testData/ast/comments/fieldWithEndOfLineComment.java"); + } + + @TestMetadata("fieldsInitializedFromParams.java") + public void testFieldsInitializedFromParams() throws Exception { + doTest("j2k/tests/testData/ast/comments/fieldsInitializedFromParams.java"); } } diff --git a/j2k/tests/testData/ast/comments/fieldWithEndOfLineComment.java b/j2k/tests/testData/ast/comments/fieldWithEndOfLineComment.java new file mode 100644 index 00000000000..340ae032186 --- /dev/null +++ b/j2k/tests/testData/ast/comments/fieldWithEndOfLineComment.java @@ -0,0 +1,4 @@ +//file +class A { + private boolean isOpen = true; // ideally should be atomic boolean +} \ No newline at end of file diff --git a/j2k/tests/testData/ast/comments/fieldWithEndOfLineComment.kt b/j2k/tests/testData/ast/comments/fieldWithEndOfLineComment.kt new file mode 100644 index 00000000000..ec3f0bf94e6 --- /dev/null +++ b/j2k/tests/testData/ast/comments/fieldWithEndOfLineComment.kt @@ -0,0 +1,3 @@ +class A() { + private var isOpen: Boolean = true // ideally should be atomic boolean +} \ No newline at end of file diff --git a/j2k/tests/testData/ast/comments/fieldsInitializedFromParams.java b/j2k/tests/testData/ast/comments/fieldsInitializedFromParams.java new file mode 100644 index 00000000000..efcc082ae6f --- /dev/null +++ b/j2k/tests/testData/ast/comments/fieldsInitializedFromParams.java @@ -0,0 +1,17 @@ +//file +class C { + private final int p1; // field p1 + + /** + * Field myP2 + */ + private final int myP2; + + /* Field p3 */ public int p3; + + public C(int p1 /* parameter p1 */, int p2, int p3) { + this.p1 = p1; + myP2 = p2; + this.p3 = p3; + } +} diff --git a/j2k/tests/testData/ast/comments/fieldsInitializedFromParams.kt b/j2k/tests/testData/ast/comments/fieldsInitializedFromParams.kt new file mode 100644 index 00000000000..f8654e83f46 --- /dev/null +++ b/j2k/tests/testData/ast/comments/fieldsInitializedFromParams.kt @@ -0,0 +1,6 @@ +class C(private val p1: Int /* parameter p1 */ // field p1 + , + /** + * Field myP2 + */ + private val myP2: Int, /* Field p3 */ public var p3: Int)