Java to Kotlin converter: refactored code to perform primary constructor detection only once

This commit is contained in:
Valentin Kipyatkov
2014-06-26 22:55:49 +04:00
parent c260eee9b8
commit e997557ba1
2 changed files with 72 additions and 74 deletions
@@ -24,18 +24,62 @@ import java.util.HashMap
import java.util.ArrayList import java.util.ArrayList
import java.util.HashSet import java.util.HashSet
class ConstructorConverter(private val converter: Converter) { class ConstructorConverter(private val psiClass: PsiClass, private val converter: Converter) {
private val typeConverter = converter.typeConverter private val typeConverter = converter.typeConverter
private val tempValName: String = "__" private val tempValName: String = "__"
private fun tempValIdentifier(): Identifier = Identifier(tempValName, false).assignNoPrototype() private fun tempValIdentifier(): Identifier = Identifier(tempValName, false).assignNoPrototype()
private val className = psiClass.getName()!!
private val constructors = psiClass.getConstructors()
private val primaryConstructor: PsiMethod? = run {
when (constructors.size) {
0 -> null
1 -> constructors.single()
else -> {
val toTargetConstructorMap = HashMap<PsiMethod, PsiMethod>()
for (constructor in constructors) {
val firstStatement = constructor.getBody()?.getStatements()?.firstOrNull()
val refExpr = ((firstStatement as? PsiExpressionStatement)
?.getExpression() as? PsiMethodCallExpression)
?.getMethodExpression()
if (refExpr != null && refExpr.getCanonicalText() == "this") {
val target = refExpr.resolve() as? PsiMethod
if (target != null && target.isConstructor()) {
val finalTarget = toTargetConstructorMap[target] ?: target!!/*TODO: see KT-5335*/
toTargetConstructorMap[constructor] = finalTarget
for (entry in toTargetConstructorMap.entrySet()) {
if (entry.getValue() == constructor) {
entry.setValue(finalTarget)
}
}
}
}
}
val candidates = constructors.filter { it !in toTargetConstructorMap }
if (candidates.size == 1) { // there should be only one constructor which does not call other constructor
val candidate = candidates.single()
if (toTargetConstructorMap.values().all { it == candidate } /* all other constructors call our candidate (directly or indirectly)*/)
candidate
else
null
}
else {
null
}
}
}
}
public fun convertConstructor(constructor: PsiMethod, public fun convertConstructor(constructor: PsiMethod,
annotations: Annotations, annotations: Annotations,
modifiers: Modifiers, modifiers: Modifiers,
membersToRemove: MutableSet<PsiMember>, membersToRemove: MutableSet<PsiMember>,
postProcessBody: (Block) -> Block): Member { postProcessBody: (Block) -> Block): Member {
if (constructor.isPrimaryConstructor()) { if (constructor == primaryConstructor) {
return convertPrimaryConstructor(constructor, annotations, modifiers, membersToRemove, postProcessBody) return convertPrimaryConstructor(constructor, annotations, modifiers, membersToRemove, postProcessBody)
} }
else { else {
@@ -165,17 +209,17 @@ class ConstructorConverter(private val converter: Converter) {
return null return null
} }
public fun postProcessConstructors(classBody: ClassBody, psiClass: PsiClass): ClassBody { public fun postProcessConstructors(classBody: ClassBody): ClassBody {
if (psiClass.getPrimaryConstructor() == null && psiClass.getConstructors().size > 1) { if (primaryConstructor == null && constructors.size > 1) {
return generateArtificialPrimaryConstructor(psiClass.getName()!!, classBody) return generateArtificialPrimaryConstructor(classBody)
} }
else { else {
val updatedFunctions = replaceConstructorCallsInFactoryFunctions(classBody.factoryFunctions, psiClass.getName()!!) val updatedFunctions = replaceConstructorCallsInFactoryFunctions(classBody.factoryFunctions)
return ClassBody(classBody.primaryConstructorSignature, classBody.members, classBody.classObjectMembers, updatedFunctions, classBody.lBrace, classBody.rBrace) return ClassBody(classBody.primaryConstructorSignature, classBody.members, classBody.classObjectMembers, updatedFunctions, classBody.lBrace, classBody.rBrace)
} }
} }
private fun generateArtificialPrimaryConstructor(className: String, classBody: ClassBody): ClassBody { private fun generateArtificialPrimaryConstructor(classBody: ClassBody): ClassBody {
assert(classBody.primaryConstructorSignature == null) assert(classBody.primaryConstructorSignature == null)
val fieldsToInitialize = classBody.members.filterIsInstance(javaClass<Field>()).filter { it.isVal } val fieldsToInitialize = classBody.members.filterIsInstance(javaClass<Field>()).filter { it.isVal }
@@ -183,8 +227,8 @@ class ConstructorConverter(private val converter: Converter) {
for (function in classBody.factoryFunctions) { for (function in classBody.factoryFunctions) {
val body = function.body!! val body = function.body!!
// 2 cases: secondary constructor either calls another constructor or does not call any // 2 cases: secondary constructor either calls another constructor or does not call any
val newStatements = replaceConstructorCallInFactoryFunction(body, className) ?: val newStatements = replaceConstructorCallInFactoryFunction(body) ?:
insertCallToArtificialPrimary(body, className, fieldsToInitialize) insertCallToArtificialPrimary(body, fieldsToInitialize)
val newBody = Block(newStatements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype() val newBody = Block(newStatements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
updatedFactoryFunctions.add(function.withBody(newBody)) updatedFactoryFunctions.add(function.withBody(newBody))
} }
@@ -203,10 +247,10 @@ class ConstructorConverter(private val converter: Converter) {
return ClassBody(constructorSignature, updatedMembers, classBody.classObjectMembers, updatedFactoryFunctions, classBody.lBrace, classBody.rBrace) return ClassBody(constructorSignature, updatedMembers, classBody.classObjectMembers, updatedFactoryFunctions, classBody.lBrace, classBody.rBrace)
} }
private fun replaceConstructorCallsInFactoryFunctions(functions: List<FactoryFunction>, className: String): List<FactoryFunction> { private fun replaceConstructorCallsInFactoryFunctions(functions: List<FactoryFunction>): List<FactoryFunction> {
return functions.map { function -> return functions.map { function ->
val body = function.body!! val body = function.body!!
val statements = replaceConstructorCallInFactoryFunction(body, className) val statements = replaceConstructorCallInFactoryFunction(body)
if (statements != null) { if (statements != null) {
function.withBody(Block(statements, body.lBrace, body.rBrace).assignPrototypesFrom(body)) function.withBody(Block(statements, body.lBrace, body.rBrace).assignPrototypesFrom(body))
} }
@@ -216,7 +260,7 @@ class ConstructorConverter(private val converter: Converter) {
} }
} }
private fun replaceConstructorCallInFactoryFunction(body: Block, className: String): List<Statement>? { private fun replaceConstructorCallInFactoryFunction(body: Block): List<Statement>? {
val statements = ArrayList(body.statements) val statements = ArrayList(body.statements)
// searching for other constructor call in form "this(...)" // searching for other constructor call in form "this(...)"
@@ -241,7 +285,7 @@ class ConstructorConverter(private val converter: Converter) {
return null return null
} }
private fun insertCallToArtificialPrimary(body: Block, className: String, fieldsToInitialize: Collection<Field>): List<Statement> { private fun insertCallToArtificialPrimary(body: Block, fieldsToInitialize: Collection<Field>): List<Statement> {
val initializers = HashMap<Field, Expression?>() val initializers = HashMap<Field, Expression?>()
for (field in fieldsToInitialize) { for (field in fieldsToInitialize) {
initializers.put(field, getDefaultInitializer(field)) initializers.put(field, getDefaultInitializer(field))
@@ -287,50 +331,4 @@ class ConstructorConverter(private val converter: Converter) {
} }
return statements return statements
} }
private fun PsiMethod.isPrimaryConstructor(): Boolean {
if (!isConstructor()) return false
val parent = getParent()
if (parent !is PsiClass) return false
return parent.getPrimaryConstructor() == this
}
private fun PsiClass.getPrimaryConstructor(): PsiMethod? {
val constructors = getConstructors()
when (constructors.size) {
0 -> return null
1 -> return constructors.single()
else -> {
val toTargetConstructorMap = HashMap<PsiMethod, PsiMethod>()
for (constructor in constructors) {
val firstStatement = constructor.getBody()?.getStatements()?.firstOrNull()
val refExpr = ((firstStatement as? PsiExpressionStatement)
?.getExpression() as? PsiMethodCallExpression)
?.getMethodExpression()
if (refExpr != null && refExpr.getCanonicalText() == "this") {
val target = refExpr.resolve() as? PsiMethod
if (target != null && target.isConstructor()) {
val finalTarget = toTargetConstructorMap[target] ?: target!!/*TODO: see KT-5335*/
toTargetConstructorMap[constructor] = finalTarget
for (entry in toTargetConstructorMap.entrySet()) {
if (entry.getValue() == constructor) {
entry.setValue(finalTarget)
}
}
}
}
}
val candidates = constructors.filter { it !in toTargetConstructorMap }
if (candidates.size != 1) return null // there should be only one constructor which does not call other constructor
val candidate = candidates.single()
return if (toTargetConstructorMap.values().all { it == candidate } /* all other constructors call our candidate (directly or indirectly)*/)
candidate
else
null
}
}
}
} }
+15 -15
View File
@@ -40,7 +40,6 @@ public class Converter private(val project: Project, val settings: ConverterSett
val typeConverter: TypeConverter = state.typeConverter val typeConverter: TypeConverter = state.typeConverter
val methodReturnType: PsiType? = state.methodReturnType val methodReturnType: PsiType? = state.methodReturnType
private val constructorConverter = ConstructorConverter(this)
private val expressionVisitor = state.expressionVisitorFactory(this) private val expressionVisitor = state.expressionVisitorFactory(this)
private val statementVisitor = state.statementVisitorFactory(this) private val statementVisitor = state.statementVisitorFactory(this)
@@ -68,7 +67,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
private fun convertTopElement(element: PsiElement?): Element? = when (element) { private fun convertTopElement(element: PsiElement?): Element? = when (element) {
is PsiJavaFile -> convertFile(element) is PsiJavaFile -> convertFile(element)
is PsiClass -> convertClass(element) is PsiClass -> convertClass(element)
is PsiMethod -> convertMethod(element, HashSet()) is PsiMethod -> convertMethod(element, null, null)
is PsiField -> convertField(element) is PsiField -> convertField(element)
is PsiStatement -> convertStatement(element) is PsiStatement -> convertStatement(element)
is PsiExpression -> convertExpression(element) is PsiExpression -> convertExpression(element)
@@ -102,15 +101,15 @@ public class Converter private(val project: Project, val settings: ConverterSett
} }
fun convertAnonymousClassBody(anonymousClass: PsiAnonymousClass): AnonymousClassBody { fun convertAnonymousClassBody(anonymousClass: PsiAnonymousClass): AnonymousClassBody {
return AnonymousClassBody(convertBody(anonymousClass), anonymousClass.getBaseClassType().resolve()?.isInterface() ?: false).assignPrototype(anonymousClass) return AnonymousClassBody(convertBody(anonymousClass, null), anonymousClass.getBaseClassType().resolve()?.isInterface() ?: false).assignPrototype(anonymousClass)
} }
private fun convertBody(psiClass: PsiClass): ClassBody { private fun convertBody(psiClass: PsiClass, constructorConverter: ConstructorConverter?): ClassBody {
val membersToRemove = HashSet<PsiMember>() val membersToRemove = HashSet<PsiMember>()
val convertedMembers = LinkedHashMap<PsiMember, Member>() val convertedMembers = LinkedHashMap<PsiMember, Member>()
for (element in psiClass.getChildren()) { for (element in psiClass.getChildren()) {
if (element is PsiMember) { if (element is PsiMember) {
val converted = convertMember(element, membersToRemove) val converted = convertMember(element, membersToRemove, constructorConverter)
if (!converted.isEmpty) { if (!converted.isEmpty) {
convertedMembers.put(element, converted) convertedMembers.put(element, converted)
} }
@@ -166,8 +165,8 @@ public class Converter private(val project: Project, val settings: ConverterSett
} }
} }
private fun convertMember(member: PsiMember, membersToRemove: MutableSet<PsiMember>): Member = when (member) { private fun convertMember(member: PsiMember, membersToRemove: MutableSet<PsiMember>, constructorConverter: ConstructorConverter?): Member = when (member) {
is PsiMethod -> convertMethod(member, membersToRemove) is PsiMethod -> convertMethod(member, membersToRemove, constructorConverter)
is PsiField -> convertField(member) is PsiField -> convertField(member)
is PsiClass -> convertClass(member) is PsiClass -> convertClass(member)
is PsiClassInitializer -> convertInitializer(member) is PsiClassInitializer -> convertInitializer(member)
@@ -181,7 +180,10 @@ public class Converter private(val project: Project, val settings: ConverterSett
val implementsTypes = convertToNotNullableTypes(psiClass.getImplementsListTypes()) val implementsTypes = convertToNotNullableTypes(psiClass.getImplementsListTypes())
val extendsTypes = convertToNotNullableTypes(psiClass.getExtendsListTypes()) val extendsTypes = convertToNotNullableTypes(psiClass.getExtendsListTypes())
val name = psiClass.declarationIdentifier() val name = psiClass.declarationIdentifier()
var classBody = convertBody(psiClass)
val constructorConverter = ConstructorConverter(psiClass, this)
var classBody = convertBody(psiClass, constructorConverter)
classBody = constructorConverter.postProcessConstructors(classBody)
return when { return when {
psiClass.isInterface() -> Trait(name, annotations, modifiers, typeParameters, extendsTypes, listOf(), implementsTypes, classBody) psiClass.isInterface() -> Trait(name, annotations, modifiers, typeParameters, extendsTypes, listOf(), implementsTypes, classBody)
@@ -189,8 +191,6 @@ public class Converter private(val project: Project, val settings: ConverterSett
psiClass.isEnum() -> Enum(name, annotations, modifiers, typeParameters, listOf(), listOf(), implementsTypes, classBody) psiClass.isEnum() -> Enum(name, annotations, modifiers, typeParameters, listOf(), listOf(), implementsTypes, classBody)
else -> { else -> {
classBody = constructorConverter.postProcessConstructors(classBody, psiClass)
val baseClassParams: List<Expression> = run { val baseClassParams: List<Expression> = run {
val superVisitor = SuperVisitor() val superVisitor = SuperVisitor()
psiClass.accept(superVisitor) psiClass.accept(superVisitor)
@@ -266,11 +266,11 @@ public class Converter private(val project: Project, val settings: ConverterSett
return if (convertedType == initializerType) null else convertedType return if (convertedType == initializerType) null else convertedType
} }
private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>): Member { private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>?, constructorConverter: ConstructorConverter?): Member {
return withMethodReturnType(method.getReturnType()).doConvertMethod(method, membersToRemove).assignPrototype(method) return withMethodReturnType(method.getReturnType()).doConvertMethod(method, membersToRemove, constructorConverter).assignPrototype(method)
} }
private fun doConvertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>): Member { private fun doConvertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>?, constructorConverter: ConstructorConverter?): Member {
val returnType = typeConverter.convertMethodReturnType(method) val returnType = typeConverter.convertMethodReturnType(method)
val annotations = (convertAnnotations(method) + convertThrows(method)).assignNoPrototype() val annotations = (convertAnnotations(method) + convertThrows(method)).assignNoPrototype()
@@ -297,8 +297,8 @@ public class Converter private(val project: Project, val settings: ConverterSett
} }
} }
if (method.isConstructor()) { if (method.isConstructor() && constructorConverter != null) {
return constructorConverter.convertConstructor(method, annotations, modifiers, membersToRemove, postProcessBody) return constructorConverter.convertConstructor(method, annotations, modifiers, membersToRemove!!, postProcessBody)
} }
else { else {
val isOverride = isOverride(method) val isOverride = isOverride(method)