From d0637ccaf47bcd691443b1893817a1f6f43f0c4c Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Fri, 3 Apr 2015 02:48:39 +0300 Subject: [PATCH] Conversion from Java on paste: post processing of code really works --- .../kotlin/psi/psiUtil/jetPsiUtil.kt | 31 +++++++ .../kotlin/idea/actions/JavaToKotlinAction.kt | 7 +- .../copy/ConvertJavaCopyPastePostProcessor.kt | 56 ++++-------- .../kotlin/idea/j2k/J2kPostProcessor.kt | 88 ++++++++++++++----- .../conversion/RedundantTypeCast.expected.kt | 6 ++ .../conversion/RedundantTypeCast.java | 7 ++ .../conversion/RedundantTypeCast.to.kt | 3 + .../conversion/RedundantTypeCast2.expected.kt | 5 ++ .../conversion/RedundantTypeCast2.java | 6 ++ .../conversion/RedundantTypeCast2.to.kt | 3 + ...otlinCopyPasteConversionTestGenerated.java | 12 +++ .../kotlin/j2k/AfterConversionPass.kt | 43 +++++---- j2k/src/org/jetbrains/kotlin/j2k/Converter.kt | 7 +- .../kotlin/j2k/JavaToKotlinConverter.kt | 40 +++++---- .../kotlin/j2k/JavaToKotlinTranslator.kt | 4 +- ...tractJavaToKotlinConverterMultiFileTest.kt | 6 +- ...ractJavaToKotlinConverterSingleFileTest.kt | 6 +- 17 files changed, 219 insertions(+), 111 deletions(-) create mode 100644 idea/testData/copyPaste/conversion/RedundantTypeCast.expected.kt create mode 100644 idea/testData/copyPaste/conversion/RedundantTypeCast.java create mode 100644 idea/testData/copyPaste/conversion/RedundantTypeCast.to.kt create mode 100644 idea/testData/copyPaste/conversion/RedundantTypeCast2.expected.kt create mode 100644 idea/testData/copyPaste/conversion/RedundantTypeCast2.java create mode 100644 idea/testData/copyPaste/conversion/RedundantTypeCast2.to.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt index 9a988cc614f..c125b20898f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt @@ -448,3 +448,34 @@ public fun JetStringTemplateExpression.getContentRange(): TextRange { public fun JetStringTemplateExpression.isSingleQuoted(): Boolean = getNode().getFirstChildNode().getTextLength() == 1 + +public fun PsiFile.elementsInRange(range: TextRange): List { + var offset = range.getStartOffset() + val result = ArrayList() + while (offset < range.getEndOffset()) { + val currentRange = TextRange(offset, range.getEndOffset()) + val leaf = findFirstLeafWhollyInRange(this, currentRange) ?: break + + val element = leaf + .parents(withItself = true) + .first { + val parent = it.getParent() + parent == null || parent.getTextRange() !in currentRange + } + result.add(element) + + offset = element.getTextRange().getEndOffset() + } + return result +} + +private fun findFirstLeafWhollyInRange(file: PsiFile, range: TextRange): PsiElement? { + var element = file.findElementAt(range.getStartOffset()) ?: return null + var elementRange = element.getTextRange() + if (elementRange.getStartOffset() < range.getStartOffset()) { + element = file.findElementAt(elementRange.getEndOffset()) ?: return null + elementRange = element.getTextRange() + } + assert(elementRange.getStartOffset() >= range.getStartOffset()) + return if (elementRange.getEndOffset() <= range.getEndOffset()) element else null +} diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt index 0a7d0cac017..a1185eb4746 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt @@ -50,9 +50,10 @@ public class JavaToKotlinAction : AnAction() { var convertedTexts: List? = null fun convert() { - val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaReferenceSearcher, IdeaResolverForConverter) - convertedTexts = converter.elementsToKotlin(javaFiles.map { it to J2kPostProcessor(it, formatCode = true) }, - ProgressManager.getInstance().getProgressIndicator()) + 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()) } if (!ProgressManager.getInstance().runProcessWithProgressSynchronously( diff --git a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt index a93c62640d0..2f51a6e9d08 100644 --- a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt @@ -27,16 +27,17 @@ import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project import com.intellij.openapi.util.Ref import com.intellij.openapi.util.TextRange -import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.* import com.intellij.psi.codeStyle.CodeStyleManager import org.jetbrains.kotlin.idea.editor.JetEditorOptions import org.jetbrains.kotlin.idea.j2k.IdeaResolverForConverter import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor +import org.jetbrains.kotlin.j2k.AfterConversionPass 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.psi.psiUtil.elementsInRange import org.jetbrains.kotlin.psi.psiUtil.parents import java.awt.datatransfer.Transferable import java.util.ArrayList @@ -73,7 +74,7 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor().map { it to J2kPostProcessor(targetFile, formatCode = false) }) + val inputElements = list.filterIsInstance().map { JavaToKotlinConverter.InputElement(it, null) } + val results = converter.elementsToKotlin(inputElements) var resultIndex = 0 val convertedCodeBuilder = StringBuilder() @@ -157,41 +161,15 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor= range.start) - return if (elementRange.end <= range.end) element else null } private fun okFromDialog(project: Project): Boolean { diff --git a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessor.kt index e3439ee63a7..09cf92dcfe6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessor.kt @@ -16,15 +16,18 @@ package org.jetbrains.kotlin.idea.j2k -import com.intellij.openapi.editor.impl.DocumentImpl -import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.editor.RangeMarker +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiComment import com.intellij.psi.PsiElement +import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.codeStyle.CodeStyleManager import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.searches.ReferencesSearch +import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArguments import org.jetbrains.kotlin.idea.intentions.SimplifyNegatedBinaryExpressionIntention import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfThenToElvisIntention @@ -33,14 +36,19 @@ import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix import org.jetbrains.kotlin.idea.quickfix.RemoveRightPartOfBinaryExpressionFix import org.jetbrains.kotlin.j2k.PostProcessor import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.elementsInRange import org.jetbrains.kotlin.resolve.BindingContext -import java.lang.reflect.Field -import java.lang.reflect.Modifier import java.util.ArrayList -public class J2kPostProcessor(override val contextToAnalyzeIn: PsiElement, private val formatCode: Boolean) : PostProcessor { - override fun analyzeFile(file: JetFile): BindingContext { - return file.analyzeFullyAndGetResult().bindingContext +public class J2kPostProcessor(private val formatCode: Boolean) : PostProcessor { + override fun analyzeFile(file: JetFile, range: TextRange?): BindingContext { + val elements = if (range == null) { + listOf(file) + } + else { + file.elementsInRange(range).filterIsInstance() + } + return file.getResolutionFacade().analyzeFullyAndGetResult(elements).bindingContext } override fun fixForProblem(problem: Diagnostic): (() -> Unit)? { @@ -69,11 +77,35 @@ public class J2kPostProcessor(override val contextToAnalyzeIn: PsiElement, priva } } - override fun doAdditionalProcessing(file: JetFile) { + private enum class RangeFilterResult { + SKIP + GO_INSIDE + PROCESS + } + + override fun doAdditionalProcessing(file: JetFile, rangeMarker: RangeMarker?) { + fun rangeFilter(element: PsiElement): RangeFilterResult { + if (rangeMarker == null) return RangeFilterResult.PROCESS + if (!rangeMarker.isValid()) return RangeFilterResult.SKIP + val range = TextRange(rangeMarker.getStartOffset(), rangeMarker.getEndOffset()) + val elementRange = element.getTextRange()!! + return when { + range.contains(elementRange) -> RangeFilterResult.PROCESS + range.intersects(elementRange) -> RangeFilterResult.GO_INSIDE + else -> RangeFilterResult.SKIP + } + } + val redundantTypeArgs = ArrayList() file.accept(object : JetTreeVisitorVoid(){ + override fun visitElement(element: PsiElement) { + if (rangeFilter(element) != RangeFilterResult.SKIP) { + super.visitElement(element) + } + } + override fun visitTypeArgumentList(typeArgumentList: JetTypeArgumentList) { - if (RemoveExplicitTypeArguments().isApplicableTo(typeArgumentList)) { + if (rangeFilter(typeArgumentList) == RangeFilterResult.PROCESS && RemoveExplicitTypeArguments().isApplicableTo(typeArgumentList)) { redundantTypeArgs.add(typeArgumentList) return } @@ -85,7 +117,7 @@ public class J2kPostProcessor(override val contextToAnalyzeIn: PsiElement, priva super.visitPrefixExpression(expression) val intention = SimplifyNegatedBinaryExpressionIntention() - if (intention.isApplicableTo(expression)) { + if (rangeFilter(expression) == RangeFilterResult.PROCESS && intention.isApplicableTo(expression)) { intention.applyTo(expression) } } @@ -93,19 +125,21 @@ public class J2kPostProcessor(override val contextToAnalyzeIn: PsiElement, priva override fun visitIfExpression(expression: JetIfExpression) { super.visitIfExpression(expression) - run { - val intention = IfThenToSafeAccessIntention() - if (intention.isApplicableTo(expression)) { - intention.applyTo(expression) - return + if (rangeFilter(expression) == RangeFilterResult.PROCESS) { + run { + val intention = IfThenToSafeAccessIntention() + if (intention.isApplicableTo(expression)) { + intention.applyTo(expression) + return + } } - } - run { - val intention = IfThenToElvisIntention() - if (intention.isApplicableTo(expression)) { - intention.applyTo(expression) - return + run { + val intention = IfThenToElvisIntention() + if (intention.isApplicableTo(expression)) { + intention.applyTo(expression) + return + } } } } @@ -116,7 +150,15 @@ public class J2kPostProcessor(override val contextToAnalyzeIn: PsiElement, priva } if (formatCode) { - CodeStyleManager.getInstance(contextToAnalyzeIn.getProject()).reformat(file) + val codeStyleManager = CodeStyleManager.getInstance(file.getProject()) + if (rangeMarker != null) { + if (rangeMarker.isValid()) { + codeStyleManager.reformatRange(file, rangeMarker.getStartOffset(), rangeMarker.getEndOffset()) + } + } + else { + codeStyleManager.reformat(file) + } } } } diff --git a/idea/testData/copyPaste/conversion/RedundantTypeCast.expected.kt b/idea/testData/copyPaste/conversion/RedundantTypeCast.expected.kt new file mode 100644 index 00000000000..edcd7dcf298 --- /dev/null +++ b/idea/testData/copyPaste/conversion/RedundantTypeCast.expected.kt @@ -0,0 +1,6 @@ +fun foo(o: Any) { + if (o is String) { + val l = o.length() + } + somethingElse() +} \ No newline at end of file diff --git a/idea/testData/copyPaste/conversion/RedundantTypeCast.java b/idea/testData/copyPaste/conversion/RedundantTypeCast.java new file mode 100644 index 00000000000..852715f101e --- /dev/null +++ b/idea/testData/copyPaste/conversion/RedundantTypeCast.java @@ -0,0 +1,7 @@ +class C { + void foo(Object o) { + if (o instanceof String) { + int l = ((String) o).length(); + } + } +} \ No newline at end of file diff --git a/idea/testData/copyPaste/conversion/RedundantTypeCast.to.kt b/idea/testData/copyPaste/conversion/RedundantTypeCast.to.kt new file mode 100644 index 00000000000..b148022b41e --- /dev/null +++ b/idea/testData/copyPaste/conversion/RedundantTypeCast.to.kt @@ -0,0 +1,3 @@ +fun foo(o: Any) { + somethingElse() +} \ No newline at end of file diff --git a/idea/testData/copyPaste/conversion/RedundantTypeCast2.expected.kt b/idea/testData/copyPaste/conversion/RedundantTypeCast2.expected.kt new file mode 100644 index 00000000000..f5ec2526675 --- /dev/null +++ b/idea/testData/copyPaste/conversion/RedundantTypeCast2.expected.kt @@ -0,0 +1,5 @@ +fun foo(o: Any) { + if (o !is String) return + val l = o.length() + somethingElse(o as String/* we should not remove this cast because it's not in pasted range*/) +} \ No newline at end of file diff --git a/idea/testData/copyPaste/conversion/RedundantTypeCast2.java b/idea/testData/copyPaste/conversion/RedundantTypeCast2.java new file mode 100644 index 00000000000..fda9930ed43 --- /dev/null +++ b/idea/testData/copyPaste/conversion/RedundantTypeCast2.java @@ -0,0 +1,6 @@ +class C { + void foo(Object o) { + if (!(o instanceof String)) return + int l = ((String) o).length(); + } +} \ No newline at end of file diff --git a/idea/testData/copyPaste/conversion/RedundantTypeCast2.to.kt b/idea/testData/copyPaste/conversion/RedundantTypeCast2.to.kt new file mode 100644 index 00000000000..300966f75ba --- /dev/null +++ b/idea/testData/copyPaste/conversion/RedundantTypeCast2.to.kt @@ -0,0 +1,3 @@ +fun foo(o: Any) { + somethingElse(o as String/* we should not remove this cast because it's not in pasted range*/) +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/JavaToKotlinCopyPasteConversionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/JavaToKotlinCopyPasteConversionTestGenerated.java index 13c2f0d38d6..ae940b15cbc 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/JavaToKotlinCopyPasteConversionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/conversion/copy/JavaToKotlinCopyPasteConversionTestGenerated.java @@ -102,6 +102,18 @@ public class JavaToKotlinCopyPasteConversionTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("RedundantTypeCast.java") + public void testRedundantTypeCast() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/RedundantTypeCast.java"); + doTest(fileName); + } + + @TestMetadata("RedundantTypeCast2.java") + public void testRedundantTypeCast2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/RedundantTypeCast2.java"); + doTest(fileName); + } + @TestMetadata("SampleBlock.java") public void testSampleBlock() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/conversion/SampleBlock.java"); diff --git a/j2k/src/org/jetbrains/kotlin/j2k/AfterConversionPass.kt b/j2k/src/org/jetbrains/kotlin/j2k/AfterConversionPass.kt index d00eb41d901..10cbc50b672 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/AfterConversionPass.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/AfterConversionPass.kt @@ -16,26 +16,33 @@ package org.jetbrains.kotlin.j2k -import org.jetbrains.kotlin.psi.JetPsiFactory import com.intellij.openapi.project.Project +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.diagnostics.Diagnostic -import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.psi.JetSimpleNameExpression -import org.jetbrains.kotlin.psi.JetUnaryExpression -import org.jetbrains.kotlin.psi.JetProperty +import org.jetbrains.kotlin.psi.JetFile -class AfterConversionPass(val project: Project, val postProcessor: PostProcessor) { - public fun run(kotlinCode: String): String { - //TODO: it's more correct to analyze all converted files together - val kotlinFile = JetPsiFactory(project).createAnalyzableFile( - "fileForAfterConversionPass.kt", kotlinCode, postProcessor.contextToAnalyzeIn - ) - val bindingContext = postProcessor.analyzeFile(kotlinFile) +public class AfterConversionPass(val project: Project, val postProcessor: PostProcessor) { + public fun run(kotlinFile: JetFile, range: TextRange?) { + val bindingContext = postProcessor.analyzeFile(kotlinFile, range) - val fixes = bindingContext.getDiagnostics().map { - val fix = postProcessor.fixForProblem(it) - if (fix != null) it.getPsiElement() to fix else null - }.filterNotNull() + fun fixForProblem(diagnostic: Diagnostic): (() -> Unit)? { + val psiElement = diagnostic.getPsiElement() + if (range != null && psiElement.getTextRange() !in range) return null + return postProcessor.fixForProblem(diagnostic) + } + + val fixes = bindingContext.getDiagnostics() + .map { + val fix = fixForProblem(it) + if (fix != null) Pair(it.getPsiElement(), fix) else null + } + .filterNotNull() + + val document = kotlinFile.getViewProvider().getDocument()!! + val rangeMarker = if (range != null) document.createRangeMarker(range.getStartOffset(), range.getEndOffset()) else null + rangeMarker?.setGreedyToLeft(true) + rangeMarker?.setGreedyToRight(true) for ((psiElement, fix) in fixes) { if (psiElement.isValid()) { @@ -43,8 +50,6 @@ class AfterConversionPass(val project: Project, val postProcessor: PostProcessor } } - postProcessor.doAdditionalProcessing(kotlinFile) - - return kotlinFile.getText()!! + postProcessor.doAdditionalProcessing(kotlinFile, rangeMarker) } } diff --git a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt index 3506ee1f196..a3ac304764e 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt @@ -38,7 +38,6 @@ class Converter private( val inConversionScope: (PsiElement) -> Boolean, val referenceSearcher: ReferenceSearcher, val resolverForConverter: ResolverForConverter, - private val postProcessor: PostProcessor?, private val commonState: Converter.CommonState, private val personalState: Converter.PersonalState ) { @@ -60,16 +59,16 @@ class Converter private( companion object { public fun create(elementToConvert: PsiElement, settings: ConverterSettings, inConversionScope: (PsiElement) -> Boolean, - referenceSearcher: ReferenceSearcher, resolverForConverter: ResolverForConverter, postProcessor: PostProcessor?, + referenceSearcher: ReferenceSearcher, resolverForConverter: ResolverForConverter, usageProcessingsCollector: (UsageProcessing) -> Unit): Converter { - return Converter(elementToConvert, settings, inConversionScope, referenceSearcher, resolverForConverter, postProcessor, CommonState(usageProcessingsCollector), PersonalState(null)) + return Converter(elementToConvert, settings, inConversionScope, referenceSearcher, resolverForConverter, CommonState(usageProcessingsCollector), PersonalState(null)) } } public fun withSpecialContext(context: PsiElement): Converter = withState(PersonalState(context)) private fun withState(state: PersonalState): Converter - = Converter(elementToConvert, settings, inConversionScope, referenceSearcher, resolverForConverter, postProcessor, commonState, state) + = Converter(elementToConvert, settings, inConversionScope, referenceSearcher, resolverForConverter, commonState, state) private fun createDefaultCodeConverter() = CodeConverter(this, DefaultExpressionConverter(), DefaultStatementConverter(), null) diff --git a/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt index c142327223d..fc7560fa43f 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt @@ -17,14 +17,15 @@ package org.jetbrains.kotlin.j2k import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.editor.RangeMarker import com.intellij.openapi.progress.EmptyProgressIndicator import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager 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.PsiJavaFile import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.j2k.ast.Element @@ -36,8 +37,7 @@ import java.util.ArrayList import java.util.HashMap public trait PostProcessor { - public val contextToAnalyzeIn: PsiElement - public fun analyzeFile(file: JetFile): BindingContext + public fun analyzeFile(file: JetFile, range: TextRange?): BindingContext public open fun fixForProblem(problem: Diagnostic): (() -> Unit)? { val psiElement = problem.getPsiElement() @@ -51,7 +51,7 @@ public trait PostProcessor { Errors.VAL_REASSIGNMENT -> { -> val property = (psiElement as? JetSimpleNameExpression)?.getReference()?.resolve() as? JetProperty if (property != null && !property.isVar()) { - val factory = JetPsiFactory(contextToAnalyzeIn.getProject()) + val factory = JetPsiFactory(psiElement.getProject()) property.getValOrVarNode().getPsi()!!.replace(factory.createVarNode().getPsi()!!) } } @@ -60,21 +60,27 @@ public trait PostProcessor { } } - public fun doAdditionalProcessing(file: JetFile) + public fun doAdditionalProcessing(file: JetFile, rangeMarker: RangeMarker?) } public class JavaToKotlinConverter(private val project: Project, private val settings: ConverterSettings, private val referenceSearcher: ReferenceSearcher, - private val resolverForConverter: ResolverForConverter) { + private val resolverForConverter: ResolverForConverter, + private val postProcessor: PostProcessor?) { private val LOG = Logger.getInstance("#org.jetbrains.kotlin.j2k.JavaToKotlinConverter") + public data class InputElement( + val element: PsiElement, + val postProcessingContext: PsiElement? + ) + public fun elementsToKotlin( - psiElementsAndProcessors: List>, + inputElements: List, progress: ProgressIndicator = EmptyProgressIndicator() ): List { try { - val elementCount = psiElementsAndProcessors.size() + val elementCount = inputElements.size() val intermediateResults = ArrayList<((Map) -> String)?>(elementCount) val usageProcessings = HashMap() val usageProcessingCollector: (UsageProcessing) -> Unit = { usageProcessing -> @@ -94,12 +100,12 @@ public class JavaToKotlinConverter(private val project: Project, { progress.setText("$progressText ($fileCountText) - pass $pass of 3") - val filesCount = psiElementsAndProcessors.indices + val filesCount = inputElements.indices for (i in filesCount) { progress.checkCanceled() progress.setFraction(fraction + passFraction * i / elementCount) - val psiFile = psiElementsAndProcessors[i].first as? PsiFile + val psiFile = inputElements[i].element as? PsiFile if (psiFile != null) { progress.setText2(psiFile.getVirtualFile().getPresentableUrl()) } @@ -114,13 +120,12 @@ public class JavaToKotlinConverter(private val project: Project, } processFilesWithProgress(0.25) { i -> - val psiElement = psiElementsAndProcessors[i].first - val postProcessor = psiElementsAndProcessors[i].second + val psiElement = inputElements[i].element fun inConversionScope(element: PsiElement) - = psiElementsAndProcessors.any { it.first.isAncestor(element, strict = false) } + = inputElements.any { it.element.isAncestor(element, strict = false) } - val converter = Converter.create(psiElement, settings, ::inConversionScope, referenceSearcher, resolverForConverter, postProcessor, usageProcessingCollector) + val converter = Converter.create(psiElement, settings, ::inConversionScope, referenceSearcher, resolverForConverter, usageProcessingCollector) val result = converter.convert() intermediateResults.add(result) } @@ -135,10 +140,11 @@ public class JavaToKotlinConverter(private val project: Project, val finalResults = ArrayList(elementCount) processFilesWithProgress(0.5) { i -> val result = results[i] - val postProcessor = psiElementsAndProcessors[i].second if (postProcessor != null) { try { - finalResults.add(AfterConversionPass(project, postProcessor).run(result)) + val kotlinFile = JetPsiFactory(project).createAnalyzableFile("dummy.kt", result, inputElements[i].postProcessingContext!!) + AfterConversionPass(project, postProcessor).run(kotlinFile, null) + finalResults.add(kotlinFile.getText()) } catch(e: ProcessCanceledException) { throw e @@ -159,7 +165,7 @@ public class JavaToKotlinConverter(private val project: Project, // if we got this exception then we need to turn element creation stack traces on to get better diagnostic Element.saveCreationStacktraces = true try { - return elementsToKotlin(psiElementsAndProcessors) + return elementsToKotlin(inputElements) } finally { Element.saveCreationStacktraces = false diff --git a/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinTranslator.kt b/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinTranslator.kt index 0d1e3d1d917..742ce63901d 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinTranslator.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinTranslator.kt @@ -134,8 +134,8 @@ public object JavaToKotlinTranslator { fun generateKotlinCode(javaCode: String): String { val file = createFile(javaCode) if (file is PsiJavaFile) { - val converter = JavaToKotlinConverter(file.getProject(), ConverterSettings.defaultSettings, EmptyReferenceSearcher, EmptyResolverForConverter) - return prettify(converter.elementsToKotlin(listOf(file to null))[0]) + val converter = JavaToKotlinConverter(file.getProject(), ConverterSettings.defaultSettings, EmptyReferenceSearcher, EmptyResolverForConverter, null) + return prettify(converter.elementsToKotlin(listOf(JavaToKotlinConverter.InputElement(file, null))).single()) } return "" } diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterMultiFileTest.kt b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterMultiFileTest.kt index 68fd4e3df0d..78b84d2a2f5 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterMultiFileTest.kt +++ b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterMultiFileTest.kt @@ -43,8 +43,10 @@ public abstract class AbstractJavaToKotlinConverterMultiFileTest : AbstractJavaT psiFiles.add(psiFile) } - val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaReferenceSearcher, IdeaResolverForConverter) - val results: List = converter.elementsToKotlin(psiFiles.map { it to J2kPostProcessor(it, formatCode = true) }) + val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, + IdeaReferenceSearcher, IdeaResolverForConverter, J2kPostProcessor(formatCode = true)) + val inputElements = psiFiles.map { JavaToKotlinConverter.InputElement(it, it) } + val results: List = converter.elementsToKotlin(inputElements) fun expectedFile(i: Int) = File(javaFiles[i].getPath().replace(".java", ".kt")) diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterSingleFileTest.kt b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterSingleFileTest.kt index 3d03e6f76f1..f8a9616c187 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterSingleFileTest.kt +++ b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterSingleFileTest.kt @@ -106,8 +106,10 @@ public abstract class AbstractJavaToKotlinConverterSingleFileTest : AbstractJava private fun fileToKotlin(text: String, settings: ConverterSettings, project: Project): String { val file = createJavaFile(text) - val converter = JavaToKotlinConverter(project, settings, IdeaReferenceSearcher, IdeaResolverForConverter) - return converter.elementsToKotlin(listOf(file to J2kPostProcessor(file, formatCode = false)))[0] + val converter = JavaToKotlinConverter(project, settings, + IdeaReferenceSearcher, IdeaResolverForConverter, J2kPostProcessor(formatCode = false)) + val inputElements = listOf(JavaToKotlinConverter.InputElement(file, file)) + return converter.elementsToKotlin(inputElements).single() } private fun methodToKotlin(text: String, settings: ConverterSettings, project: Project): String {