KT-7135 Java to Kotlin converter should update usages in Java and Koltin code in the rest of the project

#KT-7135 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-04-07 13:06:31 +03:00
parent b810ebbb0e
commit 1afbb961ee
50 changed files with 962 additions and 156 deletions
@@ -714,7 +714,7 @@ fun main(args: Array<String>) {
}
testGroup("j2k/tests", "j2k/testData") {
testClass(javaClass<AbstractJavaToKotlinConverterMultiFileTest>()) {
model("multiFile", extension = null)
model("multiFile", extension = null, recursive = false)
}
}
testGroup("j2k/tests", "j2k/testData") {
@@ -126,7 +126,8 @@ public class JetSimpleNameReference(
)
}
return nameElement.replace(element)
nameElement.replace(element)
return expression
}
public enum class ShorteningMode {
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.idea.actions
import com.intellij.codeInsight.actions.ReformatCodeProcessor
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
@@ -25,6 +24,7 @@ import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.ex.MessagesEx
import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.openapi.vfs.VfsUtilCore
@@ -32,14 +32,14 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileVisitor
import com.intellij.psi.PsiJavaFile
import com.intellij.psi.PsiManager
import com.intellij.psi.codeStyle.CodeStyleManager
import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.j2k.ConverterSettings
import org.jetbrains.kotlin.j2k.IdeaReferenceSearcher
import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.IOException
import java.util.ArrayList
@@ -49,36 +49,53 @@ public class JavaToKotlinAction : AnAction() {
val javaFiles = selectedJavaFiles(e).toList()
val project = CommonDataKeys.PROJECT.getData(e.getDataContext())!!
var convertedTexts: List<String>? = null
var converterResult: JavaToKotlinConverter.Result? = null
fun convert() {
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings,
IdeaReferenceSearcher, IdeaResolverForConverter, J2kPostProcessor(formatCode = true))
val inputElements = javaFiles.map { JavaToKotlinConverter.InputElement(it, it) }
convertedTexts = converter.elementsToKotlin(inputElements, ProgressManager.getInstance().getProgressIndicator())
.map { it!!.text /*conversion of a file always succeeds*/ }
converterResult = converter.elementsToKotlin(inputElements, ProgressManager.getInstance().getProgressIndicator())
}
val title = "Convert Java to Kotlin"
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(
{
runReadAction(::convert)
},
"Converting Java to Kotlin",
title,
true,
project)) return
CommandProcessor.getInstance().executeCommand(project, object : Runnable {
override fun run() {
CommandProcessor.getInstance().markCurrentCommandAsGlobal(project)
var externalCodeUpdate: (() -> Unit)? = null
val newFiles = saveResults(javaFiles, convertedTexts!!)
deleteFiles(javaFiles)
newFiles.singleOrNull()?.let {
FileEditorManager.getInstance(project).openFile(it, true)
}
if (converterResult!!.externalCodeProcessing != null) {
val question = "Some code in the rest of your project may require corrections after performing this conversion. Do you want to find such code and correct it too?"
if (Messages.showOkCancelDialog(project, question, title, Messages.getQuestionIcon()) == Messages.OK) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(
{
runReadAction {
externalCodeUpdate = converterResult!!.externalCodeProcessing(ProgressManager.getInstance().getProgressIndicator())
}
},
title,
true,
project)
}
}, "Convert files from Java to Kotlin", null)
}
project.executeWriteCommand("Convert files from Java to Kotlin") {
CommandProcessor.getInstance().markCurrentCommandAsGlobal(project)
val newFiles = saveResults(javaFiles, converterResult!!.results.map { it!!.text /*conversion of a file always succeeds*/ })
deleteFiles(javaFiles)
externalCodeUpdate?.invoke()
newFiles.singleOrNull()?.let {
FileEditorManager.getInstance(project).openFile(it, true)
}
}
}
override fun update(e: AnActionEvent) {
@@ -116,9 +133,7 @@ public class JavaToKotlinAction : AnAction() {
private fun saveResults(javaFiles: List<PsiJavaFile>, convertedTexts: List<String>): List<VirtualFile> {
val result = ArrayList<VirtualFile>()
for ((psiFile, text) in javaFiles.zip(convertedTexts)) {
ApplicationManager.getApplication().runWriteAction {
result.addIfNotNull(saveConversionResult(psiFile.getVirtualFile(), text, psiFile.getProject()))
}
result.addIfNotNull(saveConversionResult(psiFile.getVirtualFile(), text, psiFile.getProject()))
}
return result
}
@@ -137,13 +152,11 @@ public class JavaToKotlinAction : AnAction() {
private fun deleteFiles(javaFiles: List<PsiJavaFile>) {
for (psiFile in javaFiles) {
ApplicationManager.getApplication().runWriteAction {
try {
psiFile.getVirtualFile()?.delete(this)
}
catch (e: IOException) {
MessagesEx.error(psiFile.getProject(), e.getMessage()).showLater()
}
try {
psiFile.getVirtualFile()?.delete(this)
}
catch (e: IOException) {
MessagesEx.error(psiFile.getProject(), e.getMessage()).showLater()
}
}
}
@@ -161,7 +161,7 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
)
val inputElements = list.filterIsInstance<PsiElement>().map { JavaToKotlinConverter.InputElement(it, null) }
val results = converter.elementsToKotlin(inputElements)
val results = converter.elementsToKotlin(inputElements).results
var resultIndex = 0
val convertedCodeBuilder = StringBuilder()
@@ -21,6 +21,10 @@ import com.intellij.psi.*
import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.j2k.ast.Class
import org.jetbrains.kotlin.j2k.usageProcessing.AccessorToPropertyProcessing
import org.jetbrains.kotlin.j2k.usageProcessing.MethodIntoObjectProcessing
import org.jetbrains.kotlin.j2k.usageProcessing.ToObjectWithOnlyMethodsProcessing
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.SpecialNames
import java.util.ArrayList
import java.util.HashMap
import java.util.HashSet
@@ -71,6 +75,19 @@ class ClassBodyConverter(private val psiClass: PsiClass,
val rBrace = RBrace().assignPrototype(psiClass.getRBrace())
if (isObject) {
val psiMembers = convertedMembers.keySet()
if (psiMembers.all { it is PsiMethod }) { // for object with no fields we can use faster external usage processing
converter.addUsageProcessing(ToObjectWithOnlyMethodsProcessing(psiClass))
}
else {
for (psiMember in psiMembers) {
if (psiMember is PsiMethod /* fields in object can be accessed as fields from java */
&& !psiMember.hasModifierProperty(PsiModifier.PRIVATE)) {
converter.addUsageProcessing(MethodIntoObjectProcessing(psiMember, JvmAbi.INSTANCE_FIELD))
}
}
}
return ClassBody(null, null, convertedMembers.values().toList(), emptyList(), lBrace, rBrace)
}
@@ -87,6 +104,10 @@ class ClassBodyConverter(private val psiClass: PsiClass,
}
else if (useCompanionObject && member !is Class && psiMember !is PsiEnumConstant && psiMember.hasModifierProperty(PsiModifier.STATIC)) {
companionObjectMembers.add(member)
if (psiMember is PsiMethod /* fields in companion object can be accessed as fields from java */
&& !psiMember.hasModifierProperty(PsiModifier.PRIVATE)) {
converter.addUsageProcessing(MethodIntoObjectProcessing(psiMember, SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT.getIdentifier()))
}
}
else {
members.add(member)
@@ -179,10 +200,11 @@ class ClassBodyConverter(private val psiClass: PsiClass,
private fun getAccessorInfo(method: PsiMethod): AccessorInfo? {
val name = method.getName()
val static = method.hasModifierProperty(PsiModifier.STATIC)
if (name.startsWith("get") && method.getParameterList().getParametersCount() == 0) {
val body = method.getBody() ?: return null
val returnStatement = (body.getStatements().singleOrNull() as? PsiReturnStatement) ?: return null
val field = fieldByExpression(returnStatement.getReturnValue()) ?: return null
val field = fieldByExpression(returnStatement.getReturnValue(), static) ?: return null
if (field.getType() != method.getReturnType()) return null
if (converter.typeConverter.variableMutability(field) != converter.typeConverter.methodMutability(method)) return null
val propertyName = StringUtil.decapitalize(name.substring("get".length()))
@@ -193,7 +215,7 @@ class ClassBodyConverter(private val psiClass: PsiClass,
val statement = (body.getStatements().singleOrNull() as? PsiExpressionStatement) ?: return null
val assignment = statement.getExpression() as? PsiAssignmentExpression ?: return null
if (assignment.getOperationTokenType() != JavaTokenType.EQ) return null
val field = fieldByExpression(assignment.getLExpression()) ?: return null
val field = fieldByExpression(assignment.getLExpression(), static) ?: return null
val parameter = method.getParameterList().getParameters().single()
if ((assignment.getRExpression() as? PsiReferenceExpression)?.resolve() != parameter) return null
if (field.getType() != parameter.getType()) return null
@@ -205,11 +227,16 @@ class ClassBodyConverter(private val psiClass: PsiClass,
}
}
private fun fieldByExpression(expression: PsiExpression?): PsiField? {
private fun fieldByExpression(expression: PsiExpression?, static: Boolean): PsiField? {
val refExpr = expression as? PsiReferenceExpression ?: return null
if (!refExpr.isQualifierEmptyOrThis()) return null
if (static) {
if (!refExpr.isQualifierEmptyOrClass(psiClass)) return null
}
else {
if (!refExpr.isQualifierEmptyOrThis()) return null
}
val field = refExpr.resolve() as? PsiField ?: return null
if (field.getContainingClass() != psiClass || field.hasModifierProperty(PsiModifier.STATIC)) return null
if (field.getContainingClass() != psiClass || field.hasModifierProperty(PsiModifier.STATIC) != static) return null
return field
}
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.j2k
import com.google.common.collect.Multimap
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.CommonClassNames.*
@@ -73,7 +74,7 @@ class Converter private(
private fun createDefaultCodeConverter() = CodeConverter(this, DefaultExpressionConverter(), DefaultStatementConverter(), null)
public data class IntermediateResult(
val codeGenerator: (Map<PsiElement, UsageProcessing>) -> String,
val codeGenerator: (Map<PsiElement, Collection<UsageProcessing>>) -> String,
val parseContext: ParseContext
)
@@ -108,7 +109,7 @@ class Converter private(
else -> null
}
private fun unfoldDeferredElements(usageProcessings: Map<PsiElement, UsageProcessing>) {
private fun unfoldDeferredElements(usageProcessings: Map<PsiElement, Collection<UsageProcessing>>) {
val codeConverter = createDefaultCodeConverter().withSpecialExpressionConverter(UsageProcessingExpressionConverter(usageProcessings))
// we use loop with index because new deferred elements can be added during unfolding
@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.j2k
import com.intellij.ide.util.DelegatingProgressIndicator
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.RangeMarker
import com.intellij.openapi.progress.EmptyProgressIndicator
@@ -26,15 +28,19 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.PsiReference
import com.intellij.psi.impl.source.DummyHolder
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.JetLanguage
import org.jetbrains.kotlin.j2k.ast.Element
import org.jetbrains.kotlin.j2k.usageProcessing.UsageProcessing
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.BindingContext
import java.util.ArrayList
import java.util.HashMap
import java.util.*
public trait PostProcessor {
public fun analyzeFile(file: JetFile, range: TextRange?): BindingContext
@@ -80,22 +86,26 @@ public class JavaToKotlinConverter(private val project: Project,
val postProcessingContext: PsiElement?
)
public data class Result(val text: String, val parseContext: ParseContext)
public data class ElementResult(val text: String, val parseContext: ParseContext)
public data class Result(val results: List<ElementResult?>, val externalCodeProcessing: ((ProgressIndicator) -> (() -> Unit)?)?) //TODO: change interface to not perform write-actions under progress
public fun elementsToKotlin(
inputElements: List<InputElement>,
progress: ProgressIndicator = EmptyProgressIndicator()
): List<Result?> {
): Result {
try {
val elementCount = inputElements.size()
val intermediateResults = ArrayList<(Converter.IntermediateResult)?>(elementCount)
val usageProcessings = HashMap<PsiElement, UsageProcessing>()
val usageProcessingCollector: (UsageProcessing) -> Unit = { usageProcessing ->
assert(!usageProcessings.containsKey(usageProcessing.targetElement))
{ "Duplicated UsageProcessing for target element ${usageProcessing.targetElement}" }
usageProcessings.put(usageProcessing.targetElement, usageProcessing)
val usageProcessings = LinkedHashMap<PsiElement, MutableCollection<UsageProcessing>>()
val usageProcessingCollector: (UsageProcessing) -> Unit = {
usageProcessings.getOrPut(it.targetElement, { ArrayList() }).add(it)
}
fun inConversionScope(element: PsiElement)
= inputElements.any { it.element.isAncestor(element, strict = false) }
val progressText = "Converting Java to Kotlin"
val fileCountText = elementCount.toString() + " " + if (elementCount > 1) "files" else "file"
var fraction = 0.0
@@ -128,31 +138,29 @@ public class JavaToKotlinConverter(private val project: Project,
processFilesWithProgress(0.25) { i ->
val psiElement = inputElements[i].element
fun inConversionScope(element: PsiElement)
= inputElements.any { it.element.isAncestor(element, strict = false) }
val converter = Converter.create(psiElement, settings, ::inConversionScope, referenceSearcher, resolverForConverter, usageProcessingCollector)
val result = converter.convert()
intermediateResults.add(result)
}
val results = ArrayList<Result?>(elementCount)
val results = ArrayList<ElementResult?>(elementCount)
processFilesWithProgress(0.25) { i ->
val result = intermediateResults[i]
results.add(if (result != null)
Result(result.codeGenerator(usageProcessings), result.parseContext)
ElementResult(result.codeGenerator(usageProcessings), result.parseContext)
else
null)
intermediateResults[i] = null // to not hold unused objects in the heap
}
val externalCodeProcessing = buildExternalCodeProcessing(usageProcessings, ::inConversionScope)
if (postProcessor == null) {
assert(progress is EmptyProgressIndicator, "Progress indicator not supported for postProcessor == null")
return results
return Result(results, externalCodeProcessing)
}
val finalResults = ArrayList<Result?>(elementCount)
val finalResults = ArrayList<ElementResult?>(elementCount)
processFilesWithProgress(0.5) { i ->
val result = results[i]
if (result != null) {
@@ -160,7 +168,7 @@ public class JavaToKotlinConverter(private val project: Project,
//TODO: post processing does not work correctly for ParseContext different from TOP_LEVEL
val kotlinFile = JetPsiFactory(project).createAnalyzableFile("dummy.kt", result.text, inputElements[i].postProcessingContext!!)
AfterConversionPass(project, postProcessor).run(kotlinFile, range = null)
finalResults.add(Result(kotlinFile.getText(), result.parseContext))
finalResults.add(ElementResult(kotlinFile.getText(), result.parseContext))
}
catch(e: ProcessCanceledException) {
throw e
@@ -175,7 +183,7 @@ public class JavaToKotlinConverter(private val project: Project,
}
}
return finalResults
return Result(finalResults, externalCodeProcessing)
}
catch(e: ElementCreationStackTraceRequiredException) {
// if we got this exception then we need to turn element creation stack traces on to get better diagnostic
@@ -188,4 +196,130 @@ public class JavaToKotlinConverter(private val project: Project,
}
}
}
data class ReferenceInfo(
val reference: PsiReference,
val target: PsiElement,
val file: PsiFile,
val processings: Collection<UsageProcessing>
)
private fun buildExternalCodeProcessing(
usageProcessings: Map<PsiElement, Collection<UsageProcessing>>,
inConversionScope: (PsiElement) -> Boolean
): ((ProgressIndicator) -> (() -> Unit)?)? {
if (usageProcessings.isEmpty()) return null
val map: Map<PsiElement, Collection<UsageProcessing>> = usageProcessings.values()
.flatMap { it }
.filter { it.javaCodeProcessor != null || it.kotlinCodeProcessor != null }
.groupBy { it.targetElement }
if (map.isEmpty()) return null
return fun(progress: ProgressIndicator): (() -> Unit)? {
val refs = ArrayList<ReferenceInfo>()
progress.setText("Searching usages to update...")
for ((i, entry) in map.entrySet().withIndex()) {
val psiElement = entry.key
val processings = entry.value
progress.setText2((psiElement as? PsiNamedElement)?.getName() ?: "")
progress.checkCanceled()
ProgressManager.getInstance().runProcess(
{
val searchJava = processings.any { it.javaCodeProcessor != null }
val searchKotlin = processings.any { it.kotlinCodeProcessor != null }
referenceSearcher.findExternalCodeProcessingUsages(psiElement, searchJava, searchKotlin)
.filterNot { inConversionScope(it.getElement()) }
.mapTo(refs) { ReferenceInfo(it, psiElement, it.getElement().getContainingFile(), processings) }
},
ProgressPortionReporter(progress, i / map.size().toDouble(), 1.0 / map.size()))
}
if (refs.isEmpty()) return null
return { processUsages(refs) }
}
}
private fun processUsages(refs: Collection<ReferenceInfo>) {
@ReferenceLoop
for ((reference, target, file, processings) in refs.sortBy(ReferenceComparator)) {
val processors = when (reference.getElement().getLanguage()) {
JavaLanguage.INSTANCE -> processings.map { it.javaCodeProcessor }.filterNotNull()
JetLanguage.INSTANCE -> processings.map { it.kotlinCodeProcessor }.filterNotNull()
else -> continue@ReferenceLoop
}
checkReferenceValid(reference)
var references = listOf(reference)
for (processor in processors) {
references = references.flatMap { processor.processUsage(it) ?: listOf(it) }
references.forEach { checkReferenceValid(it) }
}
}
}
private fun checkReferenceValid(reference: PsiReference) {
val element = reference.getElement()
assert(element.isValid() && element.getContainingFile() !is DummyHolder) { "Reference $reference got invalidated" }
}
private object ReferenceComparator : Comparator<ReferenceInfo> {
override fun compare(info1: ReferenceInfo, info2: ReferenceInfo): Int {
val element1 = info1.reference.getElement()
val element2 = info2.reference.getElement()
val deepness1 = element1.deepnessInTree()
val deepness2 = element2.deepnessInTree()
if (deepness1 != deepness2) { // put deeper elements first to not invalidate them when processing ancestors
return -deepness1.compareTo(deepness2)
}
// process elements of the same deepness from right to left so that right-side of assignments is not invalidated by processing of the left one
return -element1.getStartOffsetInParent().compareTo(element2.getStartOffsetInParent())
}
private fun PsiElement.deepnessInTree() = parents(withItself = true).takeWhile { it !is PsiFile }.count()
}
private class ProgressPortionReporter(
indicator: ProgressIndicator,
private val start: Double,
private val portion: Double
) : DelegatingProgressIndicator(indicator) {
init {
setFraction(0.0)
}
override fun start() {
setFraction(0.0)
}
override fun stop() {
setFraction(portion)
}
override fun setFraction(fraction: Double) {
super.setFraction(start + (fraction * portion))
}
override fun getFraction(): Double {
return (super.getFraction() - start) / portion
}
override fun setText(text: String?) {
}
override fun setText2(text: String?) {
}
}
}
@@ -135,7 +135,7 @@ public object JavaToKotlinTranslator {
val file = createFile(javaCode)
if (file is PsiJavaFile) {
val converter = JavaToKotlinConverter(file.getProject(), ConverterSettings.defaultSettings, EmptyReferenceSearcher, EmptyResolverForConverter, null)
return prettify(converter.elementsToKotlin(listOf(JavaToKotlinConverter.InputElement(file, null))).single()!!.text)
return prettify(converter.elementsToKotlin(listOf(JavaToKotlinConverter.InputElement(file, null))).results.single()!!.text)
}
return ""
}
@@ -16,49 +16,67 @@
package org.jetbrains.kotlin.j2k
import com.intellij.psi.PsiVariable
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReferenceExpression
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.fileTypes.FileTypeManager
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.*
import com.intellij.psi.search.DelegatingGlobalSearchScope
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.PsiClass
import com.intellij.psi.search.searches.ClassInheritorsSearch
import com.intellij.psi.search.searches.OverridingMethodsSearch
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.idea.JetLanguage
public trait ReferenceSearcher {
fun findVariableUsages(variable: PsiVariable, scope: PsiElement): Collection<PsiReferenceExpression>
fun findMethodCalls(method: PsiMethod, scope: PsiElement): Collection<PsiMethodCallExpression>
fun findLocalUsages(element: PsiElement, scope: PsiElement): Collection<PsiReference>
fun hasInheritors(`class`: PsiClass): Boolean
fun hasOverrides(method: PsiMethod): Boolean
fun findExternalCodeProcessingUsages(element: PsiElement, searchJava: Boolean, searchKotlin: Boolean): Collection<PsiReference>
}
public fun ReferenceSearcher.findVariableUsages(variable: PsiVariable, scope: PsiElement): Collection<PsiReferenceExpression>
= findLocalUsages(variable, scope).filterIsInstance<PsiReferenceExpression>()
public fun ReferenceSearcher.findMethodCalls(method: PsiMethod, scope: PsiElement): Collection<PsiMethodCallExpression> {
return findLocalUsages(method, scope).map {
if (it is PsiReferenceExpression) {
val methodCall = it.getParent() as? PsiMethodCallExpression
if (methodCall?.getMethodExpression() == it) methodCall else null
}
else {
null
}
}.filterNotNull()
}
public object EmptyReferenceSearcher: ReferenceSearcher {
override fun findVariableUsages(variable: PsiVariable, scope: PsiElement) = listOf<PsiReferenceExpression>()
override fun findMethodCalls(method: PsiMethod, scope: PsiElement) = listOf<PsiMethodCallExpression>()
override fun findLocalUsages(element: PsiElement, scope: PsiElement): Collection<PsiReference> = emptyList()
override fun hasInheritors(`class`: PsiClass) = false
override fun hasOverrides(method: PsiMethod) = false
override fun findExternalCodeProcessingUsages(element: PsiElement, searchJava: Boolean, searchKotlin: Boolean): Collection<PsiReference>
= throw UnsupportedOperationException()
}
public object IdeaReferenceSearcher : ReferenceSearcher {
override fun findVariableUsages(variable: PsiVariable, scope: PsiElement): Collection<PsiReferenceExpression> {
return ReferencesSearch.search(variable, LocalSearchScope(scope)).findAll().filterIsInstance<PsiReferenceExpression>()
}
override fun findMethodCalls(method: PsiMethod, scope: PsiElement): Collection<PsiMethodCallExpression> {
return ReferencesSearch.search(method, LocalSearchScope(scope)).findAll().map {
if (it is PsiReferenceExpression) {
val methodCall = it.getParent() as? PsiMethodCallExpression
if (methodCall?.getMethodExpression() == it) methodCall else null
}
else {
null
}
}.filterNotNull()
}
public object IdeaReferenceSearcher: ReferenceSearcher {
override fun findLocalUsages(element: PsiElement, scope: PsiElement) = ReferencesSearch.search(element, LocalSearchScope(scope)).findAll()
override fun hasInheritors(`class`: PsiClass) = ClassInheritorsSearch.search(`class`, false).any()
override fun hasOverrides(method: PsiMethod) = OverridingMethodsSearch.search(method, false).any()
override fun findExternalCodeProcessingUsages(element: PsiElement, searchJava: Boolean, searchKotlin: Boolean): Collection<PsiReference> {
val searchScope = object : DelegatingGlobalSearchScope(GlobalSearchScope.projectScope(element.getProject())) {
override fun contains(file: VirtualFile): Boolean {
if (!super.contains(file)) return false
return when (FileTypeManager.getInstance().getFileTypeByFile(file)) {
JavaLanguage.INSTANCE.getAssociatedFileType() -> searchJava
JetLanguage.INSTANCE.getAssociatedFileType() -> searchKotlin
else -> false
}
}
}
return ReferencesSearch.search(element, searchScope).findAll()
}
}
@@ -79,6 +79,11 @@ fun PsiReferenceExpression.isQualifierEmptyOrThis(): Boolean {
return qualifier == null || (qualifier is PsiThisExpression && qualifier.getQualifier() == null)
}
fun PsiReferenceExpression.isQualifierEmptyOrClass(psiClass: PsiClass): Boolean {
val qualifier = getQualifierExpression()
return qualifier == null || (qualifier is PsiReferenceExpression && qualifier.isReferenceTo(psiClass))
}
fun PsiElement.isInSingleLine(): Boolean {
if (this is PsiWhiteSpace) {
val text = getText()!!
@@ -17,8 +17,10 @@
package org.jetbrains.kotlin.j2k.usageProcessing
import com.intellij.psi.*
import org.jetbrains.kotlin.j2k.*
import org.jetbrains.kotlin.j2k.AccessorKind
import org.jetbrains.kotlin.j2k.CodeConverter
import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.psi.*
class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKind: AccessorKind, val propertyName: String) : UsageProcessing {
override val targetElement: PsiElement get() = accessorMethod
@@ -34,11 +36,11 @@ class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKi
val propertyAccess = QualifiedExpression(codeConverter.convertExpression(methodExpr.getQualifierExpression()), propertyName).assignNoPrototype()
if (accessorKind == AccessorKind.GETTER) {
if (arguments.size != 0) return null // incorrect call
if (arguments.size() != 0) return null // incorrect call
return propertyAccess
}
else {
if (arguments.size != 1) return null // incorrect call
if (arguments.size() != 1) return null // incorrect call
val argument = codeConverter.convertExpression(arguments[0])
return AssignmentExpression(propertyAccess, argument, "=")
}
@@ -47,10 +49,42 @@ class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKi
override val javaCodeProcessor: ExternalCodeProcessor? = null
override val kotlinCodeProcessor: ExternalCodeProcessor? = object: ExternalCodeProcessor {
override fun processUsage(reference: PsiReference) {
//TODO
//reference.getElement() as JetSimpleNameExpression
override val kotlinCodeProcessor: ExternalCodeProcessor? = if (accessorMethod.hasModifierProperty(PsiModifier.PRIVATE))
null
else
object : ExternalCodeProcessor {
override fun processUsage(reference: PsiReference): Collection<PsiReference>? {
val nameExpr = reference.getElement() as? JetSimpleNameExpression ?: return null
val callExpr = nameExpr.getParent() as? JetCallExpression ?: return null
val arguments = callExpr.getValueArguments()
val factory = JetPsiFactory(nameExpr.getProject())
var propertyNameExpr = factory.createSimpleName(propertyName)
if (accessorKind == AccessorKind.GETTER) {
if (arguments.size() != 0) return null // incorrect call
propertyNameExpr = callExpr.replace(propertyNameExpr) as JetSimpleNameExpression
return listOf(propertyNameExpr.getReference())
}
else {
val value = arguments.singleOrNull()?.getArgumentExpression() ?: return null
var assignment = factory.createExpression("a = b") as JetBinaryExpression
assignment.getRight()!!.replace(value)
val qualifiedExpression = callExpr.getParent() as? JetQualifiedExpression
if (qualifiedExpression != null && qualifiedExpression.getSelectorExpression() == callExpr) {
callExpr.replace(propertyNameExpr)
assignment.getLeft()!!.replace(qualifiedExpression)
assignment = qualifiedExpression.replace(assignment) as JetBinaryExpression
return listOf((assignment.getLeft() as JetQualifiedExpression).getSelectorExpression()!!.getReference())
}
else {
assignment.getLeft()!!.replace(propertyNameExpr)
assignment = callExpr.replace(assignment) as JetBinaryExpression
return listOf(assignment.getLeft()!!.getReference())
}
}
}
}
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.j2k.usageProcessing
import com.intellij.psi.PsiReference
class ElementRenamedCodeProcessor(private val newName: String) : ExternalCodeProcessor {
override fun processUsage(reference: PsiReference): Collection<PsiReference>? {
val newReference = reference.handleElementRename(newName).getReference()!!
return listOf(newReference)
}
}
@@ -17,44 +17,135 @@
package org.jetbrains.kotlin.j2k.usageProcessing
import com.intellij.psi.*
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.j2k.AccessorKind
import org.jetbrains.kotlin.j2k.CodeConverter
import org.jetbrains.kotlin.j2k.ast.*
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.psi.JetPsiFactory
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
class FieldToPropertyProcessing(val field: PsiField, val propertyName: String, val isNullable: Boolean) : UsageProcessing {
override val targetElement: PsiElement get() = field
override val convertedCodeProcessor = if (field.getName() != propertyName) {
object: ConvertedCodeProcessor {
override fun convertVariableUsage(expression: PsiReferenceExpression, codeConverter: CodeConverter): Expression? {
val identifier = Identifier(propertyName, isNullable).assignNoPrototype()
override val convertedCodeProcessor = if (field.getName() != propertyName) MyConvertedCodeProcessor() else null
val qualifier = expression.getQualifierExpression()
if (qualifier != null) {
return QualifiedExpression(codeConverter.convertExpression(qualifier), identifier)
override var javaCodeProcessor = if (field.hasModifierProperty(PsiModifier.PRIVATE))
null
else if (!field.hasModifierProperty(PsiModifier.STATIC))
UseAccessorsJavaCodeProcessor()
else if (field.getName() != propertyName)
ElementRenamedCodeProcessor(propertyName)
else
null
override val kotlinCodeProcessor = if (field.getName() != propertyName) ElementRenamedCodeProcessor(propertyName) else null
private inner class MyConvertedCodeProcessor : ConvertedCodeProcessor {
override fun convertVariableUsage(expression: PsiReferenceExpression, codeConverter: CodeConverter): Expression? {
val identifier = Identifier(propertyName, isNullable).assignNoPrototype()
val qualifier = expression.getQualifierExpression()
if (qualifier != null) {
return QualifiedExpression(codeConverter.convertExpression(qualifier), identifier)
}
else {
// check if field name is shadowed
val elementFactory = PsiElementFactory.SERVICE.getInstance(expression.getProject())
val refExpr = try {
elementFactory.createExpressionFromText(propertyName, expression) as? PsiReferenceExpression ?: return identifier
}
else {
// check if field name is shadowed
val elementFactory = PsiElementFactory.SERVICE.getInstance(expression.getProject())
val refExpr = try {
elementFactory.createExpressionFromText(propertyName, expression) as? PsiReferenceExpression ?: return identifier
}
catch(e: IncorrectOperationException) {
return identifier
}
return if (refExpr.resolve() == null)
identifier
else
QualifiedExpression(ThisExpression(Identifier.Empty).assignNoPrototype(), identifier) //TODO: this is not correct in case of nested/anonymous classes
catch(e: IncorrectOperationException) {
return identifier
}
return if (refExpr.resolve() == null)
identifier
else
QualifiedExpression(ThisExpression(Identifier.Empty).assignNoPrototype(), identifier) //TODO: this is not correct in case of nested/anonymous classes
}
}
}
else {
null
private inner class UseAccessorsJavaCodeProcessor : ExternalCodeProcessor {
private val factory = PsiElementFactory.SERVICE.getInstance(field.getProject())
override fun processUsage(reference: PsiReference): Collection<PsiReference>? {
val refExpr = reference.getElement() as? PsiReferenceExpression ?: return null
val qualifier = refExpr.getQualifierExpression()
val parent = refExpr.getParent()
when (parent) {
is PsiAssignmentExpression -> {
if (refExpr == parent.getLExpression()) {
if (parent.getOperationTokenType() == JavaTokenType.EQ) {
val callExpr = parent.replace(generateSetterCall(qualifier, parent.getRExpression() ?: return null)) as PsiMethodCallExpression
return listOf(callExpr.getMethodExpression())
}
else {
val assignmentOpText = parent.getOperationSign().getText()
assert(assignmentOpText.endsWith("="))
val opText = assignmentOpText.substring(0, assignmentOpText.length() - 1)
return parent.replaceWithModificationCalls(qualifier, opText, parent.getRExpression() ?: return null)
}
}
}
is PsiPrefixExpression, is PsiPostfixExpression -> {
//TODO: what if it's used as value?
val operationType = if (parent is PsiPrefixExpression)
parent.getOperationTokenType()
else
(parent as PsiPostfixExpression).getOperationTokenType()
val opText = when (operationType) {
JavaTokenType.PLUSPLUS -> "+"
JavaTokenType.MINUSMINUS -> "-"
else -> null
}
if (opText != null) {
return (parent as PsiExpression).replaceWithModificationCalls(qualifier, opText, factory.createExpressionFromText("1", null))
}
}
}
val callExpr = refExpr.replace(generateGetterCall(qualifier)) as PsiMethodCallExpression
return listOf(callExpr.getMethodExpression())
}
//TODO: what if qualifier has side effects?
private fun PsiExpression.replaceWithModificationCalls(qualifier: PsiExpression?, op: String, value: PsiExpression): Collection<PsiReference> {
var getCall = generateGetterCall(qualifier)
var binary = factory.createExpressionFromText("x $op y", null) as PsiBinaryExpression
binary.getLOperand().replace(getCall)
binary.getROperand()!!.replace(value)
var setCall = generateSetterCall(qualifier, binary) as PsiMethodCallExpression
setCall = this.replace(setCall) as PsiMethodCallExpression
binary = setCall.getArgumentList().getExpressions().single() as PsiBinaryExpression
getCall = binary.getLOperand() as PsiMethodCallExpression
return listOf(getCall.getMethodExpression().getReference(), setCall.getMethodExpression().getReference())
}
private fun generateGetterCall(qualifier: PsiExpression?): PsiMethodCallExpression {
val text = accessorName(AccessorKind.GETTER) + "()"
val expressionText = if (qualifier != null)
"${qualifier.getText()}.$text"
else
text
return factory.createExpressionFromText(expressionText, null) as PsiMethodCallExpression
}
private fun generateSetterCall(qualifier: PsiExpression?, value: PsiExpression): PsiExpression {
val text = accessorName(AccessorKind.SETTER) + "(" + value.getText() + ")"
val expressionText = if (qualifier != null)
"${qualifier.getText()}.$text"
else
text
return factory.createExpressionFromText(expressionText, null)
}
}
override val javaCodeProcessor: ExternalCodeProcessor? get() = null //TODO
override val kotlinCodeProcessor: ExternalCodeProcessor? get() = null //TODO
private fun accessorName(kind: AccessorKind)
= (if (kind == AccessorKind.GETTER) "get" else "set") + propertyName.capitalize()
}
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.j2k.usageProcessing
import com.intellij.psi.*
public class MethodIntoObjectProcessing(private val method: PsiMethod, private val objectName: String) : UsageProcessing {
override val targetElement: PsiElement get() = method
override val convertedCodeProcessor: ConvertedCodeProcessor? get() = null
override val javaCodeProcessor = object: ExternalCodeProcessor {
override fun processUsage(reference: PsiReference): Collection<PsiReference>? {
val refExpr = reference.getElement() as? PsiReferenceExpression ?: return null
val qualifier = refExpr.getQualifierExpression()
val factory = PsiElementFactory.SERVICE.getInstance(method.getProject())
if (qualifier != null) {
val newQualifier = factory.createExpressionFromText(qualifier.getText() + "." + objectName, null)
qualifier.replace(newQualifier)
return listOf(reference)
}
else {
var qualifiedExpr = factory.createExpressionFromText(objectName + "." + refExpr.getText(), null) as PsiReferenceExpression
qualifiedExpr = refExpr.replace(qualifiedExpr) as PsiReferenceExpression
return listOf(qualifiedExpr.getReference())
}
}
}
override val kotlinCodeProcessor: ExternalCodeProcessor? get() = null
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.j2k.usageProcessing
import com.intellij.psi.*
import org.jetbrains.kotlin.load.java.JvmAbi
class ToObjectWithOnlyMethodsProcessing(private val psiClass: PsiClass) : UsageProcessing {
override val targetElement: PsiElement get() = psiClass
override val convertedCodeProcessor: ConvertedCodeProcessor? get() = null
override val javaCodeProcessor = object: ExternalCodeProcessor {
override fun processUsage(reference: PsiReference): Collection<PsiReference>? {
val refExpr = reference.getElement() as? PsiReferenceExpression ?: return null
val factory = PsiElementFactory.SERVICE.getInstance(psiClass.getProject())
var qualifiedExpr = factory.createExpressionFromText(refExpr.getText() + "." + JvmAbi.INSTANCE_FIELD, null) as PsiReferenceExpression
qualifiedExpr = refExpr.replace(qualifiedExpr) as PsiReferenceExpression
return listOf(qualifiedExpr.getReference())
}
}
override val kotlinCodeProcessor: ExternalCodeProcessor? get() = null
}
@@ -16,10 +16,11 @@
package org.jetbrains.kotlin.j2k.usageProcessing
import org.jetbrains.kotlin.j2k.ast.Expression
import com.google.common.collect.Multimap
import com.intellij.psi.*
import org.jetbrains.kotlin.j2k.CodeConverter
import org.jetbrains.kotlin.j2k.SpecialExpressionConverter
import org.jetbrains.kotlin.j2k.ast.Expression
trait UsageProcessing {
val targetElement: PsiElement
@@ -35,24 +36,32 @@ trait ConvertedCodeProcessor {
}
trait ExternalCodeProcessor {
fun processUsage(reference: PsiReference)
fun processUsage(reference: PsiReference): Collection<PsiReference>?
}
class UsageProcessingExpressionConverter(val processings: Map<PsiElement, UsageProcessing>) : SpecialExpressionConverter {
class UsageProcessingExpressionConverter(val processings: Map<PsiElement, Collection<UsageProcessing>>) : SpecialExpressionConverter {
override fun convertExpression(expression: PsiExpression, codeConverter: CodeConverter): Expression? {
if (processings.isEmpty()) return null
when (expression) {
is PsiReferenceExpression -> {
val target = expression.resolve() as? PsiVariable ?: return null
val processor = processings[target]?.convertedCodeProcessor ?: return null
return processor.convertVariableUsage(expression, codeConverter)
val forTarget = processings[target] ?: return null
for (processing in forTarget) {
val converted = processing.convertedCodeProcessor?.convertVariableUsage(expression, codeConverter)
if (converted != null) return converted
}
return null
}
is PsiMethodCallExpression -> {
val target = expression.getMethodExpression().resolve() as? PsiMethod ?: return null
val processor = processings[target]?.convertedCodeProcessor ?: return null
return processor.convertMethodUsage(expression, codeConverter)
val forTarget = processings[target] ?: return null
for (processing in forTarget) {
val converted = processing.convertedCodeProcessor?.convertMethodUsage(expression, codeConverter)
if (converted != null) return converted
}
return null
}
else -> return null
@@ -0,0 +1,15 @@
public class AAA {
public void foo() {
setX(getX() + 1);
}
private static int ourX = 42;
public static int getX() {
return ourX;
}
public static void setX(int x) {
ourX = x;
}
}
@@ -0,0 +1,10 @@
public class AAA {
public fun foo() {
x = x + 1
}
companion object {
public var x: Int = 42
}
}
@@ -0,0 +1,33 @@
public class AAA {
private static int ourX = 42;
private static int y = 0;
private static int z = 0;
public static int getX() {
return ourX;
}
public static void setX(int x) {
ourX = x;
}
public static int getY() {
return y;
}
public static void setY(int y) {
AAA.y = y;
}
public static int getZ() {
return z;
}
public static void setZ(int z) {
Other.z = z;
}
}
class Other {
public static int z = 0;
}
@@ -0,0 +1,13 @@
public object AAA {
public var x: Int = 42
public var y: Int = 0
public val z: Int = 0
public fun setZ(z: Int) {
Other.z = z
}
}
object Other {
public var z: Int = 0
}
@@ -0,0 +1,11 @@
package test;
class JavaClass {
public int field = 0;
protected int myProperty = 0;
public int getProperty() {
return myProperty;
}
}
@@ -0,0 +1,8 @@
package test
open class JavaClass {
public var field: Int = 0
public var property: Int = 0
protected set
}
@@ -0,0 +1,12 @@
package test;
class C extends JavaClass {
public void foo(JavaClass javaClass) {
javaClass.field++;
--javaClass.field;
myProperty = javaClass.field;
javaClass.field -= field;
field = myProperty;
field *= 2;
}
}
@@ -0,0 +1,12 @@
package test;
class C extends JavaClass {
public void foo(JavaClass javaClass) {
javaClass.setField(javaClass.getField() + 1);
javaClass.setField(javaClass.getField() - 1);
setProperty(javaClass.getField());
javaClass.setField(javaClass.getField() - getField());
setField(getProperty());
setField(getField() * 2);
}
}
@@ -0,0 +1,10 @@
package test
class C : JavaClass() {
public fun foo(javaClass: JavaClass) {
javaClass.field++
myProperty = javaClass.field
field = myProperty
field *= 2
}
}
@@ -0,0 +1,10 @@
package test
class C : JavaClass() {
public fun foo(javaClass: JavaClass) {
javaClass.field++
property = javaClass.field
field = property
field *= 2
}
}
@@ -0,0 +1,11 @@
fun foo1(a: AAA) {
a.setX(a.getX() + 1)
}
fun foo2(a: AAA?) {
a?.setX((a?.getX() ?: 0) + 1)
}
fun AAA.foo() {
setX(getX() + 1)
}
@@ -0,0 +1,11 @@
fun foo1(a: AAA) {
a.x = a.x + 1
}
fun foo2(a: AAA?) {
a?.x = (a?.x ?: 0) + 1
}
fun AAA.foo() {
x = x + 1
}
@@ -0,0 +1,3 @@
void foo(AAA a) {
a.setX(a.getX() + 1)
}
@@ -0,0 +1,3 @@
void foo(AAA a) {
a.setX(a.getX() + 1)
}
@@ -0,0 +1,14 @@
package test;
class ClassWithStatics {
public void instanceMethod(){}
public static void staticMethod(int p) {}
public static final int staticField = 1;
protected static int ourValue = 0;
public static int getValue() { return ourValue; }
public static void setValue(int value) { ourValue = value; }
}
@@ -0,0 +1,16 @@
package test
open class ClassWithStatics {
public fun instanceMethod() {
}
companion object {
public fun staticMethod(p: Int) {
}
public val staticField: Int = 1
public var value: Int = 0
}
}
@@ -0,0 +1,16 @@
package test;
class C {
void foo(ClassWithStatics c) {
ClassWithStatics.staticMethod(ClassWithStatics.staticField);
c.instanceMethod();
ClassWithStatics.staticField += 2;
}
}
class D extends ClassWithStatics {
void foo() {
staticMethod(staticField);
ourValue *= 2;
}
}
@@ -0,0 +1,16 @@
package test;
class C {
void foo(ClassWithStatics c) {
ClassWithStatics.Companion.staticMethod(ClassWithStatics.staticField);
c.instanceMethod();
ClassWithStatics.staticField += 2;
}
}
class D extends ClassWithStatics {
void foo() {
Companion.staticMethod(staticField);
value *= 2;
}
}
@@ -0,0 +1,5 @@
package test
fun foo() {
ClassWithStatics.staticMethod(ClassWithStatics.staticField)
}
@@ -0,0 +1,5 @@
package test
fun foo() {
ClassWithStatics.staticMethod(ClassWithStatics.staticField)
}
@@ -0,0 +1,6 @@
package test;
class PureUtils {
public static void foo1(int p) {}
public static int foo2() { return 1; }
}
@@ -0,0 +1,10 @@
package test
object PureUtils {
public fun foo1(p: Int) {
}
public fun foo2(): Int {
return 1
}
}
@@ -0,0 +1,8 @@
package test;
class Utils {
public static void foo1(int p) {}
public static int foo2() { return 1; }
public static final int staticField = 1;
}
+12
View File
@@ -0,0 +1,12 @@
package test
object Utils {
public fun foo1(p: Int) {
}
public fun foo2(): Int {
return 1
}
public val staticField: Int = 1
}
@@ -0,0 +1,9 @@
package test;
class C {
void foo() {
Utils.foo1(Utils.staticField);
Utils.staticField += Utils.foo2();
PureUtils.foo1(PureUtils.foo2())
}
}
@@ -0,0 +1,9 @@
package test;
class C {
void foo() {
Utils.INSTANCE$.foo1(Utils.staticField);
Utils.staticField += Utils.INSTANCE$.foo2();
PureUtils.INSTANCE$.foo1(PureUtils.INSTANCE$.foo2())
}
}
@@ -0,0 +1,6 @@
package test
fun foo() {
Utils.foo1(Utils.staticField)
PureUtils.foo1(PureUtils.foo2())
}
@@ -0,0 +1,6 @@
package test
fun foo() {
Utils.foo1(Utils.staticField)
PureUtils.foo1(PureUtils.foo2())
}
@@ -16,49 +16,74 @@
package org.jetbrains.kotlin.j2k
import java.io.File
import com.intellij.testFramework.LightPlatformTestCase
import com.intellij.psi.codeStyle.CodeStyleManager
import org.jetbrains.kotlin.test.JetTestUtils
import com.intellij.openapi.project.Project
import com.intellij.openapi.command.WriteCommandAction
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.JetWithJdkAndRuntimeLightProjectDescriptor
import com.intellij.openapi.progress.EmptyProgressIndicator
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiJavaFile
import com.intellij.psi.PsiManager
import java.util.ArrayList
import org.jetbrains.kotlin.psi.JetFile
import com.intellij.testFramework.LightPlatformTestCase
import org.jetbrains.kotlin.idea.JetWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.test.JetTestUtils
import java.io.File
import java.util.ArrayList
public abstract class AbstractJavaToKotlinConverterMultiFileTest : AbstractJavaToKotlinConverterTest() {
public fun doTest(dirPath: String) {
val project = LightPlatformTestCase.getProject()!!
val psiManager = PsiManager.getInstance(project)
val javaFiles = File(dirPath).listFiles { file, name -> name.endsWith(".java") }
val psiFiles = ArrayList<PsiJavaFile>()
for (javaFile: File in javaFiles) {
val filesToConvert = File(dirPath).listFiles { file, name -> name.endsWith(".java") }
val psiFilesToConvert = ArrayList<PsiJavaFile>()
for (javaFile in filesToConvert) {
val virtualFile = addFile(javaFile, "test")
val psiFile = psiManager.findFile(virtualFile) as PsiJavaFile
psiFiles.add(psiFile)
psiFilesToConvert.add(psiFile)
}
val externalFiles = File(dirPath + File.separator + "external").listFiles { file, name -> name.endsWith(".java") || name.endsWith(".kt") }
val externalPsiFiles = ArrayList<PsiFile>()
for (file in externalFiles) {
val virtualFile = addFile(file, "test")
val psiFile = psiManager.findFile(virtualFile)!!
externalPsiFiles.add(psiFile)
assert(psiFile is PsiJavaFile || psiFile is JetFile)
}
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings,
IdeaReferenceSearcher, IdeaResolverForConverter, J2kPostProcessor(formatCode = true))
val inputElements = psiFiles.map { JavaToKotlinConverter.InputElement(it, it) }
val results: List<String> = converter.elementsToKotlin(inputElements).map { it!!.text }
val inputElements = psiFilesToConvert.map { JavaToKotlinConverter.InputElement(it, it) }
val (results, externalCodeProcessor) = converter.elementsToKotlin(inputElements)
fun expectedFile(i: Int) = File(javaFiles[i].getPath().replace(".java", ".kt"))
val process = externalCodeProcessor?.invoke(EmptyProgressIndicator())
project.executeWriteCommand("") { process?.invoke() }
val jetFiles = ArrayList<JetFile>()
for (i in javaFiles.indices) {
deleteFile(psiFiles[i].getVirtualFile())
val virtualFile = addFile(results[i], expectedFile(i).getName(), "test")
jetFiles.add(psiManager.findFile(virtualFile) as JetFile)
fun expectedResultFile(i: Int) = File(filesToConvert[i].getPath().replace(".java", ".kt"))
val resultFiles = ArrayList<JetFile>()
for ((i, javaFile) in psiFilesToConvert.withIndex()) {
deleteFile(javaFile.getVirtualFile())
val virtualFile = addFile(results.map { it!!.text }[i], expectedResultFile(i).getName(), "test")
resultFiles.add(psiManager.findFile(virtualFile) as JetFile)
}
for ((i, jetFile) in jetFiles.withIndex()) {
JetTestUtils.assertEqualsToFile(expectedFile(i), addErrorsDump(jetFile))
for ((i, kotlinFile) in resultFiles.withIndex()) {
JetTestUtils.assertEqualsToFile(expectedResultFile(i), addErrorsDump(kotlinFile))
}
for ((externalFile, externalPsiFile) in externalFiles.zip(externalPsiFiles)) {
val expectedFile = File(externalFile.getPath() + ".expected")
var resultText = if (externalPsiFile is JetFile) {
addErrorsDump(externalPsiFile)
}
else {
//TODO: errors dump for java files too
externalPsiFile.getText()
}
JetTestUtils.assertEqualsToFile(expectedFile, resultText)
}
}
@@ -109,7 +109,7 @@ public abstract class AbstractJavaToKotlinConverterSingleFileTest : AbstractJava
val converter = JavaToKotlinConverter(project, settings,
IdeaReferenceSearcher, IdeaResolverForConverter, J2kPostProcessor(formatCode = false))
val inputElements = listOf(JavaToKotlinConverter.InputElement(file, file))
return converter.elementsToKotlin(inputElements).single()!!.text
return converter.elementsToKotlin(inputElements).results.single()!!.text
}
private fun methodToKotlin(text: String, settings: ConverterSettings, project: Project): String {
@@ -45,8 +45,8 @@ public abstract class AbstractJavaToKotlinConverterTest : LightCodeInsightFixtur
super.tearDown()
}
private fun addFile(fileName: String, packageName: String) {
addFile(File("j2k/testData/$fileName"), packageName)
private fun addFile(fileName: String, dirName: String) {
addFile(File("j2k/testData/$fileName"), dirName)
}
protected fun addFile(file: File, dirName: String): VirtualFile {
@@ -58,7 +58,7 @@ public abstract class AbstractJavaToKotlinConverterTest : LightCodeInsightFixtur
override fun compute(): VirtualFile? {
val root = LightPlatformTestCase.getSourceRoot()!!
val virtualDir = root.findChild(dirName) ?: root.createChildDirectory(null, dirName)
val virtualFile = virtualDir.createChildData(null, fileName)!!
val virtualFile = virtualDir.createChildData(null, fileName)
virtualFile.getOutputStream(null)!!.writer().use { it.write(text) }
return virtualFile
}
@@ -1616,6 +1616,18 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
doTest(fileName);
}
@TestMetadata("InCompanionObject.java")
public void testInCompanionObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/InCompanionObject.java");
doTest(fileName);
}
@TestMetadata("InObject.java")
public void testInObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/InObject.java");
doTest(fileName);
}
@TestMetadata("JavaKeywordPropertyName.java")
public void testJavaKeywordPropertyName() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/JavaKeywordPropertyName.java");
@@ -30,12 +30,16 @@ import java.util.regex.Pattern;
@SuppressWarnings("all")
@TestMetadata("j2k/testData/multiFile")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({
})
@RunWith(JUnit3RunnerWithInners.class)
public class JavaToKotlinConverterMultiFileTestGenerated extends AbstractJavaToKotlinConverterMultiFileTest {
public void testAllFilesPresentInMultiFile() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/multiFile"), Pattern.compile("^([^\\.]+)$"), true);
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/multiFile"), Pattern.compile("^([^\\.]+)$"), false);
}
@TestMetadata("FieldToProperty")
public void testFieldToProperty() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/multiFile/FieldToProperty/");
doTest(fileName);
}
@TestMetadata("GetterAndSetterUsages")
@@ -44,4 +48,15 @@ public class JavaToKotlinConverterMultiFileTestGenerated extends AbstractJavaToK
doTest(fileName);
}
@TestMetadata("ToCompanionObject")
public void testToCompanionObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/multiFile/ToCompanionObject/");
doTest(fileName);
}
@TestMetadata("ToObject")
public void testToObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/multiFile/ToObject/");
doTest(fileName);
}
}
@@ -1616,6 +1616,18 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
doTest(fileName);
}
@TestMetadata("InCompanionObject.java")
public void testInCompanionObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/InCompanionObject.java");
doTest(fileName);
}
@TestMetadata("InObject.java")
public void testInObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/InObject.java");
doTest(fileName);
}
@TestMetadata("JavaKeywordPropertyName.java")
public void testJavaKeywordPropertyName() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/JavaKeywordPropertyName.java");