Shiraji's Blog

Difference Between Generating Java and Generating Kotlin for Developing IntelliJ Plugin

Motivation

I am a maintainer of PermissionsDispatcher Plugin which generates Java and Kotlin for PermissionsDispatcher Since Kotlin is getting famous for Android developers, I thought IntelliJ plugins, which generate Android code, should support both Java and Kotlin. (By the way, Kotlin 1.0.2 now supports Android lint! This definitely will lead more developers use Kotlin!)

However, while I was developing this plugin, I found really hard to generate both Java and Kotlin code.

So, this blog post describes what are the differences between generating Java and generating Kotlin using IntelliJ plugin.

Environment

Before start taking about the differences, the followings are the environment for this blog post.

getClasses for PsiJavaFile vs KtFile

1
e.getData(CommonDataKeys.PSI_FILE)

returns PsiJavaFile or KtFile. Both of them implements PsiClassOwner which means both of them has the method PsiClass[] getClasses().

This method is useful for PsiJavaFile, it lets access classes of the file. The plugin can read/write contents of the classes.

For KtFile, I expected the same. I want to read/write code of classes. Yes, you can read .class file. It is not classes inside .kt file. So, even though, it has methods add, addBefore or addAfter, KtFile#classes#add throw an exception says the plugin won’t be able to write contents to .class file!

If you want to get objects for generating code of Kotlin, then use KtFile#getDeclarations.

PsiFactory

This could be because I could not find the best way to generate code…but PsiFactory of Java and Kotlin is different

1
val psiElementFactory: PsiElementFactory = JavaPsiFacade.getElementFactory(project)
1
val psiFactory: KtPsiFactory = KtPsiFactory(project)

Generating annotations

PsiMethod has modifierList

1
method.modifierList.addAnnotation("Foo")`

On the other hand, KtNamedFunction has addAnnotationEntry

1
function.addAnnotationEntry(psiFactory.createAnnotationEntry("@Foo"))

To insert new line after annotation, you need to add new line manually.

1
2
val entry = function.addAnnotationEntry(psiFactory.createAnnotationEntry("@Foo"))
entry.add(psiFactory.createNewLine())

Generating methods

PsiClass is easy to add method. Use createMethodFromText and add to PsiClass

1
2
3
4
val methodTemplate = """void foo() {
}""".trimMargin()
val method = JavaPsiFacade.getElementFactory(project).createMethodFromText(methodTemplate, psiClass)
psiClass.add(method)

For kotlin, it’s almost the same.

1
2
3
4
val psiFactory = KtPsiFactory(project)
val function = psiFactory.createFunction("""void foo() {
}""".trimMargin())
ktClass.getBody()!!.addBefore(function, ktClass.getBody()!!.rBrace)