Refactor: restructure light class hierarchy for sources

KtLightClassBase
	| KtLightClassForSourceDeclaration
		| KtLightClassImpl (new!) // top level or inner/nested of top level class
		| KtLightClassForInterfaceDefaultImpls
		| KtLightClassForLocalDeclaration (new!)
			| KtLightClassForAnonymousDeclaration
				| KtLightClassForEnumEntry
This commit is contained in:
Pavel V. Talanov
2016-07-26 15:46:40 +03:00
parent 252235787d
commit 85fb16ef1b
6 changed files with 167 additions and 108 deletions
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
internal open class KtLightClassForAnonymousDeclaration(fqNameFunction: ((KtClassOrObject) -> FqName),
classOrObject: KtClassOrObject) :
KtLightClassForSourceDeclaration(fqNameFunction, classOrObject), PsiAnonymousClass {
KtLightClassForLocalDeclaration(fqNameFunction, classOrObject), PsiAnonymousClass {
private var cachedBaseType: SoftReference<PsiClassType>? = null
@@ -120,7 +120,7 @@ internal open class KtLightClassForAnonymousDeclaration(fqNameFunction: ((KtClas
override fun getTypeParameterList() = null
override fun isEnum() = false
override fun copy(): PsiElement = KtLightClassForAnonymousDeclaration({ classFqName }, classOrObject)
override fun copy(): PsiElement = KtLightClassForAnonymousDeclaration(classFqNameFunction, classOrObject)
companion object {
private val LOG = Logger.getInstance(KtLightClassForAnonymousDeclaration::class.java)
@@ -28,4 +28,6 @@ internal class KtLightClassForEnumEntry(
): KtLightClassForAnonymousDeclaration({ fqName }, enumEntry), PsiEnumConstantInitializer {
override fun getEnumConstant(): PsiEnumConstant = enumConstant
override fun copy() = KtLightClassForEnumEntry(classFqName, classOrObject as KtEnumEntry, enumConstant)
override fun getParent() = enumConstant
}
@@ -21,10 +21,11 @@ import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
open class KtLightClassForInterfaceDefaultImpls(
class KtLightClassForInterfaceDefaultImpls(
classFqName: FqName,
classOrObject: KtClassOrObject)
: KtLightClassForSourceDeclaration({ classFqName }, classOrObject){
override fun getParent() = containingClass
override fun copy(): PsiElement {
return KtLightClassForInterfaceDefaultImpls(classFqName, classOrObject.copy() as KtClassOrObject)
@@ -0,0 +1,112 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.asJava.classes
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.impl.light.LightClass
import com.intellij.psi.impl.light.LightMethod
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
open class KtLightClassForLocalDeclaration(
classFqNameFunction: (KtClassOrObject) -> FqName,
classOrObject: KtClassOrObject
) : KtLightClassForSourceDeclaration(classFqNameFunction, classOrObject) {
override fun copy(): PsiElement = KtLightClassForLocalDeclaration(classFqNameFunction, classOrObject.copy() as KtClassOrObject)
override fun getParent() = _parent
private val _parent: PsiElement? by lazy(LazyThreadSafetyMode.PUBLICATION, this::computeParent)
private fun computeParent(): PsiElement? {
fun getParentByPsiMethod(method: PsiMethod?, name: String?, forceMethodWrapping: Boolean): PsiElement? {
if (method == null || name == null) return null
var containingClass: PsiClass? = method.containingClass ?: return null
val currentFileName = classOrObject.containingFile.name
var createWrapper = forceMethodWrapping
// Use PsiClass wrapper instead of package light class to avoid names like "FooPackage" in Type Hierarchy and related views
if (containingClass is KtLightClassForFacade) {
containingClass = object : LightClass(containingClass as KtLightClassForFacade, KotlinLanguage.INSTANCE) {
override fun getName(): String? {
return currentFileName
}
}
createWrapper = true
}
if (createWrapper) {
return object : LightMethod(myManager, method, containingClass!!, KotlinLanguage.INSTANCE) {
override fun getParent(): PsiElement {
return getContainingClass()!!
}
override fun getName(): String {
return name
}
}
}
return method
}
var declaration: PsiElement? = KtPsiUtil.getTopmostParentOfTypes(
classOrObject,
KtNamedFunction::class.java,
KtConstructor::class.java,
KtProperty::class.java,
KtAnonymousInitializer::class.java,
KtParameter::class.java)
if (declaration is KtParameter) {
declaration = declaration.getStrictParentOfType<KtNamedDeclaration>()
}
if (declaration is KtFunction) {
return getParentByPsiMethod(LightClassUtil.getLightClassMethod(declaration), declaration.name, false)
}
// Represent the property as a fake method with the same name
if (declaration is KtProperty) {
return getParentByPsiMethod(LightClassUtil.getLightClassPropertyMethods(declaration).getter, declaration.name, true)
}
if (declaration is KtAnonymousInitializer) {
val parent = declaration.parent
val grandparent = parent.parent
if (parent is KtClassBody && grandparent is KtClassOrObject) {
return grandparent.toLightClass()
}
}
if (declaration is KtClass) {
return declaration.toLightClass()
}
return null
}
}
@@ -23,8 +23,6 @@ import com.intellij.openapi.util.Key
import com.intellij.psi.*
import com.intellij.psi.impl.DebugUtil
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.impl.light.LightClass
import com.intellij.psi.impl.light.LightMethod
import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.psi.search.SearchScope
import com.intellij.psi.stubs.IStubElementType
@@ -42,22 +40,20 @@ import org.jetbrains.kotlin.asJava.elements.KtLightModifierListWithExplicitModif
import org.jetbrains.kotlin.asJava.elements.KtLightPsiReferenceList
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import java.util.*
import javax.swing.Icon
open class KtLightClassForSourceDeclaration(
private val classFqNameFunction: ((KtClassOrObject) -> FqName),
abstract class KtLightClassForSourceDeclaration(
protected val classFqNameFunction: ((KtClassOrObject) -> FqName),
protected val classOrObject: KtClassOrObject)
: KtLightClassBase(classOrObject.manager), KtJavaMirrorMarker, StubBasedPsiElement<KotlinClassOrObjectStub<out KtClassOrObject>> {
private val lightIdentifier = KtLightIdentifier(this, classOrObject)
@@ -76,92 +72,12 @@ open class KtLightClassForSourceDeclaration(
KtLightPsiReferenceList(listDelegate, this)
}
private fun getLocalClassParent(): PsiElement? {
fun getParentByPsiMethod(method: PsiMethod?, name: String?, forceMethodWrapping: Boolean): PsiElement? {
if (method == null || name == null) return null
var containingClass: PsiClass? = method.containingClass ?: return null
val currentFileName = classOrObject.containingFile.name
var createWrapper = forceMethodWrapping
// Use PsiClass wrapper instead of package light class to avoid names like "FooPackage" in Type Hierarchy and related views
if (containingClass is KtLightClassForFacade) {
containingClass = object : LightClass(containingClass as KtLightClassForFacade, KotlinLanguage.INSTANCE) {
override fun getName(): String? {
return currentFileName
}
}
createWrapper = true
}
if (createWrapper) {
return object : LightMethod(myManager, method, containingClass!!, KotlinLanguage.INSTANCE) {
override fun getParent(): PsiElement {
return getContainingClass()!!
}
override fun getName(): String {
return name
}
}
}
return method
}
var declaration: PsiElement? = KtPsiUtil.getTopmostParentOfTypes(
classOrObject,
KtNamedFunction::class.java,
KtConstructor::class.java,
KtProperty::class.java,
KtAnonymousInitializer::class.java,
KtParameter::class.java)
if (declaration is KtParameter) {
declaration = declaration.getStrictParentOfType<KtNamedDeclaration>()
}
if (declaration is KtFunction) {
return getParentByPsiMethod(LightClassUtil.getLightClassMethod(declaration), declaration.name, false)
}
// Represent the property as a fake method with the same name
if (declaration is KtProperty) {
return getParentByPsiMethod(LightClassUtil.getLightClassPropertyMethods(declaration).getter, declaration.name, true)
}
if (declaration is KtAnonymousInitializer) {
val parent = declaration.parent
val grandparent = parent.parent
if (parent is KtClassBody && grandparent is KtClassOrObject) {
return grandparent.toLightClass()
}
}
if (declaration is KtClass) {
return declaration.toLightClass()
}
return null
}
private val _parent: PsiElement? by lazy(LazyThreadSafetyMode.PUBLICATION) {
if (classOrObject.isLocal())
getLocalClassParent()
else if (classOrObject.isTopLevel())
containingFile
else
containingClass
}
override val kotlinOrigin: KtClassOrObject = classOrObject
override fun getFqName(): FqName = classFqName
override fun copy(): PsiElement {
return KtLightClassForSourceDeclaration({ classFqName }, classOrObject.copy() as KtClassOrObject)
}
abstract override fun copy(): PsiElement
abstract override fun getParent(): PsiElement?
override val clsDelegate: PsiClass by lazy(LazyThreadSafetyMode.PUBLICATION) {
val javaFileStub = getJavaFileStub()
@@ -261,8 +177,6 @@ open class KtLightClassForSourceDeclaration(
return super.getContainingClass()
}
override fun getParent(): PsiElement? = _parent
private val _typeParameterList: PsiTypeParameterList by lazy(LazyThreadSafetyMode.PUBLICATION) {
LightClassUtil.buildLightTypeParameterList(this, classOrObject)
}
@@ -429,21 +343,27 @@ open class KtLightClassForSourceDeclaration(
fun create(classOrObject: KtClassOrObject): KtLightClassForSourceDeclaration? {
if (classOrObject is KtObjectDeclaration && classOrObject.isObjectLiteral()) {
if (classOrObject.containingFile.virtualFile == null) {
return null
}
if (classOrObject.containingFile.virtualFile == null) return null
val data = getLightClassDataExactly(classOrObject) ?: return null
return KtLightClassForAnonymousDeclaration(
{ predictFqName(it) ?: throw IllegalArgumentException("Failed to create fqname for anonymous class: " + classOrObject) },
{ data.jvmQualifiedName },
classOrObject)
}
if (classOrObject.isLocal()) {
if (classOrObject.containingFile.virtualFile == null) return null
val data = getLightClassDataExactly(classOrObject) ?: return null
return KtLightClassForLocalDeclaration({ data.jvmQualifiedName }, classOrObject)
}
if (isEnumEntryWithoutBody(classOrObject)) {
return null
}
val fqName = predictFqName(classOrObject) ?: return null
return KtLightClassForSourceDeclaration({ fqName }, classOrObject)
val fqName = classOrObject.fqName ?: return null
return KtLightClassImpl({ fqName }, classOrObject)
}
private fun isEnumEntryWithoutBody(classOrObject: KtClassOrObject): Boolean {
@@ -453,15 +373,6 @@ open class KtLightClassForSourceDeclaration(
return classOrObject.getBody()?.declarations?.isEmpty() ?: true
}
private fun predictFqName(classOrObject: KtClassOrObject): FqName? {
if (classOrObject.isLocal()) {
if (classOrObject.containingFile.virtualFile == null) return null
val data = getLightClassDataExactly(classOrObject)
return data?.jvmQualifiedName
}
return classOrObject.fqName
}
fun getLightClassData(classOrObject: KtClassOrObject): LightClassData {
return getLightClassCachedValue(classOrObject).value
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.asJava.classes
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
// light class for top level or (inner/nested of top level) source declarations
class KtLightClassImpl(
classFqNameFunction: (KtClassOrObject) -> FqName,
classOrObject: KtClassOrObject
) : KtLightClassForSourceDeclaration(classFqNameFunction, classOrObject) {
override fun getParent() = if (classOrObject.isTopLevel())
containingFile
else
containingClass
override fun copy() = KtLightClassImpl(classFqNameFunction, classOrObject.copy() as KtClassOrObject)
}