Java to Kotlin converter: working on not loosing comments - refactored redundant .toKotlin() calls

This commit is contained in:
Valentin Kipyatkov
2014-06-17 13:32:03 +04:00
parent 7463da8fe0
commit 600446bbd2
13 changed files with 75 additions and 69 deletions
@@ -124,7 +124,7 @@ public class JavaToKotlinActionUtil {
if (psiFile instanceof PsiJavaFile && virtualFile != null) {
String result = "";
try {
result = converter.convertFile((PsiJavaFile) psiFile).toKotlin();
result = converter.elementToKotlin(psiFile);
} catch (Exception e) {
//noinspection CallToPrintStackTrace
e.printStackTrace();
+34 -29
View File
@@ -60,7 +60,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
= Converter(project, settings, conversionScope, State(typeConverter, state.methodReturnType, state.expressionVisitorFactory, factory))
public fun elementToKotlin(element: PsiElement): String
= convertTopElement(element)?.toKotlin() ?: ""
= convertTopElement(element)?.toKotlin(/*CommentConverter(element)*/) ?: ""
private fun convertTopElement(element: PsiElement?): Element? = when(element) {
is PsiJavaFile -> convertFile(element)
@@ -69,7 +69,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
is PsiField -> convertField(element)
is PsiStatement -> convertStatement(element)
is PsiExpression -> convertExpression(element)
is PsiComment -> Comment(element.getText()!!)
is PsiComment -> Comment(element.getText()!!/*, listOf(element)*/)
is PsiImportList -> convertImportList(element)
is PsiImportStatementBase -> convertImport(element, false)
is PsiAnnotation -> convertAnnotation(element, false)
@@ -78,7 +78,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
else -> null
}
public fun convertFile(javaFile: PsiJavaFile): File {
fun convertFile(javaFile: PsiJavaFile): File {
var convertedChildren = javaFile.getChildren().map {
if (it is PsiImportList) {
val importList = convertImportList(it)
@@ -100,7 +100,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
return File(FileMemberList(convertedChildren), createMainFunction(javaFile))
}
public fun convertAnonymousClassBody(anonymousClass: PsiAnonymousClass): AnonymousClassBody {
fun convertAnonymousClassBody(anonymousClass: PsiAnonymousClass): AnonymousClassBody {
return AnonymousClassBody(convertClassBody(anonymousClass), anonymousClass.getBaseClassType().resolve()?.isInterface() ?: false)
}
@@ -248,13 +248,13 @@ public class Converter private(val project: Project, val settings: ConverterSett
private fun generateArtificialPrimaryConstructor(className: Identifier, classBody: ClassBody): ClassBody {
assert(classBody.primaryConstructor == null)
val finalOrWithEmptyInitializerFields = classBody.normalMembers.members.filterIsInstance(javaClass<Field>()).filter { it.isVal || it.initializer.toKotlin().isEmpty() }
val initializers = HashMap<String, String>()
val finalOrWithEmptyInitializerFields = classBody.normalMembers.members.filterIsInstance(javaClass<Field>()).filter { it.isVal || it.initializer.isEmpty }
val initializers = HashMap<Field, Expression>()
for (constructor in classBody.secondaryConstructors.members) {
constructor as SecondaryConstructor
for (field in finalOrWithEmptyInitializerFields) {
initializers.put(field.identifier.toKotlin(), getDefaultInitializer(field))
initializers.put(field, getDefaultInitializer(field))
}
val newStatements = ArrayList<Statement>()
@@ -262,11 +262,11 @@ public class Converter private(val project: Project, val settings: ConverterSett
var keepStatement = true
if (statement is AssignmentExpression) {
val assignee = statement.left
if (assignee is QualifiedExpression) {
if (assignee is QualifiedExpression && (assignee.qualifier as? Identifier)?.name == SecondaryConstructor.tempValIdentifier.name) {
val name = (assignee.identifier as Identifier).name
for (field in finalOrWithEmptyInitializerFields) {
val id = field.identifier.toKotlin()
if (assignee.identifier.toKotlin().endsWith("." + id)) {
initializers.put(id, statement.right.toKotlin())
if (name == field.identifier.name) {
initializers.put(field, statement.right)
keepStatement = false
}
@@ -280,7 +280,16 @@ public class Converter private(val project: Project, val settings: ConverterSett
}
}
newStatements.add(0, DummyStringExpression("val __ = " + createPrimaryConstructorInvocation(className.toKotlin(), finalOrWithEmptyInitializerFields, initializers)))
val initializer = MethodCallExpression.buildNotNull(null, className.name, finalOrWithEmptyInitializerFields.map { initializers[it]!! })
val localVar = LocalVariable(SecondaryConstructor.tempValIdentifier,
Annotations.Empty,
setOf(),
{ ClassType(className, listOf(), Nullability.NotNull, settings) },
initializer,
true,
settings)
newStatements.add(0, DeclarationStatement(listOf(localVar)))
constructor.block = Block(newStatements)
}
@@ -506,7 +515,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
return null
}
public fun convertBlock(block: PsiCodeBlock?, notEmpty: Boolean = true, statementFilter: (PsiStatement) -> Boolean = {true}): Block {
fun convertBlock(block: PsiCodeBlock?, notEmpty: Boolean = true, statementFilter: (PsiStatement) -> Boolean = {true}): Block {
if (block == null) return Block.Empty
val filteredChildren = block.getChildren().filter { it !is PsiStatement || statementFilter(it) }
@@ -514,7 +523,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
return Block(statementList, notEmpty)
}
public fun convertStatement(statement: PsiStatement?): Statement {
fun convertStatement(statement: PsiStatement?): Statement {
if (statement == null) return Statement.Empty
statementVisitor.reset()
@@ -522,10 +531,10 @@ public class Converter private(val project: Project, val settings: ConverterSett
return statementVisitor.result
}
public fun convertExpressions(expressions: Array<PsiExpression>): List<Expression>
fun convertExpressions(expressions: Array<PsiExpression>): List<Expression>
= expressions.map { convertExpression(it) }
public fun convertExpression(expression: PsiExpression?): Expression {
fun convertExpression(expression: PsiExpression?): Expression {
if (expression == null) return Expression.Empty
expressionVisitor.reset()
@@ -533,7 +542,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
return expressionVisitor.result
}
public fun convertElement(element: PsiElement?): Element {
fun convertElement(element: PsiElement?): Element {
if (element == null) return Element.Empty
val elementVisitor = ElementVisitor(this)
@@ -541,16 +550,16 @@ public class Converter private(val project: Project, val settings: ConverterSett
return elementVisitor.result
}
public fun convertTypeElement(element: PsiTypeElement?): TypeElement
fun convertTypeElement(element: PsiTypeElement?): TypeElement
= TypeElement(if (element == null) Type.Empty else typeConverter.convertType(element.getType()))
private fun convertToNotNullableTypes(types: Array<out PsiType?>): List<Type>
= types.map { typeConverter.convertType(it, Nullability.NotNull) }
public fun convertParameterList(parameterList: PsiParameterList): ParameterList
fun convertParameterList(parameterList: PsiParameterList): ParameterList
= ParameterList(parameterList.getParameters().map { convertParameter(it) })
public fun convertParameter(parameter: PsiParameter,
fun convertParameter(parameter: PsiParameter,
nullability: Nullability = Nullability.Default,
varValModifier: Parameter.VarValModifier = Parameter.VarValModifier.None,
modifiers: Collection<Modifier> = listOf()): Parameter {
@@ -562,7 +571,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
return Parameter(Identifier(parameter.getName()!!), `type`, varValModifier, convertAnnotations(parameter), modifiers)
}
public fun convertExpression(argument: PsiExpression?, expectedType: PsiType?): Expression {
fun convertExpression(argument: PsiExpression?, expectedType: PsiType?): Expression {
if (argument == null) return Identifier.Empty
var expression = convertExpression(argument)
@@ -590,17 +599,13 @@ public class Converter private(val project: Project, val settings: ConverterSett
return expression
}
private fun createPrimaryConstructorInvocation(s: String, fields: List<Field>, initializers: Map<String, String>): String {
return s + "(" + fields.map { initializers[it.identifier.toKotlin()] }.makeString(", ") + ")"
}
public fun convertIdentifier(identifier: PsiIdentifier?): Identifier {
fun convertIdentifier(identifier: PsiIdentifier?): Identifier {
if (identifier == null) return Identifier.Empty
return Identifier(identifier.getText()!!)
}
public fun convertModifiers(owner: PsiModifierListOwner): MutableSet<Modifier>
fun convertModifiers(owner: PsiModifierListOwner): MutableSet<Modifier>
= HashSet(MODIFIERS_MAP.filter { owner.hasModifierProperty(it.first) }.map { it.second })
private val MODIFIERS_MAP = listOf(
@@ -610,7 +615,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
PsiModifier.PRIVATE to Modifier.PRIVATE
)
public fun convertAnnotations(owner: PsiModifierListOwner): Annotations {
fun convertAnnotations(owner: PsiModifierListOwner): Annotations {
val modifierList = owner.getModifierList()
val annotations = modifierList?.getAnnotations()?.filter { it.getQualifiedName() !in ANNOTATIONS_TO_REMOVE }
if (annotations == null || annotations.isEmpty()) return Annotations.Empty
@@ -633,7 +638,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
return Annotations(list, newLines)
}
public fun convertAnnotation(annotation: PsiAnnotation, brackets: Boolean): Annotation? {
private fun convertAnnotation(annotation: PsiAnnotation, brackets: Boolean): Annotation? {
val qualifiedName = annotation.getQualifiedName()
if (qualifiedName == CommonClassNames.JAVA_LANG_DEPRECATED && annotation.getParameterList().getAttributes().isEmpty()) {
return Annotation(Identifier("deprecated"), listOf(null to LiteralExpression("\"\"")), brackets) //TODO: insert comment
@@ -90,7 +90,7 @@ public object JavaToKotlinTranslator {
val file = createFile(javaCode)
if (file is PsiJavaFile) {
val converter = Converter.create(file.getProject(), ConverterSettings.defaultSettings, FilesConversionScope(listOf(file)))
return prettify(converter.convertFile(file).toKotlin())
return prettify(converter.elementToKotlin(file))
}
return ""
}
+14 -13
View File
@@ -16,14 +16,12 @@
package org.jetbrains.jet.j2k
import org.jetbrains.jet.j2k.ast.Identifier
import org.jetbrains.jet.j2k.ast.Field
import org.jetbrains.jet.lang.types.expressions.OperatorConventions
import org.jetbrains.jet.j2k.ast.Nullability
import com.intellij.psi.*
import com.intellij.psi.util.PsiUtil
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.jet.j2k.ast.*
fun quoteKeywords(packageName: String): String = packageName.split("\\.").map { Identifier(it).toKotlin() }.makeString(".")
@@ -59,19 +57,22 @@ fun PsiModifierListOwner.nullabilityFromAnnotations(): Nullability {
Nullability.Default
}
fun getDefaultInitializer(field: Field): String {
if (field.`type`.isNullable) {
return "null"
fun getDefaultInitializer(field: Field): Expression {
val t = field.`type`
if (t.isNullable) {
return LiteralExpression("null")
}
else {
return when(field.`type`.toKotlin()) {
"Boolean" -> "false"
"Char" -> "' '"
"Double" -> "0." + OperatorConventions.DOUBLE + "()"
"Float" -> "0." + OperatorConventions.FLOAT + "()"
else -> "0"
if (t is PrimitiveType) {
when(t.`type`.name) {
"Boolean" -> return LiteralExpression("false")
"Char" -> return LiteralExpression("' '")
"Double" -> return MethodCallExpression.buildNotNull(LiteralExpression("0"), OperatorConventions.DOUBLE.toString())
"Float" -> return MethodCallExpression.buildNotNull(LiteralExpression("0"), OperatorConventions.FLOAT.toString())
}
}
return LiteralExpression("0")
}
fun isQualifierEmptyOrThis(ref: PsiReferenceExpression): Boolean {
@@ -57,7 +57,7 @@ class SecondaryConstructor(converter: Converter,
public fun toInitFunction(containingClass: Class): Function {
val modifiers = HashSet(modifiers)
val statements = ArrayList(block?.statements ?: listOf())
statements.add(ReturnStatement(Identifier("__")))
statements.add(ReturnStatement(tempValIdentifier))
val block = Block(statements)
val typeParameters = ArrayList<TypeParameter>()
typeParameters.addAll(containingClass.typeParameterList.parameters)
@@ -65,4 +65,8 @@ class SecondaryConstructor(converter: Converter,
ClassType(containingClass.name, typeParameters, Nullability.NotNull, converter.settings),
TypeParameterList(typeParameters), parameterList, block, false)
}
class object {
public val tempValIdentifier: Identifier = Identifier("__", false)
}
}
@@ -71,13 +71,13 @@ class SuperExpression(val identifier: Identifier) : Expression() {
override fun toKotlin() = "super" + identifier.withPrefix("@")
}
class QualifiedExpression(val expression: Expression, val identifier: Expression) : Expression() {
class QualifiedExpression(val qualifier: Expression, val identifier: Expression) : Expression() {
override val isNullable: Boolean
get() = identifier.isNullable
override fun toKotlin(): String {
if (!expression.isEmpty) {
return operandToKotlin(expression) + (if (expression.isNullable) "!!." else ".") + identifier.toKotlin()
if (!qualifier.isEmpty) {
return operandToKotlin(qualifier) + (if (qualifier.isNullable) "!!." else ".") + identifier.toKotlin()
}
return identifier.toKotlin()
+1 -1
View File
@@ -33,7 +33,7 @@ open class Field(
override fun toKotlin(): String {
val declaration = commentsToKotlin() + annotations.toKotlin() + modifiersToKotlin() + (if (isVal) "val " else "var ") + identifier.toKotlin() + " : " + `type`.toKotlin()
return if (initializer.isEmpty)
declaration + (if (isVal && hasWriteAccesses) "" else " = " + getDefaultInitializer(this))
declaration + (if (isVal && hasWriteAccesses) "" else " = " + getDefaultInitializer(this).toKotlin())
else
declaration + " = " + initializer.toKotlin()
}
@@ -26,7 +26,7 @@ class Identifier(
override val isEmpty: Boolean
get() = name.isEmpty()
override fun toKotlin(): String {
private fun identifierToKotlin(): String {
if (quotingNeeded && ONLY_KOTLIN_KEYWORDS.contains(name) || name.contains("$")) {
return quote(name)
}
@@ -34,6 +34,8 @@ class Identifier(
return name
}
override fun toKotlin(): String = identifierToKotlin()
private fun quote(str: String): String = "`" + str + "`"
override fun toString() = if (isNullable) "$name?" else name
@@ -43,6 +45,8 @@ class Identifier(
val ONLY_KOTLIN_KEYWORDS: Set<String> = setOf(
"package", "as", "type", "val", "var", "fun", "is", "in", "object", "when", "trait", "This"
);
)
fun toKotlin(name: String): String = Identifier(name).identifierToKotlin()
}
}
+1 -9
View File
@@ -79,15 +79,7 @@ class ClassType(val `type`: Identifier, val typeArgs: List<Element>, nullability
: MayBeNullableType(nullability, settings) {
override fun toKotlin(): String {
// TODO change to map() when KT-2051 is fixed
val parametersToKotlin = ArrayList<String>()
for (param in typeArgs) {
parametersToKotlin.add(param.toKotlin())
}
var params: String = if (parametersToKotlin.size() == 0)
""
else
"<" + parametersToKotlin.makeString(", ") + ">"
var params = if (typeArgs.isEmpty()) "" else typeArgs.map { it.toKotlin() }.makeString(", ", "<", ">")
return `type`.toKotlin() + params + isNullableStr()
}
@@ -46,11 +46,11 @@ class ElementVisitor(private val converter: Converter) : JavaElementVisitor() {
result = ReferenceElement(Identifier(reference.getReferenceName()!!), types)
}
else {
var code = Identifier(reference.getReferenceName()!!).toKotlin()
var code = Identifier.toKotlin(reference.getReferenceName()!!)
var qualifier = reference.getQualifier()
while (qualifier != null) {
val p = qualifier as PsiJavaCodeReferenceElement
code = Identifier(p.getReferenceName()!!).toKotlin() + "." + code
code = Identifier.toKotlin(p.getReferenceName()!!) + "." + code
qualifier = p.getQualifier()
}
result = ReferenceElement(Identifier(code), types)
@@ -306,7 +306,7 @@ class ExpressionVisitor(private val converter: Converter,
val insideSecondaryConstructor = containingConstructor != null && !containingConstructor.isPrimaryConstructor()
if (insideSecondaryConstructor && (expression.getReference()?.resolve() as? PsiField)?.getContainingClass() == containingConstructor!!.getContainingClass()) {
identifier = QualifiedExpression(Identifier("__", false), Identifier(referencedName, isNullable))
identifier = QualifiedExpression(SecondaryConstructor.tempValIdentifier, Identifier(referencedName, isNullable))
}
else if (insideSecondaryConstructor && expression.isThisConstructorCall()) {
identifier = Identifier("val __ = " + (containingConstructor?.getContainingClass()?.getNameIdentifier()?.getText() ?: ""))
@@ -336,9 +336,9 @@ class ExpressionVisitor(private val converter: Converter,
&& !PsiTreeUtil.isAncestor(target.getContainingClass(), expression, true)
&& !isStaticallyImported(target, expression)) {
var member: PsiMember = target
var code = Identifier(referencedName).toKotlin()
var code = Identifier.toKotlin(referencedName)
while (member.getContainingClass() != null) {
code = Identifier(member.getContainingClass()!!.getName()!!).toKotlin() + "." + code
code = Identifier.toKotlin(member.getContainingClass()!!.getName()!!) + "." + code
member = member.getContainingClass()!!
}
result = Identifier(code, false, false)
@@ -354,7 +354,7 @@ class ExpressionVisitor(private val converter: Converter,
}
result = QualifiedExpression(converter.convertExpression(qualifier), identifier)
result = if (qualifier != null) QualifiedExpression(converter.convertExpression(qualifier), identifier) else identifier
}
override fun visitSuperExpression(expression: PsiSuperExpression) {
@@ -273,7 +273,7 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
var statementList = bodyConverted.statementList
var expression: Expression = Expression.Empty
for (variable in variables.reverse()) {
val lambda = LambdaExpression(Identifier(variable.getName()!!).toKotlin(), statementList)
val lambda = LambdaExpression(Identifier.toKotlin(variable.getName()!!), statementList)
expression = MethodCallExpression.build(converter.convertExpression(variable.getInitializer()), "use", listOf(), listOf(), false, lambda)
statementList = StatementList(listOf(expression))
}
@@ -85,11 +85,11 @@ class TypeVisitor(private val converter: TypeConverter, private val classesToImp
if (classType is PsiClassReferenceType) {
val reference = classType.getReference()
if (reference.isQualified()) {
var result = Identifier(reference.getReferenceName()!!).toKotlin()
var result = Identifier.toKotlin(reference.getReferenceName()!!)
var qualifier = reference.getQualifier()
while (qualifier != null) {
val codeRefElement = qualifier as PsiJavaCodeReferenceElement
result = Identifier(codeRefElement.getReferenceName()!!).toKotlin() + "." + result
result = Identifier.toKotlin(codeRefElement.getReferenceName()!!) + "." + result
qualifier = codeRefElement.getQualifier()
}
return Identifier(result)