From 324040a02f0a8762593652e527a5774ff40ed1a7 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 10 Jun 2014 14:34:37 +0400 Subject: [PATCH] Java to Kotlin converter: no usage search and body analysis for members out of conversion scope --- .../plugin/actions/JavaToKotlinAction.java | 8 ++- .../actions/JavaToKotlinActionUtil.java | 15 +++-- .../copy/ConvertJavaCopyPastePostProcessor.kt | 28 ++++----- j2k/src/org/jetbrains/jet/j2k/Converter.kt | 12 +++- .../jet/j2k/JavaToKotlinTranslator.kt | 7 +-- .../org/jetbrains/jet/j2k/TypeConverter.kt | 7 ++- .../test/AbstractJavaToKotlinConverterTest.kt | 57 +++++++++---------- 7 files changed, 67 insertions(+), 67 deletions(-) diff --git a/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinAction.java b/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinAction.java index e434f58b207..59779950ffb 100644 --- a/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinAction.java +++ b/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinAction.java @@ -26,9 +26,11 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiJavaFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.j2k.Converter; import org.jetbrains.jet.j2k.ConverterSettings; +import org.jetbrains.jet.j2k.FilesConversionScope; import java.util.List; @@ -43,7 +45,7 @@ public class JavaToKotlinAction extends AnAction { assert virtualFiles != null; final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); assert project != null; - final List selectedJavaFiles = getAllJavaFiles(virtualFiles, project); + final List selectedJavaFiles = getAllJavaFiles(virtualFiles, project); if (selectedJavaFiles.isEmpty()) { return; } @@ -77,8 +79,8 @@ public class JavaToKotlinAction extends AnAction { } @NotNull - private static Converter prepareConverter(@NotNull Project project, @NotNull List selectedJavaFiles) { - Converter converter = new Converter(project, ConverterSettings.defaultSettings); + private static Converter prepareConverter(@NotNull Project project, @NotNull List selectedJavaFiles) { + Converter converter = new Converter(project, ConverterSettings.defaultSettings, new FilesConversionScope(selectedJavaFiles)); converter.clearClassIdentifiers(); for (PsiFile f : selectedJavaFiles) { if (f.getFileType() instanceof JavaFileType) { diff --git a/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinActionUtil.java b/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinActionUtil.java index 9f3c3c2729d..103a5462508 100644 --- a/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinActionUtil.java +++ b/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinActionUtil.java @@ -16,7 +16,6 @@ package org.jetbrains.jet.plugin.actions; -import com.intellij.ide.highlighter.JavaFileType; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ex.MessagesEx; @@ -53,14 +52,14 @@ public class JavaToKotlinActionUtil { } @NotNull - /*package*/ static List getAllJavaFiles(@NotNull VirtualFile[] vFiles, Project project) { + /*package*/ static List getAllJavaFiles(@NotNull VirtualFile[] vFiles, Project project) { Set filesSet = allVirtualFiles(vFiles); PsiManager manager = PsiManager.getInstance(project); - List res = new ArrayList(); + List res = new ArrayList(); for (VirtualFile file : filesSet) { PsiFile psiFile = manager.findFile(file); - if (psiFile != null && psiFile.getFileType() instanceof JavaFileType) { - res.add(psiFile); + if (psiFile != null && psiFile instanceof PsiJavaFile) { + res.add((PsiJavaFile)psiFile); } } return res; @@ -92,7 +91,7 @@ public class JavaToKotlinActionUtil { } @NotNull - static List convertFiles(final Converter converter, List allJavaFilesNear) { + static List convertFiles(final Converter converter, List allJavaFilesNear) { final List result = new LinkedList(); for (final PsiFile f : allJavaFilesNear) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @@ -108,7 +107,7 @@ public class JavaToKotlinActionUtil { return result; } - static void deleteFiles(List allJavaFilesNear) { + static void deleteFiles(List allJavaFilesNear) { for (final PsiFile f : allJavaFilesNear) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override @@ -150,7 +149,7 @@ public class JavaToKotlinActionUtil { return null; } - static void renameFiles(@NotNull List psiFiles) { + static void renameFiles(@NotNull List psiFiles) { for (final PsiFile f : psiFiles) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override diff --git a/idea/src/org/jetbrains/jet/plugin/conversion/copy/ConvertJavaCopyPastePostProcessor.kt b/idea/src/org/jetbrains/jet/plugin/conversion/copy/ConvertJavaCopyPastePostProcessor.kt index 4ad242f2c06..143f9b75d50 100644 --- a/idea/src/org/jetbrains/jet/plugin/conversion/copy/ConvertJavaCopyPastePostProcessor.kt +++ b/idea/src/org/jetbrains/jet/plugin/conversion/copy/ConvertJavaCopyPastePostProcessor.kt @@ -48,44 +48,38 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor, value: TextBlockTransferableData) { - if (value !is CopiedCode) - return + if (value !is CopiedCode) return + val sourceFile = value.getFile() ?: return - if (value.getFile() == null) - return - - val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()) - if (file !is JetFile) - return + val targetFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()) + if (targetFile !is JetFile) return val jetEditorOptions = JetEditorOptions.getInstance()!! val needConvert = jetEditorOptions.isEnableJavaToKotlinConversion() && (jetEditorOptions.isDonTShowConversionDialog() || okFromDialog(project)) if (needConvert) { - val text = convertCopiedCodeToKotlin(value, file.getProject()) + val text = convertCopiedCodeToKotlin(value, sourceFile) if (text.isNotEmpty()) { ApplicationManager.getApplication()!!.runWriteAction { val startOffset = bounds.getStartOffset() editor.getDocument().replaceString(bounds.getStartOffset(), bounds.getEndOffset(), text) val endOffsetAfterCopy = startOffset + text.length() editor.getCaretModel().moveToOffset(endOffsetAfterCopy) - CodeStyleManager.getInstance(project)!!.reformatText(file, startOffset, endOffsetAfterCopy) - PsiDocumentManager.getInstance(file.getProject()).commitDocument(editor.getDocument()) + CodeStyleManager.getInstance(project)!!.reformatText(targetFile, startOffset, endOffsetAfterCopy) + PsiDocumentManager.getInstance(targetFile.getProject()).commitDocument(editor.getDocument()) } } } } - private fun convertCopiedCodeToKotlin(code: CopiedCode, project: Project): String { - val converter = Converter(project, ConverterSettings.defaultSettings) + private fun convertCopiedCodeToKotlin(code: CopiedCode, file: PsiJavaFile): String { + val converter = Converter(file.getProject(), ConverterSettings.defaultSettings, FilesConversionScope(listOf(file))) val startOffsets = code.getStartOffsets() val endOffsets = code.getEndOffsets() assert(startOffsets.size == endOffsets.size) { "Must have the same size" } @@ -93,7 +87,7 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor) : ConversionScope { + override fun contains(element: PsiElement) = files.any { element.getContainingFile() == it } +} + +public class Converter(val project: Project, val settings: ConverterSettings, val conversionScope: ConversionScope) { + + private val typeConverter = TypeConverter(settings, conversionScope) private var classIdentifiersSet: MutableSet = HashSet() diff --git a/j2k/src/org/jetbrains/jet/j2k/JavaToKotlinTranslator.kt b/j2k/src/org/jetbrains/jet/j2k/JavaToKotlinTranslator.kt index 4715ee6f6df..a15c8c21678 100644 --- a/j2k/src/org/jetbrains/jet/j2k/JavaToKotlinTranslator.kt +++ b/j2k/src/org/jetbrains/jet/j2k/JavaToKotlinTranslator.kt @@ -19,7 +19,6 @@ package org.jetbrains.jet.j2k import com.intellij.core.JavaCoreApplicationEnvironment import com.intellij.core.JavaCoreProjectEnvironment import com.intellij.lang.java.JavaLanguage -import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer import com.intellij.psi.PsiElement @@ -40,8 +39,8 @@ public object JavaToKotlinTranslator { return PsiFileFactory.getInstance(javaCoreEnvironment?.getProject())?.createFileFromText("test.java", JavaLanguage.INSTANCE, text) } - fun createFile(project: Project, text: String): PsiFile? { - return PsiFileFactory.getInstance(project)?.createFileFromText("test.java", JavaLanguage.INSTANCE, text) + fun createFile(project: Project, text: String): PsiJavaFile { + return PsiFileFactory.getInstance(project)?.createFileFromText("test.java", JavaLanguage.INSTANCE, text) as PsiJavaFile } fun setUpJavaCoreEnvironment(): JavaCoreProjectEnvironment { @@ -98,7 +97,7 @@ public object JavaToKotlinTranslator { fun generateKotlinCode(javaCode: String): String { val file = createFile(javaCode) if (file is PsiJavaFile) { - val converter = Converter(file.getProject(), ConverterSettings.defaultSettings) + val converter = Converter(file.getProject(), ConverterSettings.defaultSettings, FilesConversionScope(listOf(file))) setClassIdentifiers(converter, file) return prettify(converter.convertFile(file).toKotlin()) } diff --git a/j2k/src/org/jetbrains/jet/j2k/TypeConverter.kt b/j2k/src/org/jetbrains/jet/j2k/TypeConverter.kt index 3b379e08f97..1fa6f166021 100644 --- a/j2k/src/org/jetbrains/jet/j2k/TypeConverter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/TypeConverter.kt @@ -22,7 +22,7 @@ import com.intellij.psi.* import org.jetbrains.jet.j2k.visitors.TypeVisitor import java.util.HashMap -class TypeConverter(val settings: ConverterSettings) { +class TypeConverter(val settings: ConverterSettings, val conversionScope: ConversionScope) { private val nullabilityCache = HashMap() public fun convertType(`type`: PsiType?, nullability: Nullability = Nullability.Default): Type { @@ -68,6 +68,8 @@ class TypeConverter(val settings: ConverterSettings) { } } + if (!conversionScope.contains(variable)) return nullability // do not analyze usages of fields out of our conversion scope + if (nullability == Nullability.Default) { val scope = searchScope(variable) if (scope != null) { @@ -114,6 +116,8 @@ class TypeConverter(val settings: ConverterSettings) { private fun methodNullabilityNoCache(method: PsiMethod): Nullability { var nullability = method.nullabilityFromAnnotations() + if (!conversionScope.contains(method)) return nullability // do not analyze body and usages of methods out of our conversion scope + if (nullability == Nullability.Default) { method.getBody()?.accept(object: JavaRecursiveElementVisitor() { override fun visitReturnStatement(statement: PsiReturnStatement) { @@ -196,7 +200,6 @@ class TypeConverter(val settings: ConverterSettings) { return when(this) { is PsiLocalVariable -> !hasWriteAccesses(getContainingMethod()) is PsiField -> if (hasModifierProperty(PsiModifier.PRIVATE)) !hasWriteAccesses(getContainingClass()) else false - is PsiParameter -> true else -> false } } diff --git a/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt b/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt index b988bff7328..d924e6e6f6f 100644 --- a/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt +++ b/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt @@ -33,6 +33,7 @@ import org.jetbrains.jet.JetTestUtils import com.intellij.openapi.project.Project import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.application.ApplicationManager +import org.jetbrains.jet.j2k.FilesConversionScope abstract class AbstractJavaToKotlinConverterTest() : LightIdeaTestCase() { val testHeaderPattern = Pattern.compile("//(element|expression|statement|method|class|file|comp)\n") @@ -74,15 +75,14 @@ abstract class AbstractJavaToKotlinConverterTest() : LightIdeaTestCase() { else -> throw IllegalArgumentException("Unknown option: $name") } } - val converter = Converter(project, settings) val rawConverted = when (prefix) { - "element" -> elementToKotlin(converter, javaCode) - "expression" -> expressionToKotlin(converter, javaCode) - "statement" -> statementToKotlin(converter, javaCode) - "method" -> methodToKotlin(converter, javaCode) - "class" -> fileToKotlin(converter, javaCode) - "file" -> fileToKotlin(converter, javaCode) + "element" -> elementToKotlin(javaCode, settings, project) + "expression" -> expressionToKotlin(javaCode, settings, project) + "statement" -> statementToKotlin(javaCode, settings, project) + "method" -> methodToKotlin(javaCode, settings, project) + "class" -> fileToKotlin(javaCode, settings, project) + "file" -> fileToKotlin(javaCode, settings, project) else -> throw IllegalStateException("Specify what is it: file, class, method, statement or expression " + "using the first line of test data file") } @@ -114,42 +114,37 @@ abstract class AbstractJavaToKotlinConverterTest() : LightIdeaTestCase() { reformattedText } - private fun elementToKotlin(converter: Converter, text: String): String { - val fileWithText = JavaToKotlinTranslator.createFile(converter.project, text)!! + private fun elementToKotlin(text: String, settings: ConverterSettings, project: Project): String { + val fileWithText = JavaToKotlinTranslator.createFile(project, text) + val converter = Converter(project, settings, FilesConversionScope(listOf(fileWithText))) val element = fileWithText.getFirstChild()!! return converter.elementToKotlin(element) } - private fun fileToKotlin(converter: Converter, text: String): String { - return generateKotlinCode(converter, JavaToKotlinTranslator.createFile(converter.project, text)) + private fun fileToKotlin(text: String, settings: ConverterSettings, project: Project): String { + val file = JavaToKotlinTranslator.createFile(project, text) + val converter = Converter(project, settings, FilesConversionScope(listOf(file))) + return generateKotlinCode(converter, file) } - private fun methodToKotlin(converter: Converter, text: String?): String { - var result = fileToKotlin(converter, "final class C {" + text + "}").replaceAll("class C\\(\\) \\{", "") - result = result.substring(0, (result.lastIndexOf("}"))).trim() - return result + private fun methodToKotlin(text: String, settings: ConverterSettings, project: Project): String { + val result = fileToKotlin("final class C {" + text + "}", settings, project).replaceAll("class C\\(\\) \\{", "") + return result.substring(0, (result.lastIndexOf("}"))).trim() } - private fun statementToKotlin(converter: Converter, text: String?): String { - var result = methodToKotlin(converter, "void main() {" + text + "}") - val pos = result.lastIndexOf("}") - result = result.substring(0, pos).replaceFirst("fun main\\(\\) \\{", "").trim() - return result + private fun statementToKotlin(text: String, settings: ConverterSettings, project: Project): String { + val result = methodToKotlin("void main() {" + text + "}", settings, project) + return result.substring(0, result.lastIndexOf("}")).replaceFirst("fun main\\(\\) \\{", "").trim() } - private fun expressionToKotlin(converter: Converter, code: String?): String { - var result = statementToKotlin(converter, "final Object o =" + code + "}") - result = result.replaceFirst("val o : Any\\? =", "").replaceFirst("val o : Any = ", "").replaceFirst("val o = ", "").trim() - return result + private fun expressionToKotlin(code: String, settings: ConverterSettings, project: Project): String { + val result = statementToKotlin("final Object o =" + code + "}", settings, project) + return result.replaceFirst("val o : Any\\? =", "").replaceFirst("val o : Any = ", "").replaceFirst("val o = ", "").trim() } - private fun generateKotlinCode(converter: Converter, file: PsiFile?): String { - if (file is PsiJavaFile) { - JavaToKotlinTranslator.setClassIdentifiers(converter, file) - return converter.elementToKotlin(file) - } - - return "" + private fun generateKotlinCode(converter: Converter, file: PsiJavaFile): String { + JavaToKotlinTranslator.setClassIdentifiers(converter, file) + return converter.elementToKotlin(file) } override fun getProjectJDK(): Sdk? {