Skip to content

Instantly share code, notes, and snippets.

@Takhion
Created May 11, 2019 20:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Takhion/9cb28c6cab589936e5fbca891afb241e to your computer and use it in GitHub Desktop.
Save Takhion/9cb28c6cab589936e5fbca891afb241e to your computer and use it in GitHub Desktop.
Convert Java files to Kotlin without the IDE
plugins {
kotlin("jvm") version "1.3.31"
id("org.jetbrains.intellij") version "0.4.8"
}
repositories {
mavenCentral()
jcenter()
}
dependencies {
implementation(kotlin("stdlib"))
}
intellij {
version = "191.5109.14" // field "Since:" from https://plugins.jetbrains.com/plugin/6954-kotlin/update/61407
setPlugins("Kotlin") // dependency on the Intellij Kotlin plugin (needed for 'j2k.jar')
downloadSources = false
}
configurations {
runtimeClasspath {
extendsFrom(idea.get())
}
}
// adapted from https://github.com/JetBrains/kotlin/blob/0103c0d2fd8c9ae9e7f41898064407a6278c5831/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterForWebDemoTest.kt
package me.eugeniomarletti
import com.intellij.codeInsight.ContainerProvider
import com.intellij.codeInsight.NullabilityAnnotationInfo
import com.intellij.codeInsight.NullableNotNullManager
import com.intellij.codeInsight.runner.JavaMainMethodProvider
import com.intellij.core.CoreApplicationEnvironment
import com.intellij.core.JavaCoreApplicationEnvironment
import com.intellij.core.JavaCoreProjectEnvironment
import com.intellij.lang.MetaLanguage
import com.intellij.lang.jvm.facade.JvmElementProvider
import com.intellij.openapi.Disposable
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.extensions.ExtensionsArea
import com.intellij.openapi.fileTypes.FileTypeExtensionPoint
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.FileContextProvider
import com.intellij.psi.JavaModuleSystem
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementFinder
import com.intellij.psi.PsiModifierListOwner
import com.intellij.psi.augment.PsiAugmentProvider
import com.intellij.psi.augment.TypeAnnotationModifier
import com.intellij.psi.compiled.ClassFileDecompilers
import com.intellij.psi.impl.JavaClassSupersImpl
import com.intellij.psi.impl.PsiTreeChangePreprocessor
import com.intellij.psi.meta.MetaDataContributor
import com.intellij.psi.stubs.BinaryFileStubBuilders
import com.intellij.psi.util.JavaClassSupers
import org.jetbrains.kotlin.j2k.JavaToKotlinTranslator
import org.jetbrains.kotlin.j2k.translateToKotlin
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import java.net.URLClassLoader
fun convertJavaFilesToKotlin(javaSources: List<File>): Map<File, String> =
when {
javaSources.isEmpty() -> emptyMap()
else -> {
val disposable = Disposer.newDisposable()
try {
val javaCoreEnvironment: JavaCoreProjectEnvironment = setUpJavaCoreEnvironment(disposable)
javaSources.associateWith { javaSource ->
val fileContents = FileUtil.loadFile(javaSource, true)
translateToKotlin(fileContents, javaCoreEnvironment.project)
}
}
finally {
disposable.let(Disposer::dispose)
}
}
}
private fun setUpJavaCoreEnvironment(disposable: Disposable): JavaCoreProjectEnvironment {
Extensions.cleanRootArea(disposable)
registerExtensionPoints(Extensions.getRootArea())
val applicationEnvironment = JavaCoreApplicationEnvironment(disposable)
val javaCoreEnvironment = object : JavaCoreProjectEnvironment(disposable, applicationEnvironment) {
override fun preregisterServices() {
val projectArea = Extensions.getArea(project)
@Suppress("DEPRECATION")
CoreApplicationEnvironment.registerExtensionPoint(projectArea, PsiTreeChangePreprocessor.EP_NAME, PsiTreeChangePreprocessor::class.java)
CoreApplicationEnvironment.registerExtensionPoint(projectArea, PsiElementFinder.EP_NAME, PsiElementFinder::class.java)
CoreApplicationEnvironment.registerExtensionPoint(projectArea, JvmElementProvider.EP_NAME, JvmElementProvider::class.java)
}
}
javaCoreEnvironment.project.registerService(NullableNotNullManager::class.java, object : NullableNotNullManager(javaCoreEnvironment.project) {
override fun isNullable(owner: PsiModifierListOwner, checkBases: Boolean) = !isNotNull(owner, checkBases)
override fun isNotNull(owner: PsiModifierListOwner, checkBases: Boolean) = true
override fun hasHardcodedContracts(element: PsiElement): Boolean = false
override fun getNullables() = emptyList<String>()
override fun setNullables(vararg p0: String) = Unit
override fun getNotNulls() = emptyList<String>()
override fun setNotNulls(vararg p0: String) = Unit
override fun getDefaultNullable() = ""
override fun setDefaultNullable(defaultNullable: String) = Unit
override fun getDefaultNotNull() = ""
override fun setDefaultNotNull(p0: String) = Unit
override fun setInstrumentedNotNulls(p0: List<String>) = Unit
override fun getInstrumentedNotNulls() = emptyList<String>()
override fun isJsr305Default(psiAnnotation: PsiAnnotation, p1: Array<PsiAnnotation.TargetType>): NullabilityAnnotationInfo? = null
})
applicationEnvironment.application.registerService(JavaClassSupers::class.java, JavaClassSupersImpl::class.java)
PathUtil.getJdkClassesRootsFromCurrentJre().forEach(javaCoreEnvironment::addJarToClassPath)
findAnnotations()?.takeIf { it.exists() }?.let(javaCoreEnvironment::addJarToClassPath)
return javaCoreEnvironment
}
private fun registerExtensionPoints(area: ExtensionsArea) {
CoreApplicationEnvironment.registerExtensionPoint(area, BinaryFileStubBuilders.EP_NAME, FileTypeExtensionPoint::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, FileContextProvider.EP_NAME, FileContextProvider::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, MetaDataContributor.EP_NAME, MetaDataContributor::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, PsiAugmentProvider.EP_NAME, PsiAugmentProvider::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, JavaMainMethodProvider.EP_NAME, JavaMainMethodProvider::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, ContainerProvider.EP_NAME, ContainerProvider::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, ClassFileDecompilers.EP_NAME, ClassFileDecompilers.Decompiler::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, TypeAnnotationModifier.EP_NAME, TypeAnnotationModifier::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, MetaLanguage.EP_NAME, MetaLanguage::class.java)
@Suppress("UnstableApiUsage")
CoreApplicationEnvironment.registerExtensionPoint(area, JavaModuleSystem.EP_NAME, JavaModuleSystem::class.java)
}
private fun findAnnotations(): File? =
JavaToKotlinTranslator::class.java
.classLoader
.let { generateSequence(it, ClassLoader::getParent) }
.filterIsInstance<URLClassLoader>()
.flatMap { it.urLs.asSequence() }
.filter { it.protocol == "file" }
.map { it.file }
.filter { it.endsWith("/annotations.jar") }
.map(::File)
.firstOrNull()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment