Java to Kotlin convertor: more code improvements

This commit is contained in:
Valentin Kipyatkov
2014-05-30 20:34:16 +04:00
parent aa8b968756
commit 846f2d9954
7 changed files with 134 additions and 216 deletions
@@ -21,8 +21,8 @@ import com.intellij.psi.PsiClass
import com.intellij.psi.PsiReferenceExpression
import com.intellij.psi.JavaRecursiveElementVisitor
import java.util.LinkedHashSet
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethodCallExpression
fun PsiMethod.isPrimaryConstructor(): Boolean {
if (!isConstructor()) return false
@@ -64,12 +64,9 @@ fun PsiClass.getPrimaryConstructor(): PsiMethod? {
}
fun isInsidePrimaryConstructor(element: PsiElement): Boolean
= containingConstructor(element)?.isPrimaryConstructor() ?: false
= getContainingConstructor(element)?.isPrimaryConstructor() ?: false
fun isInsideSecondaryConstructor(element: PsiElement): Boolean
= !(containingConstructor(element)?.isPrimaryConstructor() ?: true)
fun containingConstructor(element: PsiElement): PsiMethod? {
fun getContainingConstructor(element: PsiElement): PsiMethod? {
var context = element.getContext()
while (context != null) {
val _context = context!!
@@ -82,10 +79,14 @@ fun containingConstructor(element: PsiElement): PsiMethod? {
return null
}
fun isSuperConstructorRef(ref: PsiReference): Boolean {
fun PsiMethodCallExpression.isSuperConstructorCall(): Boolean {
val ref = getMethodExpression()
if (ref.getCanonicalText().equals("super")) {
val target = ref.resolve()
return target is PsiMethod && target.isConstructor()
}
return false
}
fun PsiReferenceExpression.isThisConstructorCall(): Boolean
= getReferences().filter { it.getCanonicalText() == "this" }.map { it.resolve() }.any { it is PsiMethod && it.isConstructor() }
+68 -72
View File
@@ -55,7 +55,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
is PsiJavaFile -> convertFile(element)
is PsiClass -> convertClass(element)
is PsiMethod -> convertMethod(element)
is PsiField -> convertField(element, element.getContainingClass())
is PsiField -> convertField(element)
is PsiStatement -> convertStatement(element)
is PsiExpression -> convertExpression(element)
is PsiComment -> Comment(element.getText()!!)
@@ -78,7 +78,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
private fun convertMembers(psiClass: PsiClass): List<Element> {
val allChildren = psiClass.getChildren().toList()
val lBraceIndex = allChildren.indexOf(psiClass.getLBrace())
return allChildren.subList(lBraceIndex, allChildren.size).map { convertMember(it, psiClass) }.filterNotNull()
return allChildren.subList(lBraceIndex, allChildren.size).map { convertMember(it) }.filterNotNull()
}
private fun getComments(member: PsiMember): MemberComments {
@@ -93,9 +93,9 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
return MemberComments(whiteSpacesAndComments)
}
private fun convertMember(e: PsiElement?, containingClass: PsiClass): Element? = when(e) {
private fun convertMember(e: PsiElement?): Element? = when(e) {
is PsiMethod -> convertMethod(e, true)
is PsiField -> convertField(e, containingClass)
is PsiField -> convertField(e)
is PsiClass -> convertClass(e)
is PsiClassInitializer -> convertInitializer(e)
else -> convertElement(e)
@@ -103,90 +103,86 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
private fun convertClass(psiClass: PsiClass): Class {
val modifiers = convertModifierList(psiClass.getModifierList())
val fields = convertFields(psiClass.getFields(), psiClass)
val typeParameters = convertTypeParameterList(psiClass.getTypeParameterList())
val implementsTypes = convertToNotNullableTypes(psiClass.getImplementsListTypes())
val extendsTypes = convertToNotNullableTypes(psiClass.getExtendsListTypes())
val name = Identifier(psiClass.getName()!!)
val members = ArrayList(convertMembers(psiClass))
val baseClassParams: List<Expression> = run {
val superVisitor = SuperVisitor()
psiClass.accept(superVisitor)
val resolvedSuperCallParameters = superVisitor.resolvedSuperCallParameters
if (resolvedSuperCallParameters.size() == 1) {
convertExpressions(resolvedSuperCallParameters.single().getExpressions())
}
else {
listOf()
}
}
when {
psiClass.isInterface() -> return Trait(this, name, getComments(psiClass), modifiers, typeParameters, extendsTypes, listOf(), implementsTypes, members)
if (!psiClass.isEnum() && !psiClass.isInterface() && psiClass.getConstructors().size > 1 && psiClass.getPrimaryConstructor() == null) {
val finalOrWithEmptyInitializer = fields.filter { it.isVal() || it.initializer.toKotlin().isEmpty() }
val initializers = HashMap<String, String>()
for (member in members) {
if (member is Constructor && !member.isPrimary) {
for (field in finalOrWithEmptyInitializer) {
initializers.put(field.identifier.toKotlin(), getDefaultInitializer(field))
}
psiClass.isEnum() -> return Enum(this, name, getComments(psiClass), modifiers, typeParameters, listOf(), listOf(), implementsTypes, members)
val newStatements = ArrayList<Statement>()
for (statement in member.block!!.statements) {
var isRemoved = false
if (statement is AssignmentExpression) {
val assignee = statement.left
if (assignee is CallChainExpression) {
for (field in finalOrWithEmptyInitializer) {
val id = field.identifier.toKotlin()
if (assignee.identifier.toKotlin().endsWith("." + id)) {
initializers.put(id, statement.right.toKotlin())
isRemoved = true
else -> {
if (psiClass.getConstructors().size > 1 && psiClass.getPrimaryConstructor() == null) {
val finalOrWithEmptyInitializerFields = members.filterIsInstance(javaClass<Field>()).filter { it.isVal() || it.initializer.toKotlin().isEmpty() }
val initializers = HashMap<String, String>()
for (member in members) {
if (member is Constructor && !member.isPrimary) {
for (field in finalOrWithEmptyInitializerFields) {
initializers.put(field.identifier.toKotlin(), getDefaultInitializer(field))
}
val newStatements = ArrayList<Statement>()
for (statement in member.block!!.statements) {
var keepStatement = true
if (statement is AssignmentExpression) {
val assignee = statement.left
if (assignee is CallChainExpression) {
for (field in finalOrWithEmptyInitializerFields) {
val id = field.identifier.toKotlin()
if (assignee.identifier.toKotlin().endsWith("." + id)) {
initializers.put(id, statement.right.toKotlin())
keepStatement = false
}
}
}
}
if (keepStatement) {
newStatements.add(statement)
}
}
newStatements.add(0, DummyStringExpression("val __ = " + createPrimaryConstructorInvocation(name.toKotlin(), finalOrWithEmptyInitializerFields, initializers)))
member.block = Block(newStatements)
}
if (!isRemoved) {
newStatements.add(statement)
}
}
newStatements.add(0, DummyStringExpression("val __ = " + createPrimaryConstructorInvocation(name.toKotlin(), finalOrWithEmptyInitializer, initializers)))
member.block = Block(newStatements)
//TODO: comments?
members.add(Constructor(this, Identifier.Empty, MemberComments.Empty, Collections.emptySet<Modifier>(),
ClassType(name, listOf(), false, this),
TypeParameterList.Empty,
ParameterList(createParametersFromFields(finalOrWithEmptyInitializerFields)),
Block(createInitStatementsFromFields(finalOrWithEmptyInitializerFields)),
true))
}
val baseClassParams: List<Expression> = run {
val superVisitor = SuperVisitor()
psiClass.accept(superVisitor)
val resolvedSuperCallParameters = superVisitor.resolvedSuperCallParameters
if (resolvedSuperCallParameters.size() == 1) {
convertExpressions(resolvedSuperCallParameters.single().getExpressions())
}
else {
listOf()
}
}
return Class(this, name, getComments(psiClass), modifiers, typeParameters, extendsTypes, baseClassParams, implementsTypes, members)
}
//TODO: comments?
members.add(Constructor(this, Identifier.Empty, MemberComments.Empty, Collections.emptySet<Modifier>(),
ClassType(name, listOf(), false, this),
TypeParameterList.Empty,
ParameterList(createParametersFromFields(finalOrWithEmptyInitializer)),
Block(createInitStatementsFromFields(finalOrWithEmptyInitializer)),
true))
}
if (psiClass.isInterface()) {
return Trait(this, name, getComments(psiClass), modifiers, typeParameters, extendsTypes, listOf(), implementsTypes, members)
}
if (psiClass.isEnum()) {
return Enum(this, name, getComments(psiClass), modifiers, typeParameters, listOf(), listOf(), implementsTypes, members)
}
return Class(this, name, getComments(psiClass), modifiers, typeParameters, extendsTypes, baseClassParams, implementsTypes, members)
}
private fun convertInitializer(initializer: PsiClassInitializer): Initializer {
return Initializer(convertBlock(initializer.getBody(), true), convertModifierList(initializer.getModifierList()))
}
private fun convertFields(fields: Array<PsiField>, psiClass: PsiClass): List<Field> {
return fields.map { convertField(it, psiClass) }
}
private fun convertField(field: PsiField, psiClass: PsiClass?): Field {
private fun convertField(field: PsiField): Field {
val modifiers = convertModifierList(field.getModifierList())
if (field is PsiEnumConstant) {
return EnumConstant(Identifier(field.getName()!!),
@@ -196,8 +192,8 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
convertElement(field.getArgumentList()))
}
var kType = convertType(field.getType(), isAnnotatedAsNotNull(field.getModifierList()))
if (field.hasModifierProperty(PsiModifier.FINAL) && isDefinitelyNotNull(field.getInitializer())) {
var kType = convertType(field.getType(), field.isAnnotatedAsNotNull())
if (field.hasModifierProperty(PsiModifier.FINAL) && field.getInitializer().isDefinitelyNotNull()) {
kType = kType.convertedToNotNull();
}
@@ -206,7 +202,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
modifiers,
kType,
convertExpression(field.getInitializer(), field.getType()),
countWriteAccesses(field, psiClass))
field.countWriteAccesses(field.getContainingClass()))
}
private fun convertMethod(method: PsiMethod): Function {
@@ -222,7 +218,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
}
methodReturnType = method.getReturnType()
val identifier = Identifier(method.getName())
val returnType = convertType(method.getReturnType(), isAnnotatedAsNotNull(method.getModifierList()))
val returnType = convertType(method.getReturnType(), method.isAnnotatedAsNotNull())
val body = convertBlock(method.getBody(), notEmpty)
val params = createFunctionParameters(method)
@@ -253,8 +249,8 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
val result = ArrayList<Parameter>()
for (parameter in method.getParameterList().getParameters()) {
result.add(Parameter(Identifier(parameter.getName()!!),
convertType(parameter.getType(), isAnnotatedAsNotNull(parameter.getModifierList())),
isReadOnly(parameter, method.getBody())))
convertType(parameter.getType(), parameter.isAnnotatedAsNotNull()),
parameter.countWriteAccesses(method.getBody()) == 0))
}
return ParameterList(result)
}
@@ -339,7 +335,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
public fun convertParameter(parameter: PsiParameter, forceNotNull: Boolean = false): Parameter {
return Parameter(Identifier(parameter.getName()!!),
convertType(parameter.getType(),
forceNotNull || isAnnotatedAsNotNull(parameter.getModifierList())), true)
forceNotNull || parameter.isAnnotatedAsNotNull()), true)
}
public fun convertArguments(expression: PsiCallExpression): List<Expression> {
+15 -32
View File
@@ -19,53 +19,36 @@ package org.jetbrains.jet.j2k
import org.jetbrains.jet.j2k.ast.Identifier
import com.intellij.psi.PsiElement
import com.intellij.psi.JavaRecursiveElementVisitor
import java.util.ArrayList
import com.intellij.psi.PsiReferenceExpression
import com.intellij.psi.util.PsiUtil
import com.intellij.psi.PsiModifierList
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiLiteralExpression
import com.intellij.psi.PsiNewExpression
import org.jetbrains.jet.j2k.ast.Field
import org.jetbrains.jet.lang.types.expressions.OperatorConventions
import com.intellij.psi.util.PsiUtil
import com.intellij.psi.PsiModifierListOwner
fun quoteKeywords(packageName: String): String = packageName.split("\\.").map { Identifier(it).toKotlin() }.makeString(".")
fun countWriteAccesses(element: PsiElement?, container: PsiElement?): Int {
if (container == null) return 0
class ReferenceCollector() : JavaRecursiveElementVisitor() {
private val _collectedReferences = ArrayList<PsiReferenceExpression>()
public val collectedReferences: List<PsiReferenceExpression>
get() = _collectedReferences
fun PsiElement.countWriteAccesses(scope: PsiElement?): Int {
if (scope == null) return 0
var writes = 0
scope.accept(object: JavaRecursiveElementVisitor() {
override fun visitReferenceExpression(expression: PsiReferenceExpression) {
super.visitReferenceExpression(expression)
_collectedReferences.add(expression)
if (PsiUtil.isAccessedForWriting(expression) && expression.isReferenceTo(this@countWriteAccesses)) {
writes++
}
}
}
val collector = ReferenceCollector()
container.accept(collector)
return collector.collectedReferences.count { it.isReferenceTo(element) && PsiUtil.isAccessedForWriting(it) }
})
return writes
}
fun isReadOnly(element: PsiElement?, container: PsiElement?): Boolean
= countWriteAccesses(element, container) == 0
fun PsiModifierListOwner.isAnnotatedAsNotNull(): Boolean
= getModifierList()?.getAnnotations()?.any { NOT_NULL_ANNOTATIONS.contains(it.getQualifiedName()) } ?: false
fun isAnnotatedAsNotNull(modifierList: PsiModifierList?): Boolean {
if (modifierList != null) {
for (annotation in modifierList.getAnnotations()) {
val qualifiedName = annotation.getQualifiedName()
return qualifiedName != null && NOT_NULL_ANNOTATIONS.contains(qualifiedName)
}
}
return false
}
fun isDefinitelyNotNull(element: PsiElement?): Boolean = when(element) {
is PsiLiteralExpression -> element.getValue() != null
fun PsiElement?.isDefinitelyNotNull(): Boolean = when(this) {
is PsiLiteralExpression -> getValue() != null
is PsiNewExpression -> true
else -> false
}
@@ -26,9 +26,9 @@ class ElementVisitor(public val converter: Converter) : JavaElementVisitor() {
protected set
override fun visitLocalVariable(variable: PsiLocalVariable) {
var kType = converter.convertType(variable.getType(), isAnnotatedAsNotNull(variable.getModifierList()))
if (variable.hasModifierProperty(PsiModifier.FINAL) && isDefinitelyNotNull(variable.getInitializer())) {
kType = kType.convertedToNotNull();
var kType = converter.convertType(variable.getType(), variable.isAnnotatedAsNotNull())
if (variable.hasModifierProperty(PsiModifier.FINAL) && variable.getInitializer().isDefinitelyNotNull()) {
kType = kType.convertedToNotNull()
}
result = LocalVariable(Identifier(variable.getName()!!),
converter.convertModifierList(variable.getModifierList()),
@@ -141,7 +141,7 @@ open class ExpressionVisitor(public val converter: Converter) : JavaElementVisit
}
protected fun convertMethodCallExpression(expression: PsiMethodCallExpression) {
if (!isSuperConstructorRef(expression.getMethodExpression()) || !isInsidePrimaryConstructor(expression)) {
if (!expression.isSuperConstructorCall() || !isInsidePrimaryConstructor(expression)) {
result = MethodCallExpression(converter.convertExpression(expression.getMethodExpression()),
converter.convertArguments(expression),
converter.convertTypes(expression.getTypeArguments()),
@@ -212,21 +212,20 @@ open class ExpressionVisitor(public val converter: Converter) : JavaElementVisit
}
override fun visitReferenceExpression(expression: PsiReferenceExpression) {
val isFieldReference = isFieldReference(expression, getContainingClass(expression))
val insideSecondaryConstructor = isInsideSecondaryConstructor(expression)
val hasReceiver = isFieldReference && insideSecondaryConstructor
val isThis = isThisExpression(expression)
val notNull = isResolvedToNotNull(expression)
val isNullable = converter.convertType(expression.getType(), notNull).isNullable
val className = getClassNameWithConstructor(expression)
val containingConstructor = getContainingConstructor(expression)
val insideSecondaryConstructor = containingConstructor != null && !containingConstructor.isPrimaryConstructor()
val addReceiver = insideSecondaryConstructor && (expression.getReference()?.resolve() as? PsiField)?.getContainingClass() == containingConstructor!!.getContainingClass()
val isNullable = converter.convertType(expression.getType(), expression.isResolvedToNotNull()).isNullable
val referencedName = expression.getReferenceName()!!
var identifier: Expression = Identifier(referencedName, isNullable)
val qualifier = expression.getQualifierExpression()
if (hasReceiver) {
if (addReceiver) {
identifier = CallChainExpression(Identifier("__", false), Identifier(referencedName, isNullable))
}
else if (insideSecondaryConstructor && isThis) {
identifier = Identifier("val __ = " + className)
else if (insideSecondaryConstructor && expression.isThisConstructorCall()) {
identifier = Identifier("val __ = " + (containingConstructor?.getContainingClass()?.getNameIdentifier()?.getText() ?: ""))
}
else if (qualifier != null && qualifier.getType() is PsiArrayType && referencedName == "length") {
identifier = Identifier("size", isNullable)
@@ -259,15 +258,13 @@ open class ExpressionVisitor(public val converter: Converter) : JavaElementVisit
result = CallChainExpression(converter.convertExpression(qualifier), identifier)
}
private fun isResolvedToNotNull(expression: PsiReference): Boolean {
val target = expression.resolve()
if (target is PsiEnumConstant) {
return true;
private fun PsiReference.isResolvedToNotNull(): Boolean {
val target = resolve()
return when(target) {
is PsiEnumConstant -> true
is PsiModifierListOwner -> target.isAnnotatedAsNotNull()
else -> false
}
if (target is PsiModifierListOwner) {
return isAnnotatedAsNotNull(target.getModifierList());
}
return false;
}
override fun visitSuperExpression(expression: PsiSuperExpression) {
@@ -287,19 +284,17 @@ open class ExpressionVisitor(public val converter: Converter) : JavaElementVisit
}
override fun visitTypeCastExpression(expression: PsiTypeCastExpression) {
val castType: PsiTypeElement? = expression.getCastType()
if (castType != null) {
val operand = expression.getOperand()
val operandType = operand?.getType()
val typeText = castType.getType().getCanonicalText()
val typeConversion = PRIMITIVE_TYPE_CONVERSIONS[typeText]
if (operandType is PsiPrimitiveType && typeConversion != null) {
result = MethodCallExpression.build(converter.convertExpression(operand), typeConversion)
}
else {
result = TypeCastExpression(converter.convertType(castType.getType()),
converter.convertExpression(operand))
}
val castType = expression.getCastType() ?: return
val operand = expression.getOperand()
val operandType = operand?.getType()
val typeText = castType.getType().getCanonicalText()
val typeConversion = PRIMITIVE_TYPE_CONVERSIONS[typeText]
if (operandType is PsiPrimitiveType && typeConversion != null) {
result = MethodCallExpression.build(converter.convertExpression(operand), typeConversion)
}
else {
result = TypeCastExpression(converter.convertType(castType.getType()),
converter.convertExpression(operand))
}
}
@@ -339,26 +334,6 @@ open class ExpressionVisitor(public val converter: Converter) : JavaElementVisit
}
}
private fun getClassNameWithConstructor(expression: PsiReferenceExpression): String {
var context = expression.getContext()
while (context != null) {
val _context = context!!
if (_context is PsiMethod && _context.isConstructor()) {
val containingClass = _context.getContainingClass()
if (containingClass != null) {
val identifier = containingClass.getNameIdentifier()
if (identifier != null) {
return identifier.getText()!!
}
}
}
context = _context.getContext()
}
return ""
}
protected fun getClassName(expression: PsiExpression): String {
var context = expression.getContext()
while (context != null) {
@@ -376,45 +351,6 @@ open class ExpressionVisitor(public val converter: Converter) : JavaElementVisit
return ""
}
private fun isFieldReference(expression: PsiReferenceExpression, currentClass: PsiClass?): Boolean {
val reference = expression.getReference()
if (reference != null) {
val target = reference.resolve()
if (target is PsiField) {
return target.getContainingClass() == currentClass
}
}
return false
}
private fun getContainingClass(expression: PsiExpression): PsiClass? {
var context = expression.getContext()
while (context != null) {
val _context = context!!
if (_context is PsiMethod && _context.isConstructor()) {
return _context.getContainingClass()
}
context = _context.getContext()
}
return null
}
private fun isThisExpression(expression: PsiReferenceExpression): Boolean {
for (ref in expression.getReferences()) {
if (ref.getCanonicalText() == "this") {
val target = ref.resolve()
if (target is PsiMethod && target.isConstructor()) {
return true
}
}
}
return false
}
private fun isStaticallyImported(member: PsiMember, context: PsiElement): Boolean {
val containingFile = context.getContainingFile()
val targetContainingClass = member.getContainingClass()
@@ -79,11 +79,13 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() {
val update = statement.getUpdate()
val condition = statement.getCondition()
val body = statement.getBody()
val firstChildLocalVar = initialization?.getFirstChild() as? PsiLocalVariable
var bodyWriteCount = countWriteAccesses(firstChildLocalVar, body)
var conditionWriteCount = countWriteAccesses(firstChildLocalVar, condition)
var updateWriteCount = countWriteAccesses(firstChildLocalVar, update)
val onceWritableIterator = updateWriteCount == 1 && bodyWriteCount + conditionWriteCount == 0
val initializationVar = initialization?.getFirstChild() as? PsiLocalVariable
val onceWritableIterator = initializationVar != null
&& initializationVar.countWriteAccesses(body) == 0
&& initializationVar.countWriteAccesses(condition) == 0
&& initializationVar.countWriteAccesses(update) == 1
val operationTokenType = (condition as? PsiBinaryExpression)?.getOperationTokenType()
if (initialization is PsiDeclarationStatement
&& initialization.getFirstChild() == initialization.getLastChild()
@@ -92,16 +94,16 @@ class StatementVisitor(public val converter: Converter) : JavaElementVisitor() {
&& update.getChildren().size == 1
&& isPlusPlusExpression(update.getChildren().single())
&& (operationTokenType == JavaTokenType.LT || operationTokenType == JavaTokenType.LE)
&& firstChildLocalVar != null
&& firstChildLocalVar.getNameIdentifier() != null
&& initializationVar != null
&& initializationVar.getNameIdentifier() != null
&& onceWritableIterator) {
val end = converter.convertExpression((condition as PsiBinaryExpression).getROperand())
val endExpression = if (operationTokenType == JavaTokenType.LT)
BinaryExpression(end, Identifier("1"), "-")
else
end
result = ForeachWithRangeStatement(Identifier(firstChildLocalVar.getName()!!),
converter.convertExpression(firstChildLocalVar.getInitializer()),
result = ForeachWithRangeStatement(Identifier(initializationVar.getName()!!),
converter.convertExpression(initializationVar.getInitializer()),
endExpression,
converter.convertStatement(body))
}
@@ -18,7 +18,7 @@ package org.jetbrains.jet.j2k.visitors
import com.intellij.psi.*
import java.util.HashSet
import org.jetbrains.jet.j2k.isSuperConstructorRef
import org.jetbrains.jet.j2k.isSuperConstructorCall
class SuperVisitor() : JavaRecursiveElementVisitor() {
private val _resolvedSuperCallParameters = HashSet<PsiExpressionList>()
@@ -27,7 +27,7 @@ class SuperVisitor() : JavaRecursiveElementVisitor() {
get() = _resolvedSuperCallParameters
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
if (isSuperConstructorRef(expression.getMethodExpression())) {
if (expression.isSuperConstructorCall()) {
_resolvedSuperCallParameters.add(expression.getArgumentList())
}
}