Uast: support internal class names in Java and method signatures in Java and Kotlin

This commit is contained in:
Yan Zhulanow
2016-03-22 19:44:22 +03:00
parent 5bc31112c1
commit 3dc091d92c
5 changed files with 111 additions and 3 deletions
@@ -15,7 +15,10 @@
*/
package org.jetbrains.uast.java
import com.intellij.ide.util.JavaAnonymousClassesHelper
import com.intellij.psi.*
import com.intellij.psi.util.ClassUtil
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.PsiTypesUtil
import org.jetbrains.uast.*
import org.jetbrains.uast.kinds.UastClassKind
@@ -59,7 +62,7 @@ class JavaUClass(
override val isAnonymous: Boolean
get() = psi is PsiAnonymousClass
override val internalName = null
override val internalName by lz { getInternalName(psi) }
override val superTypes by lz {
psi.extendsListTypes.map { JavaConverter.convert(it, this) } + psi.implementsListTypes.map { JavaConverter.convert(it, this) }
@@ -98,6 +101,61 @@ class JavaUClass(
return isSubClassOf(psi, name)
}
private companion object {
/* Primarily copied from IntellijLintUtils and ClassContext classes from the Android IDEA plugin */
private fun getInternalName(psiClass: PsiClass): String? {
if (psiClass is PsiAnonymousClass) {
val parent = PsiTreeUtil.getParentOfType(psiClass, PsiClass::class.java)
if (parent != null) {
val internalName = getInternalName(parent) ?: return null
return internalName + JavaAnonymousClassesHelper.getName(psiClass)
}
}
var sig = ClassUtil.getJVMClassName(psiClass)
if (sig == null) {
val qualifiedName = psiClass.qualifiedName
if (qualifiedName != null) {
return getInternalName(qualifiedName)
}
return null
}
else if (sig.indexOf('.') != -1) {
// Workaround -- ClassUtil doesn't treat this correctly!
// .replace('.', '/');
sig = getInternalName(sig)
}
return sig
}
private fun getInternalName(fqcn: String): String {
if (fqcn.indexOf('.') == -1) {
return fqcn
}
// If class name contains $, it's not an ambiguous inner class name.
if (fqcn.indexOf('$') != -1) {
return fqcn.replace('.', '/')
}
// Let's assume that components that start with Caps are class names.
val sb = StringBuilder(fqcn.length)
var prev: String? = null
for (part in fqcn.split('.')) {
if (prev != null && !prev.isEmpty()) {
if (Character.isUpperCase(prev[0])) {
sb.append('$')
}
else {
sb.append('/')
}
}
sb.append(part)
prev = part
}
return sb.toString()
}
}
}
private class JavaUAnonymousClassConstructor(
@@ -15,7 +15,10 @@
*/
package org.jetbrains.uast.java
import com.intellij.psi.PsiArrayType
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiType
import org.jetbrains.uast.*
import org.jetbrains.uast.psi.PsiElementBacked
@@ -53,10 +56,37 @@ class JavaUFunction(
override val visibility: UastVisibility
get() = psi.getVisibility()
override val body by lz { JavaConverter.convertOrEmpty(psi.body, this) }
override val bytecodeDescriptor by lz { getDescriptor(psi) }
override fun getSuperFunctions(context: UastContext): List<UFunction> {
return psi.findSuperMethods().map { context.convert(it) as? UFunction }.filterNotNull()
}
private companion object {
fun getDescriptor(psi: PsiMethod): String? {
val parameterTypes = psi.parameterList.parameters.map {
renderType(it.type) ?: return null
}
val returnType = renderType(psi.returnType) ?: return null
return parameterTypes.joinToString("", "(", ")") + returnType
}
fun renderType(type: PsiType?): String? = when (type) {
null -> null
PsiType.CHAR -> "C"
PsiType.DOUBLE -> "D"
PsiType.FLOAT -> "F"
PsiType.INT -> "I"
PsiType.LONG -> "J"
PsiType.SHORT -> "S"
PsiType.BOOLEAN -> "Z"
PsiType.VOID -> "V"
is PsiArrayType -> renderType(type.componentType)?.let { "[$it" }
is PsiClassType -> type.resolve()?.qualifiedName?.let { "L$it;" }
else -> null
}
}
}