New J2K: Add ConvertDataClass PostProcessing

This commit is contained in:
Ilya Kirillov
2018-12-18 14:22:49 +03:00
committed by Ilya Kirillov
parent 5c8035b339
commit c7df104b36
2 changed files with 120 additions and 0 deletions
@@ -0,0 +1,85 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.j2k.postProcessing
import com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.setDefaultValue
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessing
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.addRemoveModifier.setModifierList
import org.jetbrains.kotlin.psi.psiUtil.asAssignment
import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
class ConvertDataClass : J2kPostProcessing {
override val writeActionNeeded: Boolean = true
fun KtCallableDeclaration.rename(newName: String) {
val factory = KtPsiFactory(this)
ReferencesSearch.search(this, LocalSearchScope(containingKtFile)).forEach {
it.element.replace(factory.createExpression(newName))
}
setName(newName)
}
private data class DataClassInfo(
val constructorParameter: KtParameter,
val property: KtProperty,
val initBlockStatement: KtBinaryExpression
)
private fun collectPropertiesData(klass: KtClass): List<DataClassInfo> =
klass.getAnonymousInitializers()
.flatMap { (it.body as KtBlockExpression).statements }
.asSequence()
.mapNotNull { it.asAssignment() }
.mapNotNull { statement ->
val property =
statement.left
?.unpackedReferenceToProperty()
?.takeIf { it.containingClass() == klass } ?: return@mapNotNull null
if (property.getter != null || property.setter != null) return@mapNotNull null
val constructorParameter =
((statement.right as? KtReferenceExpression)
?.references
?.firstOrNull { it is KtSimpleNameReference }
?.resolve() as? KtParameter)
?.takeIf {
it.containingClass() == klass && !it.hasValOrVar()
} ?: return@mapNotNull null
if (constructorParameter.type() != property.type()) return@mapNotNull null
DataClassInfo(constructorParameter, property, statement)
}.toList()
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
if (element !is KtClass) return null
return {
val factory = KtPsiFactory(element)
for ((constructorParameter, property, statement) in collectPropertiesData(element)) {
constructorParameter.addBefore(property.valOrVarKeyword, constructorParameter.nameIdentifier!!)
constructorParameter.addAfter(factory.createWhiteSpace(), constructorParameter.valOrVarKeyword!!)
constructorParameter.rename(property.name!!)
if (property.modifierList != null) {
constructorParameter.setModifierList(property.modifierList!!)
}
property.delete()
statement.delete()
}
for (initBlock in element.getAnonymousInitializers()) {
if ((initBlock.body as KtBlockExpression).statements.isEmpty()) {
initBlock.delete()
}
}
}
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.j2k.postProcessing
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.psi.*
fun KtExpression.asProperty(): KtProperty? =
(this as? KtNameReferenceExpression)
?.references
?.firstOrNull { it is KtSimpleNameReference }
?.resolve() as? KtProperty
fun KtExpression.unpackedReferenceToProperty(): KtProperty? =
when (this) {
is KtDotQualifiedExpression ->
if (receiverExpression is KtThisExpression) selectorExpression as? KtNameReferenceExpression
else null
is KtNameReferenceExpression -> this
else -> null
}?.references
?.firstOrNull { it is KtSimpleNameReference }
?.resolve() as? KtProperty
fun KtDeclaration.type() =
(resolveToDescriptorIfAny() as? CallableDescriptor)?.returnType