Java to Kotlin converter: no usage search and body analysis for members out of conversion scope
This commit is contained in:
@@ -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<PsiFile> selectedJavaFiles = getAllJavaFiles(virtualFiles, project);
|
||||
final List<PsiJavaFile> 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<PsiFile> selectedJavaFiles) {
|
||||
Converter converter = new Converter(project, ConverterSettings.defaultSettings);
|
||||
private static Converter prepareConverter(@NotNull Project project, @NotNull List<PsiJavaFile> selectedJavaFiles) {
|
||||
Converter converter = new Converter(project, ConverterSettings.defaultSettings, new FilesConversionScope(selectedJavaFiles));
|
||||
converter.clearClassIdentifiers();
|
||||
for (PsiFile f : selectedJavaFiles) {
|
||||
if (f.getFileType() instanceof JavaFileType) {
|
||||
|
||||
@@ -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<PsiFile> getAllJavaFiles(@NotNull VirtualFile[] vFiles, Project project) {
|
||||
/*package*/ static List<PsiJavaFile> getAllJavaFiles(@NotNull VirtualFile[] vFiles, Project project) {
|
||||
Set<VirtualFile> filesSet = allVirtualFiles(vFiles);
|
||||
PsiManager manager = PsiManager.getInstance(project);
|
||||
List<PsiFile> res = new ArrayList<PsiFile>();
|
||||
List<PsiJavaFile> res = new ArrayList<PsiJavaFile>();
|
||||
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<VirtualFile> convertFiles(final Converter converter, List<PsiFile> allJavaFilesNear) {
|
||||
static List<VirtualFile> convertFiles(final Converter converter, List<PsiJavaFile> allJavaFilesNear) {
|
||||
final List<VirtualFile> result = new LinkedList<VirtualFile>();
|
||||
for (final PsiFile f : allJavaFilesNear) {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@@ -108,7 +107,7 @@ public class JavaToKotlinActionUtil {
|
||||
return result;
|
||||
}
|
||||
|
||||
static void deleteFiles(List<PsiFile> allJavaFilesNear) {
|
||||
static void deleteFiles(List<PsiJavaFile> 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<PsiFile> psiFiles) {
|
||||
static void renameFiles(@NotNull List<PsiJavaFile> psiFiles) {
|
||||
for (final PsiFile f : psiFiles) {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
|
||||
+11
-17
@@ -48,44 +48,38 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor<TextBl
|
||||
}
|
||||
|
||||
public override fun collectTransferableData(file: PsiFile, editor: Editor, startOffsets: IntArray, endOffsets: IntArray): TextBlockTransferableData? {
|
||||
if (!(file is PsiJavaFile)) {
|
||||
return null
|
||||
}
|
||||
if (file !is PsiJavaFile) return null
|
||||
|
||||
val lightFile = PsiFileFactory.getInstance(file.getProject())!!.createFileFromText(file.getText()!!, file)
|
||||
return CopiedCode(lightFile as? PsiJavaFile, startOffsets, endOffsets)
|
||||
}
|
||||
|
||||
public override fun processTransferableData(project: Project, editor: Editor, bounds: RangeMarker, caretOffset: Int, indented: Ref<Boolean>, 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<TextBl
|
||||
for (i in startOffsets.indices) {
|
||||
val startOffset = startOffsets[i]
|
||||
val endOffset = endOffsets[i]
|
||||
result.append(convertRangeToKotlin(code.getFile()!!, TextRange(startOffset, endOffset), converter))
|
||||
result.append(convertRangeToKotlin(file, TextRange(startOffset, endOffset), converter))
|
||||
}
|
||||
return StringUtil.convertLineSeparators(result.toString())
|
||||
}
|
||||
|
||||
@@ -25,9 +25,17 @@ import org.jetbrains.jet.lang.types.expressions.OperatorConventions.*
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
|
||||
public class Converter(val project: Project, val settings: ConverterSettings) {
|
||||
public trait ConversionScope {
|
||||
public fun contains(element: PsiElement): Boolean
|
||||
}
|
||||
|
||||
private val typeConverter = TypeConverter(settings)
|
||||
public class FilesConversionScope(val files: Collection<PsiJavaFile>) : 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<String> = HashSet()
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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<PsiElement, Nullability>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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? {
|
||||
|
||||
Reference in New Issue
Block a user