J2K SourceNavigationHelper: convert to Kotlin and prettify

This commit is contained in:
Pavel V. Talanov
2017-03-18 19:50:19 +03:00
parent aa74ccf163
commit 4dee108afe
@@ -14,501 +14,354 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.decompiler.navigation; package org.jetbrains.kotlin.idea.decompiler.navigation
import com.google.common.collect.Lists; import com.intellij.openapi.diagnostic.Logger
import com.google.common.collect.Sets; import com.intellij.openapi.module.Module
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbService
import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project
import com.intellij.openapi.project.DumbService; import com.intellij.openapi.roots.OrderEntry
import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.psi.JavaPsiFacade
import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.psi.PsiClass
import com.intellij.openapi.util.Condition; import com.intellij.psi.search.EverythingGlobalScope
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.stubs.StringStubIndexExtension
import com.intellij.psi.PsiClass; import com.intellij.util.containers.ContainerUtil
import com.intellij.psi.PsiElement; import gnu.trove.THashSet
import com.intellij.psi.PsiFile; import org.jetbrains.annotations.TestOnly
import com.intellij.psi.search.EverythingGlobalScope; import org.jetbrains.kotlin.asJava.toLightClass
import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import com.intellij.psi.stubs.StringStubIndexExtension; import org.jetbrains.kotlin.context.ContextForNewModule
import com.intellij.util.containers.ContainerUtil; import org.jetbrains.kotlin.context.ProjectContext
import gnu.trove.THashSet; import org.jetbrains.kotlin.descriptors.CallableDescriptor
import kotlin.collections.CollectionsKt; import org.jetbrains.kotlin.frontend.di.createLazyResolveSession
import kotlin.jvm.functions.Function1; import org.jetbrains.kotlin.idea.decompiler.navigation.MemberMatching.*
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.idea.stubindex.*
import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.annotations.TestOnly; import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.builtins.DefaultBuiltIns; import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.context.ContextKt; import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.context.MutableModuleContext; import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.psi.debugText.getDebugText
import org.jetbrains.kotlin.frontend.di.InjectionKt; import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
import org.jetbrains.kotlin.idea.stubindex.*;
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer;
import org.jetbrains.kotlin.resolve.lazy.ResolveSession;
import java.util.Collection; object SourceNavigationHelper {
import java.util.Collections; private val LOG = Logger.getInstance(SourceNavigationHelper::class.java)
import java.util.List;
import java.util.Set;
import static org.jetbrains.kotlin.asJava.LightClassUtilsKt.toLightClass; enum class NavigationKind {
import static org.jetbrains.kotlin.idea.decompiler.navigation.MemberMatching.*;
public class SourceNavigationHelper {
private static final Logger LOG = Logger.getInstance(SourceNavigationHelper.class);
public enum NavigationKind {
CLASS_FILES_TO_SOURCES, CLASS_FILES_TO_SOURCES,
SOURCES_TO_CLASS_FILES SOURCES_TO_CLASS_FILES
} }
private static boolean forceResolve = false; private var forceResolve = false
private SourceNavigationHelper() { @TestOnly
fun setForceResolve(forceResolve: Boolean) {
SourceNavigationHelper.forceResolve = forceResolve
} }
@NotNull private fun createLibraryOrSourcesScope(
private static GlobalSearchScope createLibraryOrSourcesScope( declaration: KtNamedDeclaration,
@NotNull KtNamedDeclaration declaration, navigationKind: NavigationKind
@NotNull NavigationKind navigationKind ): GlobalSearchScope {
) { val containingFile = declaration.containingKtFile
KtFile containingFile = declaration.getContainingKtFile(); containingFile.virtualFile ?: return GlobalSearchScope.EMPTY_SCOPE
VirtualFile libraryFile = containingFile.getVirtualFile();
if (libraryFile == null) return GlobalSearchScope.EMPTY_SCOPE;
boolean includeLibrarySources = navigationKind == NavigationKind.CLASS_FILES_TO_SOURCES; val includeLibrarySources = navigationKind == NavigationKind.CLASS_FILES_TO_SOURCES
if (ProjectRootsUtil.isInContent(declaration, false, includeLibrarySources, !includeLibrarySources, true)) { if (ProjectRootsUtil.isInContent(declaration, false, includeLibrarySources, !includeLibrarySources, true)) {
return GlobalSearchScope.EMPTY_SCOPE; return GlobalSearchScope.EMPTY_SCOPE
} }
Project project = declaration.getProject(); val project = declaration.project
return includeLibrarySources return if (includeLibrarySources)
? KotlinSourceFilterScope.librarySources(new EverythingGlobalScope(project), project) KotlinSourceFilterScope.librarySources(EverythingGlobalScope(project), project)
: KotlinSourceFilterScope.libraryClassFiles(new EverythingGlobalScope(project), project); else
KotlinSourceFilterScope.libraryClassFiles(EverythingGlobalScope(project), project)
} }
private static List<KtFile> getContainingFiles(@NotNull Iterable<KtNamedDeclaration> declarations) { private fun haveRenamesInImports(files: Collection<KtFile>) = files.any { it.importDirectives.any { it.aliasName != null } }
Set<KtFile> result = Sets.newHashSet();
for (KtNamedDeclaration declaration : declarations) {
PsiFile containingFile = declaration.getContainingFile();
if (containingFile instanceof KtFile) {
result.add((KtFile) containingFile);
}
}
return Lists.newArrayList(result);
}
private static boolean haveRenamesInImports(@NotNull List<KtFile> files) { private fun findSpecialProperty(memberName: Name, containingClass: KtClass): KtNamedDeclaration? {
for (KtFile file : files) {
for (KtImportDirective importDirective : file.getImportDirectives()) {
if (importDirective.getAliasName() != null) {
return true;
}
}
}
return false;
}
@Nullable
private static KtNamedDeclaration findSpecialProperty(@NotNull Name memberName, @NotNull KtClass containingClass) {
// property constructor parameters // property constructor parameters
List<KtParameter> constructorParameters = containingClass.getPrimaryConstructorParameters(); val constructorParameters = containingClass.primaryConstructorParameters
for (KtParameter constructorParameter : constructorParameters) { for (constructorParameter in constructorParameters) {
if (memberName.equals(constructorParameter.getNameAsName()) && constructorParameter.hasValOrVar()) { if (memberName == constructorParameter.nameAsName && constructorParameter.hasValOrVar()) {
return constructorParameter; return constructorParameter
} }
} }
// enum entries // enum entries
if (containingClass.hasModifier(KtTokens.ENUM_KEYWORD)) { if (containingClass.hasModifier(KtTokens.ENUM_KEYWORD)) {
for (KtEnumEntry enumEntry : ContainerUtil.findAll(containingClass.getDeclarations(), KtEnumEntry.class)) { for (enumEntry in ContainerUtil.findAll<KtDeclaration, KtEnumEntry>(containingClass.declarations, KtEnumEntry::class.java)) {
if (memberName.equals(enumEntry.getNameAsName())) { if (memberName == enumEntry.nameAsName) {
return enumEntry; return enumEntry
} }
} }
} }
return null; return null
} }
@Nullable private fun convertPropertyOrFunction(
private static KtNamedDeclaration convertPropertyOrFunction( declaration: KtNamedDeclaration,
@NotNull KtNamedDeclaration declaration, navigationKind: NavigationKind
@NotNull NavigationKind navigationKind ): KtNamedDeclaration? {
) { if (declaration is KtPrimaryConstructor) {
if (declaration instanceof KtPrimaryConstructor) { val sourceClassOrObject = findClassOrObject(declaration.getContainingClassOrObject(), navigationKind)
KtClassOrObject sourceClassOrObject = return sourceClassOrObject?.primaryConstructor ?: sourceClassOrObject
findClassOrObject(((KtPrimaryConstructor) declaration).getContainingClassOrObject(), navigationKind);
KtPrimaryConstructor primaryConstructor = sourceClassOrObject != null ? sourceClassOrObject.getPrimaryConstructor() : null;
return primaryConstructor != null ? primaryConstructor : sourceClassOrObject;
} }
String memberNameAsString = declaration.getName(); val memberNameAsString = declaration.name
if (memberNameAsString == null) { if (memberNameAsString == null) {
LOG.debug("JetSourceNavigationHelper.convertPropertyOrFunction(): null name for declaration " + declaration); LOG.debug("Declaration with null name:" + declaration.getDebugText())
return null; return null
} }
Name memberName = Name.identifier(memberNameAsString); val memberName = Name.identifier(memberNameAsString)
PsiElement decompiledContainer = declaration.getParent(); val decompiledContainer = declaration.parent
Collection<KtNamedDeclaration> candidates; var candidates: Collection<KtNamedDeclaration>
if (decompiledContainer instanceof KtFile) { when (decompiledContainer) {
candidates = getInitialTopLevelCandidates(declaration, navigationKind); is KtFile -> candidates = getInitialTopLevelCandidates(declaration, navigationKind)
} is KtClassBody -> {
else if (decompiledContainer instanceof KtClassBody) { val decompiledClassOrObject = decompiledContainer.getParent() as KtClassOrObject
KtClassOrObject decompiledClassOrObject = (KtClassOrObject) decompiledContainer.getParent(); val sourceClassOrObject = findClassOrObject(decompiledClassOrObject, navigationKind)
KtClassOrObject sourceClassOrObject = findClassOrObject(decompiledClassOrObject, navigationKind);
//noinspection unchecked candidates = sourceClassOrObject?.let {
candidates = sourceClassOrObject == null getInitialMemberCandidates(sourceClassOrObject, memberName, declaration::class.java)
? Collections.<KtNamedDeclaration>emptyList() }.orEmpty()
: getInitialMemberCandidates(sourceClassOrObject, memberName,
(Class<KtNamedDeclaration>) declaration.getClass());
if (candidates.isEmpty()) { if (candidates.isEmpty()) {
if (declaration instanceof KtProperty && sourceClassOrObject instanceof KtClass) { if (declaration is KtProperty && sourceClassOrObject is KtClass) {
return findSpecialProperty(memberName, (KtClass) sourceClassOrObject); return findSpecialProperty(memberName, sourceClassOrObject)
}
} }
} }
} else -> throw IllegalStateException("Unexpected container of " +
else { (if (navigationKind == NavigationKind.CLASS_FILES_TO_SOURCES) "decompiled" else "source") +
throw new IllegalStateException("Unexpected container of " + " declaration: " +
(navigationKind == NavigationKind.CLASS_FILES_TO_SOURCES ? "decompiled" : "source") + decompiledContainer::class.java.simpleName)
" declaration: " +
decompiledContainer.getClass().getSimpleName());
} }
if (candidates.isEmpty()) { if (candidates.isEmpty()) {
return null; return null
} }
candidates = filterByOrderEntries(declaration, candidates); candidates = filterByOrderEntries(declaration, candidates)
if (!forceResolve) { if (!forceResolve) {
candidates = filterByReceiverPresenceAndParametersCount(declaration, candidates); candidates = candidates.filter { sameReceiverPresenceAndParametersCount(it, declaration) }
if (candidates.size() <= 1) { if (candidates.size <= 1) {
return candidates.isEmpty() ? null : candidates.iterator().next(); return candidates.firstOrNull()
} }
if (!haveRenamesInImports(getContainingFiles(candidates))) { if (!haveRenamesInImports(candidates.getContainingFiles())) {
candidates = filterByReceiverAndParameterTypes(declaration, candidates); candidates = candidates.filter { receiverAndParametersShortTypesMatch(it, declaration) }
if (candidates.size() <= 1) {
return candidates.isEmpty() ? null : candidates.iterator().next(); if (candidates.size <= 1) {
return candidates.firstOrNull()
} }
} }
} }
KotlinCodeAnalyzer analyzer = createAnalyzer(candidates, declaration.getProject()); val analyzer = createAnalyzer(candidates, declaration.project)
for (KtNamedDeclaration candidate : candidates) { for (candidate in candidates) {
//noinspection unchecked val candidateDescriptor = analyzer.resolveToDescriptor(candidate) as CallableDescriptor
CallableDescriptor candidateDescriptor = (CallableDescriptor) analyzer.resolveToDescriptor(candidate);
if (receiversMatch(declaration, candidateDescriptor) if (receiversMatch(declaration, candidateDescriptor)
&& valueParametersTypesMatch(declaration, candidateDescriptor) && valueParametersTypesMatch(declaration, candidateDescriptor)
&& typeParametersMatch((KtTypeParameterListOwner) declaration, candidateDescriptor.getTypeParameters())) { && typeParametersMatch(declaration as KtTypeParameterListOwner, candidateDescriptor.typeParameters)) {
return candidate; return candidate
} }
} }
return null; return null
} }
@NotNull private fun createAnalyzer(
private static KotlinCodeAnalyzer createAnalyzer( candidates: Collection<KtNamedDeclaration>,
@NotNull Collection<KtNamedDeclaration> candidates, project: Project
@NotNull Project project ): KotlinCodeAnalyzer {
) { val context = ContextForNewModule(
MutableModuleContext context = ContextKt.ContextForNewModule( ProjectContext(project), Name.special("<library module>"), DefaultBuiltIns.Instance, null
ContextKt.ProjectContext(project), Name.special("<library module>"), DefaultBuiltIns.getInstance(), null )
); context.setDependencies(context.module, context.module.builtIns.builtInsModule)
context.setDependencies(context.getModule(), context.getModule().getBuiltIns().getBuiltInsModule()); val resolveSession = createLazyResolveSession(context, candidates.getContainingFiles())
ResolveSession resolveSession = InjectionKt.createLazyResolveSession(context, getContainingFiles(candidates)); context.initializeModuleContents(resolveSession.packageFragmentProvider)
context.initializeModuleContents(resolveSession.getPackageFragmentProvider()); return resolveSession
return resolveSession;
} }
@Nullable private fun <T : KtNamedDeclaration> findFirstMatchingInIndex(
private static <T extends KtNamedDeclaration> T findFirstMatchingInIndex( entity: T,
@NotNull T entity, navigationKind: NavigationKind,
@NotNull NavigationKind navigationKind, index: StringStubIndexExtension<T>
@NotNull StringStubIndexExtension<T> index ): T? {
) { val classFqName = entity.fqName!!
FqName classFqName = entity.getFqName();
assert classFqName != null;
GlobalSearchScope librarySourcesScope = createLibraryOrSourcesScope(entity, navigationKind); val librarySourcesScope = createLibraryOrSourcesScope(entity, navigationKind)
if (librarySourcesScope == GlobalSearchScope.EMPTY_SCOPE) { // .getProject() == null for EMPTY_SCOPE, and this breaks code if (librarySourcesScope === GlobalSearchScope.EMPTY_SCOPE) { // .getProject() == null for EMPTY_SCOPE, and this breaks code
return null; return null
} }
Collection<T> classes = index.get(classFqName.asString(), entity.getProject(), librarySourcesScope); return index.get(classFqName.asString(), entity.project, librarySourcesScope).firstOrNull()
if (classes.isEmpty()) { }
return null;
private fun findClassOrObject(decompiledClassOrObject: KtClassOrObject, navigationKind: NavigationKind): KtClassOrObject? {
return findFirstMatchingInIndex<KtClassOrObject>(decompiledClassOrObject, navigationKind, KotlinFullClassNameIndex.getInstance())
}
private fun getInitialTopLevelCandidates(
declaration: KtNamedDeclaration,
navigationKind: NavigationKind
): Collection<KtNamedDeclaration> {
val librarySourcesScope = createLibraryOrSourcesScope(declaration, navigationKind)
if (librarySourcesScope === GlobalSearchScope.EMPTY_SCOPE) { // .getProject() == null for EMPTY_SCOPE, and this breaks code
return emptyList()
} }
return classes.iterator().next(); // if there are more than one with this FQ, find first of them
val index = getIndexForTopLevelPropertyOrFunction(declaration)
return index.get(declaration.fqName!!.asString(), declaration.project, librarySourcesScope)
} }
@Nullable private fun getIndexForTopLevelPropertyOrFunction(
private static KtClassOrObject findClassOrObject( decompiledDeclaration: KtNamedDeclaration
@NotNull KtClassOrObject decompiledClassOrObject, ): StringStubIndexExtension<out KtNamedDeclaration> {
@NotNull NavigationKind navigationKind when (decompiledDeclaration) {
) { is KtNamedFunction -> return KotlinTopLevelFunctionFqnNameIndex.getInstance()
return findFirstMatchingInIndex(decompiledClassOrObject, navigationKind, KotlinFullClassNameIndex.getInstance()); is KtProperty -> return KotlinTopLevelPropertyFqnNameIndex.getInstance()
} else -> throw IllegalArgumentException("Neither function nor declaration: " + decompiledDeclaration::class.java.name)
@NotNull
private static Collection<KtNamedDeclaration> getInitialTopLevelCandidates(
@NotNull KtNamedDeclaration declaration,
@NotNull NavigationKind navigationKind
) {
FqName memberFqName = declaration.getFqName();
assert memberFqName != null;
GlobalSearchScope librarySourcesScope = createLibraryOrSourcesScope(declaration, navigationKind);
if (librarySourcesScope == GlobalSearchScope.EMPTY_SCOPE) { // .getProject() == null for EMPTY_SCOPE, and this breaks code
return Collections.emptyList();
} }
//noinspection unchecked
StringStubIndexExtension<KtNamedDeclaration> index =
(StringStubIndexExtension<KtNamedDeclaration>) getIndexForTopLevelPropertyOrFunction(declaration);
return index.get(memberFqName.asString(), declaration.getProject(), librarySourcesScope);
} }
private static StringStubIndexExtension<? extends KtNamedDeclaration> getIndexForTopLevelPropertyOrFunction( private fun getInitialMemberCandidates(
@NotNull KtNamedDeclaration decompiledDeclaration sourceClassOrObject: KtClassOrObject,
) { name: Name,
if (decompiledDeclaration instanceof KtNamedFunction) { declarationClass: Class<out KtNamedDeclaration>
return KotlinTopLevelFunctionFqnNameIndex.getInstance(); ) = sourceClassOrObject.declarations.filterIsInstance(declarationClass).filter {
declaration ->
name == declaration.nameAsSafeName
}
private fun filterByOrderEntries(
declaration: KtNamedDeclaration,
candidates: Collection<KtNamedDeclaration>
): List<KtNamedDeclaration> {
val fileIndex = ProjectRootManager.getInstance(declaration.project).fileIndex
val orderEntries = fileIndex.getOrderEntriesForFile(declaration.containingFile.virtualFile)
return candidates.filter {
val candidateOrderEntries = fileIndex.getOrderEntriesForFile(it.containingFile.virtualFile)
ContainerUtil.intersects<OrderEntry>(orderEntries, candidateOrderEntries)
} }
if (decompiledDeclaration instanceof KtProperty) {
return KotlinTopLevelPropertyFqnNameIndex.getInstance();
}
throw new IllegalArgumentException("Neither function nor declaration: " + decompiledDeclaration.getClass().getName());
} }
@NotNull fun getOriginalPsiClassOrCreateLightClass(classOrObject: KtClassOrObject): PsiClass? {
private static List<KtNamedDeclaration> getInitialMemberCandidates( val fqName = classOrObject.fqName
@NotNull KtClassOrObject sourceClassOrObject,
@NotNull final Name name,
@NotNull Class<KtNamedDeclaration> declarationClass
) {
List<KtNamedDeclaration> allByClass = ContainerUtil.findAll(sourceClassOrObject.getDeclarations(), declarationClass);
return ContainerUtil.filter(allByClass, new Condition<KtNamedDeclaration>() {
@Override
public boolean value(KtNamedDeclaration declaration) {
return name.equals(declaration.getNameAsSafeName());
}
});
}
@NotNull
private static List<KtNamedDeclaration> filterByOrderEntries(
@NotNull KtNamedDeclaration declaration,
@NotNull Collection<KtNamedDeclaration> candidates
) {
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(declaration.getProject()).getFileIndex();
final List<OrderEntry> orderEntries = fileIndex.getOrderEntriesForFile(declaration.getContainingFile().getVirtualFile());
return CollectionsKt.filter(
candidates,
new Function1<KtNamedDeclaration, Boolean>() {
@Override
public Boolean invoke(KtNamedDeclaration candidate) {
List<OrderEntry> candidateOrderEntries = fileIndex.getOrderEntriesForFile(candidate.getContainingFile().getVirtualFile());
return ContainerUtil.intersects(orderEntries, candidateOrderEntries);
}
}
);
}
@NotNull
private static List<KtNamedDeclaration> filterByReceiverPresenceAndParametersCount(
final @NotNull KtNamedDeclaration decompiledDeclaration,
@NotNull Collection<KtNamedDeclaration> candidates
) {
return ContainerUtil.filter(candidates, new Condition<KtNamedDeclaration>() {
@Override
public boolean value(KtNamedDeclaration candidate) {
return sameReceiverPresenceAndParametersCount(candidate, decompiledDeclaration);
}
});
}
@NotNull
private static List<KtNamedDeclaration> filterByReceiverAndParameterTypes(
final @NotNull KtNamedDeclaration decompiledDeclaration,
@NotNull Collection<KtNamedDeclaration> candidates
) {
return ContainerUtil.filter(candidates, new Condition<KtNamedDeclaration>() {
@Override
public boolean value(KtNamedDeclaration candidate) {
return receiverAndParametersShortTypesMatch(candidate, decompiledDeclaration);
}
});
}
@TestOnly
public static void setForceResolve(boolean forceResolve) {
SourceNavigationHelper.forceResolve = forceResolve;
}
@Nullable
public static PsiClass getOriginalPsiClassOrCreateLightClass(@NotNull KtClassOrObject classOrObject) {
FqName fqName = classOrObject.getFqName();
if (fqName != null) { if (fqName != null) {
ClassId javaClassId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(fqName.toUnsafe()); val javaClassId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(fqName.toUnsafe())
if (javaClassId != null) { if (javaClassId != null) {
return JavaPsiFacade.getInstance(classOrObject.getProject()).findClass( return JavaPsiFacade.getInstance(classOrObject.project).findClass(
javaClassId.asSingleFqName().asString(), javaClassId.asSingleFqName().asString(),
GlobalSearchScope.allScope(classOrObject.getProject()) GlobalSearchScope.allScope(classOrObject.project)
); )
} }
} }
return toLightClass(classOrObject); return classOrObject.toLightClass()
} }
@Nullable fun getOriginalClass(classOrObject: KtClassOrObject): PsiClass? {
public static PsiClass getOriginalClass(@NotNull KtClassOrObject classOrObject) {
// Copied from JavaPsiImplementationHelperImpl:getOriginalClass() // Copied from JavaPsiImplementationHelperImpl:getOriginalClass()
FqName fqName = classOrObject.getFqName(); val fqName = classOrObject.fqName ?: return null
if (fqName == null) {
return null;
}
KtFile file = classOrObject.getContainingKtFile(); val file = classOrObject.containingKtFile
VirtualFile vFile = file.getVirtualFile(); val vFile = file.virtualFile
Project project = file.getProject(); val project = file.project
final ProjectFileIndex idx = ProjectRootManager.getInstance(project).getFileIndex(); val idx = ProjectRootManager.getInstance(project).fileIndex
if (vFile == null || !idx.isInLibrarySource(vFile)) return null; if (vFile == null || !idx.isInLibrarySource(vFile)) return null
final Set<OrderEntry> orderEntries = new THashSet<OrderEntry>(idx.getOrderEntriesForFile(vFile)); val orderEntries = THashSet<OrderEntry>(idx.getOrderEntriesForFile(vFile))
return JavaPsiFacade.getInstance(project).findClass(fqName.asString(), new GlobalSearchScope(project) { return JavaPsiFacade.getInstance(project).findClass(fqName.asString(), object : GlobalSearchScope(project) {
@Override override fun compare(file1: VirtualFile, file2: VirtualFile): Int {
public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) { return 0
return 0;
} }
@Override override fun contains(file: VirtualFile): Boolean {
public boolean contains(@NotNull VirtualFile file) { val entries = idx.getOrderEntriesForFile(file)
List<OrderEntry> entries = idx.getOrderEntriesForFile(file); for (entry in entries) {
for (OrderEntry entry : entries) { if (orderEntries.contains(entry)) return true
if (orderEntries.contains(entry)) return true;
} }
return false; return false
} }
@Override override fun isSearchInModuleContent(aModule: Module): Boolean {
public boolean isSearchInModuleContent(@NotNull Module aModule) { return false
return false;
} }
@Override override fun isSearchInLibraries(): Boolean {
public boolean isSearchInLibraries() { return true
return true;
} }
}); })
} }
@NotNull fun getNavigationElement(declaration: KtDeclaration) = navigateToDeclaration(declaration, NavigationKind.CLASS_FILES_TO_SOURCES)
public static KtDeclaration getNavigationElement(@NotNull KtDeclaration declaration) {
return navigateToDeclaration(declaration, NavigationKind.CLASS_FILES_TO_SOURCES); fun getOriginalElement(declaration: KtDeclaration) = navigateToDeclaration(declaration, NavigationKind.SOURCES_TO_CLASS_FILES)
private fun navigateToDeclaration(
from: KtDeclaration,
navigationKind: NavigationKind
): KtDeclaration {
if (DumbService.isDumb(from.project)) return from
when (navigationKind) {
SourceNavigationHelper.NavigationKind.CLASS_FILES_TO_SOURCES -> if (!from.containingKtFile.isCompiled) return from
SourceNavigationHelper.NavigationKind.SOURCES_TO_CLASS_FILES -> {
if (from.containingKtFile.isCompiled) return from
if (!ProjectRootsUtil.isInContent(from, false, true, false, true)) return from
if (KtPsiUtil.isLocal(from)) return from
}
}
return from.accept(SourceAndDecompiledConversionVisitor(navigationKind), Unit) ?: from
} }
@NotNull private class SourceAndDecompiledConversionVisitor(private val navigationKind: NavigationKind) : KtVisitor<KtDeclaration?, Unit>() {
public static KtDeclaration getOriginalElement(@NotNull KtDeclaration declaration) {
return navigateToDeclaration(declaration, NavigationKind.SOURCES_TO_CLASS_FILES);
}
@NotNull override fun visitNamedFunction(function: KtNamedFunction, data: Unit) = convertPropertyOrFunction(function, navigationKind)
private static KtDeclaration navigateToDeclaration(
@NotNull KtDeclaration from,
@NotNull NavigationKind navigationKind
) {
if (DumbService.isDumb(from.getProject())) return from;
switch (navigationKind) { override fun visitProperty(property: KtProperty, data: Unit) = convertPropertyOrFunction(property, navigationKind)
case CLASS_FILES_TO_SOURCES:
if (!from.getContainingKtFile().isCompiled()) return from; override fun visitObjectDeclaration(declaration: KtObjectDeclaration, data: Unit) = findClassOrObject(declaration, navigationKind)
break;
case SOURCES_TO_CLASS_FILES: override fun visitClass(klass: KtClass, data: Unit) = findClassOrObject(klass, navigationKind)
if (from.getContainingKtFile().isCompiled()) return from;
if (!ProjectRootsUtil.isInContent(from, false, true, false, true)) return from; override fun visitTypeAlias(typeAlias: KtTypeAlias, data: Unit)
if (KtPsiUtil.isLocal(from)) return from; = findFirstMatchingInIndex(typeAlias, navigationKind, KotlinTopLevelTypeAliasFqNameIndex.getInstance())
break;
override fun visitParameter(parameter: KtParameter, data: Unit): KtDeclaration? {
val callableDeclaration = parameter.parent.parent as KtCallableDeclaration
val parameters = callableDeclaration.valueParameters
val index = parameters.indexOf(parameter)
val sourceCallable = callableDeclaration.accept(this, Unit) as? KtCallableDeclaration ?: return null
val sourceParameters = sourceCallable.valueParameters
if (sourceParameters.size != parameters.size) return null
return sourceParameters.get(index)
} }
KtDeclaration result = from.accept(new SourceAndDecompiledConversionVisitor(navigationKind), null); override fun visitPrimaryConstructor(constructor: KtPrimaryConstructor, data: Unit)
return result != null ? result : from; = convertPropertyOrFunction(constructor, navigationKind)
}
private static class SourceAndDecompiledConversionVisitor extends KtVisitor<KtDeclaration, Void> { override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor, data: Unit)
private final NavigationKind navigationKind; = convertPropertyOrFunction(constructor, navigationKind)
public SourceAndDecompiledConversionVisitor(@NotNull NavigationKind navigationKind) {
this.navigationKind = navigationKind;
}
@Override
public KtDeclaration visitNamedFunction(@NotNull KtNamedFunction function, Void data) {
return convertPropertyOrFunction(function, navigationKind);
}
@Override
public KtDeclaration visitProperty(@NotNull KtProperty property, Void data) {
return convertPropertyOrFunction(property, navigationKind);
}
@Override
public KtDeclaration visitObjectDeclaration(@NotNull KtObjectDeclaration declaration, Void data) {
return findClassOrObject(declaration, navigationKind);
}
@Override
public KtDeclaration visitClass(@NotNull KtClass klass, Void data) {
return findClassOrObject(klass, navigationKind);
}
@Override
public KtDeclaration visitTypeAlias(@NotNull KtTypeAlias typeAlias, Void data) {
return findFirstMatchingInIndex(typeAlias, navigationKind, KotlinTopLevelTypeAliasFqNameIndex.getInstance());
}
@Override
public KtDeclaration visitParameter(@NotNull KtParameter parameter, Void data) {
KtCallableDeclaration callableDeclaration = (KtCallableDeclaration) parameter.getParent().getParent();
List<KtParameter> parameters = callableDeclaration.getValueParameters();
int index = parameters.indexOf(parameter);
KtCallableDeclaration sourceCallable = (KtCallableDeclaration) callableDeclaration.accept(this, null);
if (sourceCallable == null) return null;
List<KtParameter> sourceParameters = sourceCallable.getValueParameters();
if (sourceParameters.size() != parameters.size()) return null;
return sourceParameters.get(index);
}
@Override
public KtDeclaration visitPrimaryConstructor(@NotNull KtPrimaryConstructor constructor, Void data) {
return convertPropertyOrFunction(constructor, navigationKind);
}
@Override
public KtDeclaration visitSecondaryConstructor(@NotNull KtSecondaryConstructor constructor, Void data) {
return convertPropertyOrFunction(constructor, navigationKind);
}
} }
} }
private fun Collection<KtNamedDeclaration>.getContainingFiles(): Collection<KtFile> = mapNotNullTo(LinkedHashSet()) {
it.containingFile as? KtFile
}