Java to Kotlin converter: refactoring working with constructors (in progress)

This commit is contained in:
Valentin Kipyatkov
2014-06-25 19:39:32 +04:00
parent b7bcb0d9e3
commit 0edd9d24be
8 changed files with 87 additions and 65 deletions
+56 -21
View File
@@ -122,22 +122,22 @@ public class Converter private(val project: Project, val settings: ConverterSett
convertedMembers.remove(member)
}
val primaryConstructor = convertedMembers.values().filterIsInstance(javaClass<PrimaryConstructor>()).firstOrNull()
val secondaryConstructors = convertedMembers.values().filterIsInstance(javaClass<SecondaryConstructor>())
val useClassObject = shouldGenerateClassObject(psiClass, convertedMembers)
val members = ArrayList<Member>()
val classObjectMembers = ArrayList<Member>()
var primaryConstructorSignature: PrimaryConstructorSignature? = null
for ((psiMember, member) in convertedMembers) {
if (member is SecondaryConstructor) continue
if (member is PrimaryConstructor) {
assert(primaryConstructorSignature == null)
primaryConstructorSignature = member.signature()
val initializer = member.initializer()
if (initializer != null) {
members.add(initializer)
}
}
else if (useClassObject && psiMember !is PsiClass && psiMember.hasModifierProperty(PsiModifier.STATIC)) {
else if (useClassObject && psiMember !is PsiClass && psiMember.hasModifierProperty(PsiModifier.STATIC) ||
member is FactoryFunction) {
classObjectMembers.add(member)
}
else {
@@ -147,7 +147,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
val lBrace = LBrace().assignPrototype(psiClass.getLBrace())
val rBrace = RBrace().assignPrototype(psiClass.getRBrace())
return ClassBody(primaryConstructor?.signature(), members, secondaryConstructors, classObjectMembers, lBrace, rBrace)
return ClassBody(primaryConstructorSignature, members, classObjectMembers, lBrace, rBrace)
}
// do not convert private static methods into class object if possible
@@ -190,6 +190,9 @@ public class Converter private(val project: Project, val settings: ConverterSett
if (psiClass.getPrimaryConstructor() == null && psiClass.getConstructors().size > 1) {
classBody = generateArtificialPrimaryConstructor(name, classBody)
}
else {
correctFactoryFunctions(classBody, psiClass.getName()!!)
}
val baseClassParams: List<Expression> = run {
val superVisitor = SuperVisitor()
@@ -216,22 +219,24 @@ public class Converter private(val project: Project, val settings: ConverterSett
}.assignPrototype(psiClass)
}
private fun ClassBody.factoryFunctions() = classObjectMembers.filterIsInstance(javaClass<FactoryFunction>())
private fun generateArtificialPrimaryConstructor(className: Identifier, classBody: ClassBody): ClassBody {
assert(classBody.primaryConstructorSignature == null)
val finalOrWithEmptyInitializerFields = classBody.members.filterIsInstance(javaClass<Field>()).filter { it.isVal || it.initializer.isEmpty }
val initializers = HashMap<Field, Expression>()
for (constructor in classBody.secondaryConstructors) {
for (factoryFunction in classBody.factoryFunctions()) {
for (field in finalOrWithEmptyInitializerFields) {
initializers.put(field, getDefaultInitializer(field))
}
val newStatements = ArrayList<Statement>()
for (statement in constructor.block!!.statements) {
val statements = ArrayList<Statement>()
for (statement in factoryFunction.body!!.statements) {
var keepStatement = true
if (statement is AssignmentExpression) {
val assignee = statement.left
if (assignee is QualifiedExpression && (assignee.qualifier as? Identifier)?.name == SecondaryConstructor.tempValName) {
if (assignee is QualifiedExpression && (assignee.qualifier as? Identifier)?.name == FactoryFunction.tempValName) {
val name = (assignee.identifier as Identifier).name
for (field in finalOrWithEmptyInitializerFields) {
if (name == field.identifier.name) {
@@ -245,21 +250,21 @@ public class Converter private(val project: Project, val settings: ConverterSett
}
if (keepStatement) {
newStatements.add(statement)
statements.add(statement)
}
}
val initializer = MethodCallExpression.buildNotNull(null, className.name, finalOrWithEmptyInitializerFields.map { initializers[it]!! })
initializer.assignNoPrototype()
val localVar = LocalVariable(SecondaryConstructor.tempValIdentifier(),
val localVar = LocalVariable(FactoryFunction.tempValIdentifier(),
Annotations.Empty,
Modifiers.Empty,
null,
initializer,
true).assignNoPrototype()
newStatements.add(0, DeclarationStatement(listOf(localVar)).assignNoPrototype())
constructor.block = Block(newStatements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
statements.add(0, DeclarationStatement(listOf(localVar)).assignNoPrototype())
statements.add(ReturnStatement(FactoryFunction.tempValIdentifier()).assignNoPrototype())
factoryFunction.body = Block(statements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
}
val parameters = finalOrWithEmptyInitializerFields.map { field ->
@@ -271,7 +276,31 @@ public class Converter private(val project: Project, val settings: ConverterSett
val parameterList = ParameterList(parameters).assignNoPrototype()
val constructorSignature = PrimaryConstructorSignature(modifiers, parameterList).assignNoPrototype()
val updatedMembers = classBody.members.filter { !finalOrWithEmptyInitializerFields.contains(it) }
return ClassBody(constructorSignature, updatedMembers, classBody.secondaryConstructors, classBody.classObjectMembers, classBody.lBrace, classBody.rBrace)
return ClassBody(constructorSignature, updatedMembers, classBody.classObjectMembers, classBody.lBrace, classBody.rBrace)
}
private fun correctFactoryFunctions(classBody: ClassBody, className: String) {
for (factoryFunction in classBody.factoryFunctions()) {
val body = factoryFunction.body!!
val statements = ArrayList(body.statements)
// searching for other constructor call in form "this(...)"
// it's not necessary the first statement because of statements inserted for writable parameters
for (i in statements.indices) {
val statement = statements[i]
if (statement is MethodCallExpression) {
if ((statement.methodExpression as? Identifier)?.name == "this") {
val constructorCall = MethodCallExpression.buildNotNull(null, className, statement.arguments).assignPrototypesFrom(statement)
val localVar = LocalVariable(FactoryFunction.tempValIdentifier(), Annotations.Empty, Modifiers.Empty, null, constructorCall, true).assignNoPrototype()
statements[i] = DeclarationStatement(listOf(localVar)).assignNoPrototype()
break
}
}
}
statements.add(ReturnStatement(FactoryFunction.tempValIdentifier()).assignNoPrototype())
factoryFunction.body = Block(statements, body.lBrace, body.rBrace).assignPrototypesFrom(body)
}
}
private fun convertInitializer(initializer: PsiClassInitializer): Initializer {
@@ -344,11 +373,11 @@ public class Converter private(val project: Project, val settings: ConverterSett
return false
}
private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>): Function {
private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>): Member {
return withMethodReturnType(method.getReturnType()).doConvertMethod(method, membersToRemove).assignPrototype(method)
}
private fun doConvertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>): Function {
private fun doConvertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>): Member {
val returnType = typeConverter.convertMethodReturnType(method)
val annotations = (convertAnnotations(method) + convertThrows(method)).assignNoPrototype()
@@ -382,7 +411,13 @@ public class Converter private(val project: Project, val settings: ConverterSett
else {
val params = convertParameterList(method.getParameterList())
var body = postProcessBody(convertBlock(method.getBody()))
return SecondaryConstructor(this, annotations, modifiers, params, body)
val containingClass = method.getContainingClass()
val typeParameterList = convertTypeParameterList(containingClass?.getTypeParameterList())
val factoryFunctionType = ClassType(containingClass?.declarationIdentifier() ?: Identifier.Empty,
typeParameterList.parameters,
Nullability.NotNull,
settings).assignNoPrototype()
return FactoryFunction(annotations, modifiers, factoryFunctionType, params, typeParameterList, body)
}
}
else {
@@ -404,7 +439,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
var params = convertParameterList(method.getParameterList())
val typeParameterList = convertTypeParameterList(method.getTypeParameterList())
var body = postProcessBody(convertBlock(method.getBody()))
return Function(this, method.declarationIdentifier(), annotations, modifiers, returnType, typeParameterList, params, body, containingClass?.isInterface() ?: false)
return Function(method.declarationIdentifier(), annotations, modifiers, returnType, typeParameterList, params, body, containingClass?.isInterface() ?: false)
}
}
@@ -499,7 +534,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
convertModifiers(field).filter { it in ACCESS_MODIFIERS }).assignPrototypes(listOf(parameter, field), CommentsAndSpacesInheritance(blankLinesBefore = false))
}
}).assignPrototype(constructor.getParameterList())
return PrimaryConstructor(this, annotations, modifiers, parameterList, block).assignPrototype(constructor)
return PrimaryConstructor(annotations, modifiers, parameterList, block).assignPrototype(constructor)
}
private fun findBackingFieldForConstructorParameter(parameter: PsiParameter, constructor: PsiMethod): Pair<PsiField, PsiStatement>? {
@@ -21,6 +21,6 @@ import org.jetbrains.jet.j2k.CodeBuilder
class AnonymousClassBody(body: ClassBody, val extendsTrait: Boolean)
: Class(Identifier.Empty, Annotations.Empty, Modifiers.Empty, TypeParameterList.Empty, listOf(), listOf(), listOf(), body) {
override fun generateCode(builder: CodeBuilder) {
body.append(builder, null)
body.append(builder)
}
}
+1 -1
View File
@@ -41,7 +41,7 @@ open class Class(
}
appendBaseTypes(builder)
typeParameterList.appendWhere(builder)
body.append(builder, this)
body.append(builder)
}
protected open val keyword: String
@@ -20,30 +20,29 @@ import org.jetbrains.jet.j2k.*
abstract class Member(val annotations: Annotations, val modifiers: Modifiers) : Element()
//TODO: should be Element?
class ClassBody (
val primaryConstructorSignature: PrimaryConstructorSignature?,
val members: List<Member>,
val secondaryConstructors: List<SecondaryConstructor>,
val classObjectMembers: List<Member>,
val lBrace: LBrace,
val rBrace: RBrace) {
fun append(builder: CodeBuilder, containingClass: Class?) {
if (members.isEmpty() && classObjectMembers.isEmpty() && secondaryConstructors.isEmpty()) return
fun append(builder: CodeBuilder) {
if (members.isEmpty() && classObjectMembers.isEmpty()) return
builder append " " append lBrace append "\n"
builder.append(members, "\n")
appendClassObject(builder, containingClass, members.isNotEmpty())
appendClassObject(builder, members.isNotEmpty())
builder append "\n" append rBrace
}
private fun appendClassObject(builder: CodeBuilder, containingClass: Class?, blankLineBefore: Boolean) {
if (secondaryConstructors.isEmpty() && classObjectMembers.isEmpty()) return
private fun appendClassObject(builder: CodeBuilder, blankLineBefore: Boolean) {
if (classObjectMembers.isEmpty()) return
if (blankLineBefore) builder.append("\n\n")
val factoryFunctions = secondaryConstructors.map { it.toFactoryFunction(containingClass) }
builder.append(factoryFunctions + classObjectMembers, "\n", "class object {\n", "\n}")
builder.append(classObjectMembers, "\n", "class object {\n", "\n}")
}
}
@@ -16,37 +16,26 @@
package org.jetbrains.jet.j2k.ast
import java.util.ArrayList
import org.jetbrains.jet.j2k.*
import com.intellij.util.IncorrectOperationException
abstract class Constructor(
converter: Converter,
annotations: Annotations,
modifiers: Modifiers,
parameterList: ParameterList,
block: Block
) : Function(converter, Identifier.Empty, annotations, modifiers, ErrorType(), TypeParameterList.Empty, parameterList, block, false) {
class PrimaryConstructor(annotations: Annotations,
modifiers: Modifiers,
val parameterList: ParameterList,
val block: Block)
: Member(annotations, modifiers) {
override fun generateCode(builder: CodeBuilder) { throw IncorrectOperationException() }
}
class PrimaryConstructor(converter: Converter,
annotations: Annotations,
modifiers: Modifiers,
parameterList: ParameterList,
block: Block)
: Constructor(converter, annotations, modifiers, parameterList, block) {
public fun initializer(): Initializer? {
return if (!block!!.isEmpty)
Initializer(block!!, Modifiers.Empty).assignPrototypesFrom(this, CommentsAndSpacesInheritance(commentsBefore = false))
return if (!block.isEmpty)
Initializer(block, Modifiers.Empty).assignPrototypesFrom(this, CommentsAndSpacesInheritance(commentsBefore = false))
else
null
}
public fun signature(): PrimaryConstructorSignature {
val noBody = block!!.isEmpty
val noBody = block.isEmpty
val inheritance = CommentsAndSpacesInheritance(blankLinesBefore = false, commentsAfter = noBody, commentsInside = noBody)
return PrimaryConstructorSignature(modifiers, parameterList).assignPrototypesFrom(this, inheritance)
}
@@ -63,13 +52,15 @@ class PrimaryConstructorSignature(val modifiers: Modifiers, val parameterList: P
}
}
class SecondaryConstructor(converter: Converter,
annotations: Annotations,
modifiers: Modifiers,
parameterList: ParameterList,
block: Block)
: Constructor(converter, annotations, modifiers, parameterList, block) {
class FactoryFunction(annotations: Annotations,
modifiers: Modifiers,
returnType: Type,
parameterList: ParameterList,
typeParameterList: TypeParameterList,
block: Block)
: Function(Identifier("create").assignNoPrototype(), annotations, modifiers, returnType, typeParameterList, parameterList, block, false) {
/*
public fun toFactoryFunction(containingClass: Class?): Function {
val statements = ArrayList(block?.statements ?: listOf())
statements.add(ReturnStatement(tempValIdentifier()).assignNoPrototype())
@@ -85,6 +76,7 @@ class SecondaryConstructor(converter: Converter,
return Function(converter, Identifier("create").assignNoPrototype(), annotations, modifiers,
returnType, typeParameterList, parameterList, newBlock, false).assignPrototypesFrom(this)
}
*/
class object {
public val tempValName: String = "__"
+1 -1
View File
@@ -37,6 +37,6 @@ class Enum(
}
builder append typeParameterList
appendBaseTypes(builder)
body.append(builder, this)
body.append(builder)
}
}
@@ -19,14 +19,13 @@ package org.jetbrains.jet.j2k.ast
import org.jetbrains.jet.j2k.*
open class Function(
val converter: Converter,
val name: Identifier,
annotations: Annotations,
modifiers: Modifiers,
val `type`: Type,
val typeParameterList: TypeParameterList,
val parameterList: ParameterList,
var block: Block?,
var body: Block?,
val isInTrait: Boolean
) : Member(annotations, modifiers) {
@@ -59,8 +58,8 @@ open class Function(
typeParameterList.appendWhere(builder)
if (block != null) {
builder append " " append block!!
if (body != null) {
builder append " " append body!!
}
}
}
@@ -318,10 +318,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(SecondaryConstructor.tempValIdentifier(), Identifier(referencedName, isNullable).assignNoPrototype())
}
else if (insideSecondaryConstructor && expression.isThisConstructorCall()) {
identifier = Identifier("val __ = " + (containingConstructor?.getContainingClass()?.getNameIdentifier()?.getText() ?: "")).assignNoPrototype()
identifier = QualifiedExpression(FactoryFunction.tempValIdentifier(), Identifier(referencedName, isNullable).assignNoPrototype())
}
else if (qualifier != null && qualifier.getType() is PsiArrayType && referencedName == "length") {
identifier = Identifier("size", isNullable).assignNoPrototype()