Java to Kotlin convertor: improving code related to primary constructors

This commit is contained in:
Valentin Kipyatkov
2014-05-30 17:34:48 +04:00
parent 1e39021bd3
commit aa8b968756
3 changed files with 56 additions and 69 deletions
@@ -22,57 +22,70 @@ import com.intellij.psi.PsiReferenceExpression
import com.intellij.psi.JavaRecursiveElementVisitor
import java.util.LinkedHashSet
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiElement
fun isConstructorPrimary(constructor: PsiMethod): Boolean {
val parent = constructor.getParent()
if (parent is PsiClass) {
if (parent.getConstructors().size == 1) {
return true
}
else {
val primary = getPrimaryConstructorForThisCase(parent)
//TODO: I do not understand code below
if (primary != null && primary.hashCode() == constructor.hashCode()) {
return true
}
}
}
return false
fun PsiMethod.isPrimaryConstructor(): Boolean {
if (!isConstructor()) return false
val parent = getParent()
if (parent !is PsiClass) return false
return parent.getPrimaryConstructor() == this
}
fun getPrimaryConstructorForThisCase(psiClass: PsiClass): PsiMethod? {
class FindPrimaryConstructorVisitor() : JavaRecursiveElementVisitor() {
private val resolvedConstructors = LinkedHashSet<PsiMethod>()
fun PsiClass.getPrimaryConstructor(): PsiMethod? {
val constructors = getConstructors()
return when (constructors.size) {
0 -> null
override fun visitReferenceExpression(expression: PsiReferenceExpression) {
expression.getReferences()
.filter { it.getCanonicalText() == "this" }
.map { it.resolve() }
.filterIsInstance(javaClass<PsiMethod>())
.filterTo(resolvedConstructors) { it.isConstructor() }
1 -> constructors.single()
else -> {
// if there is more than one constructor then choose one invoked by all others
class Visitor() : JavaRecursiveElementVisitor() {
//TODO: skip all non-constructor members (optimization)
private val invokedConstructors = LinkedHashSet<PsiMethod>()
override fun visitReferenceExpression(expression: PsiReferenceExpression) {
expression.getReferences()
.filter { it.getCanonicalText() == "this" }
.map { it.resolve() }
.filterIsInstance(javaClass<PsiMethod>())
.filterTo(invokedConstructors) { it.isConstructor() }
}
val primaryConstructor: PsiMethod?
get() = if (invokedConstructors.size == 1) invokedConstructors.single() else null
}
val visitor = Visitor()
accept(visitor)
visitor.primaryConstructor
}
}
}
fun isInsidePrimaryConstructor(element: PsiElement): Boolean
= containingConstructor(element)?.isPrimaryConstructor() ?: false
fun isInsideSecondaryConstructor(element: PsiElement): Boolean
= !(containingConstructor(element)?.isPrimaryConstructor() ?: true)
fun containingConstructor(element: PsiElement): PsiMethod? {
var context = element.getContext()
while (context != null) {
val _context = context!!
if (_context is PsiMethod) {
return if (_context.isConstructor()) _context else null
}
val result: PsiMethod?
get() {
if (resolvedConstructors.isEmpty()) return null
//TODO: I do not understand code below
val first = resolvedConstructors.first()
return if (resolvedConstructors.all { it.hashCode() == first.hashCode() }) first else null
}
context = _context.getContext()
}
val visitor = FindPrimaryConstructorVisitor()
psiClass.accept(visitor)
return visitor.result
return null
}
fun isSuperConstructorRef(ref: PsiReference): Boolean {
if (ref.getCanonicalText().equals("super")) {
val baseConstructor = ref.resolve()
if (baseConstructor is PsiMethod && baseConstructor.isConstructor()) {
return true
}
val target = ref.resolve()
return target is PsiMethod && target.isConstructor()
}
return false
}
+2 -2
View File
@@ -122,7 +122,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
}
}
if (!psiClass.isEnum() && !psiClass.isInterface() && psiClass.getConstructors().size > 1 && getPrimaryConstructorForThisCase(psiClass) == null) {
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) {
@@ -243,7 +243,7 @@ public class Converter(val project: Project, val settings: ConverterSettings) {
if (method.isConstructor()) {
return Constructor(this, identifier, getComments(method), modifiers, returnType, typeParameterList, params,
Block(body.statements), isConstructorPrimary(method))
Block(body.statements), method.isPrimaryConstructor())
}
return Function(this, identifier, getComments(method), modifiers, returnType, typeParameterList, params, body)
@@ -168,7 +168,7 @@ open class ExpressionVisitor(public val converter: Converter) : JavaElementVisit
val isNotConvertedClass = classReference != null && !converter.getClassIdentifiers().contains(classReference.getQualifiedName())
val argumentList = expression.getArgumentList()
var arguments = argumentList?.getExpressions() ?: array()
if (constructor == null || isConstructorPrimary(constructor) || isNotConvertedClass) {
if (constructor == null || constructor.isPrimaryConstructor() || isNotConvertedClass) {
return NewClassExpression(converter.convertElement(classReference),
converter.convertArguments(expression),
converter.convertExpression(expression.getQualifier()),
@@ -388,32 +388,6 @@ open class ExpressionVisitor(public val converter: Converter) : JavaElementVisit
return false
}
private fun isInsideSecondaryConstructor(expression: PsiReferenceExpression): Boolean {
var context = expression.getContext()
while (context != null) {
val _context = context!!
if (_context is PsiMethod && _context.isConstructor()) {
return !isConstructorPrimary(_context)
}
context = _context.getContext()
}
return false
}
private fun isInsidePrimaryConstructor(expression: PsiExpression): Boolean {
var context = expression.getContext()
while (context != null) {
val _context = context!!
if (_context is PsiMethod && _context.isConstructor()) {
return isConstructorPrimary(_context)
}
context = _context.getContext()
}
return false
}
private fun getContainingClass(expression: PsiExpression): PsiClass? {
var context = expression.getContext()
while (context != null) {