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.HashSet
class ConstructorConverter(private val converter: Converter) {
class ConstructorConverter(private val psiClass: PsiClass, private val converter: Converter) {
private val typeConverter = converter.typeConverter
private val tempValName: String = "__"
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,
annotations: Annotations,
modifiers: Modifiers,
membersToRemove: MutableSet<PsiMember>,
postProcessBody: (Block) -> Block): Member {
if (constructor.isPrimaryConstructor()) {
if (constructor == primaryConstructor) {
return convertPrimaryConstructor(constructor, annotations, modifiers, membersToRemove, postProcessBody)
}
else {
@@ -165,17 +209,17 @@ class ConstructorConverter(private val converter: Converter) {
return null
}
public fun postProcessConstructors(classBody: ClassBody, psiClass: PsiClass): ClassBody {
if (psiClass.getPrimaryConstructor() == null && psiClass.getConstructors().size > 1) {
return generateArtificialPrimaryConstructor(psiClass.getName()!!, classBody)
public fun postProcessConstructors(classBody: ClassBody): ClassBody {
if (primaryConstructor == null && constructors.size > 1) {
return generateArtificialPrimaryConstructor(classBody)
}
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)
}
}
private fun generateArtificialPrimaryConstructor(className: String, classBody: ClassBody): ClassBody {
private fun generateArtificialPrimaryConstructor(classBody: ClassBody): ClassBody {
assert(classBody.primaryConstructorSignature == null)
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) {
val body = function.body!!
// 2 cases: secondary constructor either calls another constructor or does not call any
val newStatements = replaceConstructorCallInFactoryFunction(body, className) ?:
insertCallToArtificialPrimary(body, className, fieldsToInitialize)
val newStatements = replaceConstructorCallInFactoryFunction(body) ?:
insertCallToArtificialPrimary(body, fieldsToInitialize)
val newBody = Block(newStatements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
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)
}
private fun replaceConstructorCallsInFactoryFunctions(functions: List<FactoryFunction>, className: String): List<FactoryFunction> {
private fun replaceConstructorCallsInFactoryFunctions(functions: List<FactoryFunction>): List<FactoryFunction> {
return functions.map { function ->
val body = function.body!!
val statements = replaceConstructorCallInFactoryFunction(body, className)
val statements = replaceConstructorCallInFactoryFunction(body)
if (statements != null) {
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)
// searching for other constructor call in form "this(...)"
@@ -241,7 +285,7 @@ class ConstructorConverter(private val converter: Converter) {
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?>()
for (field in fieldsToInitialize) {
initializers.put(field, getDefaultInitializer(field))
@@ -287,50 +331,4 @@ class ConstructorConverter(private val converter: Converter) {
}
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 methodReturnType: PsiType? = state.methodReturnType
private val constructorConverter = ConstructorConverter(this)
private val expressionVisitor = state.expressionVisitorFactory(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) {
is PsiJavaFile -> convertFile(element)
is PsiClass -> convertClass(element)
is PsiMethod -> convertMethod(element, HashSet())
is PsiMethod -> convertMethod(element, null, null)
is PsiField -> convertField(element)
is PsiStatement -> convertStatement(element)
is PsiExpression -> convertExpression(element)
@@ -102,15 +101,15 @@ public class Converter private(val project: Project, val settings: ConverterSett
}
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 convertedMembers = LinkedHashMap<PsiMember, Member>()
for (element in psiClass.getChildren()) {
if (element is PsiMember) {
val converted = convertMember(element, membersToRemove)
val converted = convertMember(element, membersToRemove, constructorConverter)
if (!converted.isEmpty) {
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) {
is PsiMethod -> convertMethod(member, membersToRemove)
private fun convertMember(member: PsiMember, membersToRemove: MutableSet<PsiMember>, constructorConverter: ConstructorConverter?): Member = when (member) {
is PsiMethod -> convertMethod(member, membersToRemove, constructorConverter)
is PsiField -> convertField(member)
is PsiClass -> convertClass(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 extendsTypes = convertToNotNullableTypes(psiClass.getExtendsListTypes())
val name = psiClass.declarationIdentifier()
var classBody = convertBody(psiClass)
val constructorConverter = ConstructorConverter(psiClass, this)
var classBody = convertBody(psiClass, constructorConverter)
classBody = constructorConverter.postProcessConstructors(classBody)
return when {
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)
else -> {
classBody = constructorConverter.postProcessConstructors(classBody, psiClass)
val baseClassParams: List<Expression> = run {
val superVisitor = 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
}
private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>): Member {
return withMethodReturnType(method.getReturnType()).doConvertMethod(method, membersToRemove).assignPrototype(method)
private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>?, constructorConverter: ConstructorConverter?): Member {
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 annotations = (convertAnnotations(method) + convertThrows(method)).assignNoPrototype()
@@ -297,8 +297,8 @@ public class Converter private(val project: Project, val settings: ConverterSett
}
}
if (method.isConstructor()) {
return constructorConverter.convertConstructor(method, annotations, modifiers, membersToRemove, postProcessBody)
if (method.isConstructor() && constructorConverter != null) {
return constructorConverter.convertConstructor(method, annotations, modifiers, membersToRemove!!, postProcessBody)
}
else {
val isOverride = isOverride(method)