Cleanup 192 patchset files (KTI-315)

This commit is contained in:
Yunir Salimzyanov
2020-08-13 19:00:06 +03:00
parent 73aa21aab6
commit 42da9e62db
126 changed files with 0 additions and 11671 deletions
@@ -1,420 +0,0 @@
/*
* Copyright 2000-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.caches
import com.intellij.ProjectTopics
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.rootManager
import com.intellij.openapi.roots.ModuleRootEvent
import com.intellij.openapi.roots.ModuleRootListener
import com.intellij.openapi.util.Key
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.newvfs.BulkFileListener
import com.intellij.openapi.vfs.newvfs.events.*
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.PsiManagerEx
import com.intellij.psi.impl.PsiTreeChangeEventImpl
import com.intellij.psi.impl.PsiTreeChangePreprocessor
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.caches.PerModulePackageCacheService.Companion.DEBUG_LOG_ENABLE_PerModulePackageCache
import org.jetbrains.kotlin.idea.caches.PerModulePackageCacheService.Companion.FULL_DROP_THRESHOLD
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
import org.jetbrains.kotlin.idea.caches.project.getModuleInfoByVirtualFile
import org.jetbrains.kotlin.idea.caches.project.getNullableModuleInfo
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
import org.jetbrains.kotlin.idea.util.getSourceRoot
import org.jetbrains.kotlin.idea.util.sourceRoot
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPackageDirective
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
class KotlinPackageContentModificationListener(private val project: Project) : Disposable {
val connection = project.messageBus.connect()
companion object {
val LOG = Logger.getInstance(this::class.java)
}
init {
connection.subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener {
override fun before(events: MutableList<out VFileEvent>) = onEvents(events, false)
override fun after(events: List<VFileEvent>) = onEvents(events, true)
private fun isRelevant(event: VFileEvent): Boolean = when (event) {
is VFilePropertyChangeEvent -> false
is VFileCreateEvent -> true
is VFileMoveEvent -> true
is VFileDeleteEvent -> true
is VFileContentChangeEvent -> true
is VFileCopyEvent -> true
else -> {
LOG.warn("Unknown vfs event: ${event.javaClass}")
false
}
}
fun onEvents(events: List<VFileEvent>, isAfter: Boolean) {
val service = PerModulePackageCacheService.getInstance(project)
val fileManager = PsiManagerEx.getInstanceEx(project).fileManager
if (events.size >= FULL_DROP_THRESHOLD) {
service.onTooComplexChange()
} else {
events.asSequence()
.filter(::isRelevant)
.filter {
(it.isValid || it !is VFileCreateEvent) && it.file != null
}
.filter {
val vFile = it.file!!
vFile.isDirectory || vFile.fileType == KotlinFileType.INSTANCE
}
.filter {
// It expected that content change events will be duplicated with more precise PSI events and processed
// in KotlinPackageStatementPsiTreeChangePreprocessor, but events might have been missing if PSI view provider
// is absent.
if (it is VFileContentChangeEvent) {
isAfter && fileManager.findCachedViewProvider(it.file) == null
} else {
true
}
}
.filter {
when (val origin = it.requestor) {
is Project -> origin == project
is PsiManager -> origin.project == project
else -> true
}
}
.forEach { event -> service.notifyPackageChange(event) }
}
}
})
connection.subscribe(ProjectTopics.PROJECT_ROOTS, object : ModuleRootListener {
override fun rootsChanged(event: ModuleRootEvent) {
PerModulePackageCacheService.getInstance(project).onTooComplexChange()
}
})
}
override fun dispose() {
connection.disconnect()
}
}
class KotlinPackageStatementPsiTreeChangePreprocessor(private val project: Project) : PsiTreeChangePreprocessor {
override fun treeChanged(event: PsiTreeChangeEventImpl) {
val eFile = event.file ?: event.child as? PsiFile
if (eFile == null) {
LOG.debugIfEnabled(project, true) { "Got PsiEvent: $event without file" }
}
val file = eFile as? KtFile ?: return
when (event.code) {
PsiTreeChangeEventImpl.PsiEventType.CHILD_ADDED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_MOVED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_REPLACED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_REMOVED -> {
val child = event.child ?: run {
LOG.debugIfEnabled(project, true) { "Got PsiEvent: $event without child" }
return
}
if (child.getParentOfType<KtPackageDirective>(false) != null)
ServiceManager.getService(project, PerModulePackageCacheService::class.java).notifyPackageChange(file)
}
PsiTreeChangeEventImpl.PsiEventType.CHILDREN_CHANGED -> {
val parent = event.parent ?: run {
LOG.debugIfEnabled(project, true) { "Got PsiEvent: $event without parent" }
return
}
val childrenOfType = parent.getChildrenOfType<KtPackageDirective>()
if (
(!event.isGenericChange && (childrenOfType.any() || parent is KtPackageDirective)) ||
(childrenOfType.any { it.name.isEmpty() } && parent is KtFile)
) {
ServiceManager.getService(project, PerModulePackageCacheService::class.java).notifyPackageChange(file)
}
}
else -> {
}
}
}
companion object {
val LOG = Logger.getInstance(this::class.java)
}
}
private typealias ImplicitPackageData = MutableMap<FqName, MutableList<VirtualFile>>
class ImplicitPackagePrefixCache(private val project: Project) {
private val implicitPackageCache = ConcurrentHashMap<VirtualFile, ImplicitPackageData>()
fun getPrefix(sourceRoot: VirtualFile): FqName {
val implicitPackageMap = implicitPackageCache.getOrPut(sourceRoot) { analyzeImplicitPackagePrefixes(sourceRoot) }
return implicitPackageMap.keys.singleOrNull() ?: FqName.ROOT
}
internal fun clear() {
implicitPackageCache.clear()
}
private fun analyzeImplicitPackagePrefixes(sourceRoot: VirtualFile): MutableMap<FqName, MutableList<VirtualFile>> {
val result = mutableMapOf<FqName, MutableList<VirtualFile>>()
val ktFiles = sourceRoot.children.filter { it.fileType == KotlinFileType.INSTANCE }
for (ktFile in ktFiles) {
result.addFile(ktFile)
}
return result
}
private fun ImplicitPackageData.addFile(ktFile: VirtualFile) {
synchronized(this) {
val psiFile = PsiManager.getInstance(project).findFile(ktFile) as? KtFile ?: return
addPsiFile(psiFile, ktFile)
}
}
private fun ImplicitPackageData.addPsiFile(
psiFile: KtFile,
ktFile: VirtualFile
) = getOrPut(psiFile.packageFqName) { mutableListOf() }.add(ktFile)
private fun ImplicitPackageData.removeFile(file: VirtualFile) {
synchronized(this) {
for ((key, value) in this) {
if (value.remove(file)) {
if (value.isEmpty()) remove(key)
break
}
}
}
}
private fun ImplicitPackageData.updateFile(file: KtFile) {
synchronized(this) {
removeFile(file.virtualFile)
addPsiFile(file, file.virtualFile)
}
}
internal fun update(event: VFileEvent) {
when (event) {
is VFileCreateEvent -> checkNewFileInSourceRoot(event.file)
is VFileDeleteEvent -> checkDeletedFileInSourceRoot(event.file)
is VFileCopyEvent -> {
val newParent = event.newParent
if (newParent.isValid) {
checkNewFileInSourceRoot(newParent.findChild(event.newChildName))
}
}
is VFileMoveEvent -> {
checkNewFileInSourceRoot(event.file)
if (event.oldParent.getSourceRoot(project) == event.oldParent) {
implicitPackageCache[event.oldParent]?.removeFile(event.file)
}
}
}
}
private fun checkNewFileInSourceRoot(file: VirtualFile?) {
if (file == null) return
if (file.getSourceRoot(project) == file.parent) {
implicitPackageCache[file.parent]?.addFile(file)
}
}
private fun checkDeletedFileInSourceRoot(file: VirtualFile?) {
val directory = file?.parent
if (directory == null || !directory.isValid) return
if (directory.getSourceRoot(project) == directory) {
implicitPackageCache[directory]?.removeFile(file)
}
}
internal fun update(ktFile: KtFile) {
val parent = ktFile.virtualFile?.parent ?: return
if (ktFile.sourceRoot == parent) {
implicitPackageCache[parent]?.updateFile(ktFile)
}
}
}
class PerModulePackageCacheService(private val project: Project) : Disposable {
/*
* Actually an WeakMap<Module, SoftMap<ModuleSourceInfo, SoftMap<FqName, Boolean>>>
*/
private val cache = ContainerUtil.createConcurrentWeakMap<Module, ConcurrentMap<ModuleSourceInfo, ConcurrentMap<FqName, Boolean>>>()
private val implicitPackagePrefixCache = ImplicitPackagePrefixCache(project)
private val pendingVFileChanges: MutableSet<VFileEvent> = mutableSetOf()
private val pendingKtFileChanges: MutableSet<KtFile> = mutableSetOf()
private val projectScope = GlobalSearchScope.projectScope(project)
internal fun onTooComplexChange() {
clear()
}
private fun clear() {
synchronized(this) {
pendingVFileChanges.clear()
pendingKtFileChanges.clear()
cache.clear()
implicitPackagePrefixCache.clear()
}
}
internal fun notifyPackageChange(file: VFileEvent): Unit = synchronized(this) {
pendingVFileChanges += file
}
internal fun notifyPackageChange(file: KtFile): Unit = synchronized(this) {
pendingKtFileChanges += file
}
private fun invalidateCacheForModuleSourceInfo(moduleSourceInfo: ModuleSourceInfo) {
LOG.debugIfEnabled(project) { "Invalidated cache for $moduleSourceInfo" }
val perSourceInfoData = cache[moduleSourceInfo.module] ?: return
val dataForSourceInfo = perSourceInfoData[moduleSourceInfo] ?: return
dataForSourceInfo.clear()
}
private fun checkPendingChanges() = synchronized(this) {
if (pendingVFileChanges.size + pendingKtFileChanges.size >= FULL_DROP_THRESHOLD) {
onTooComplexChange()
} else {
pendingVFileChanges.processPending { event ->
val vfile = event.file ?: return@processPending
// When VirtualFile !isValid (deleted for example), it impossible to use getModuleInfoByVirtualFile
// For directory we must check both is it in some sourceRoot, and is it contains some sourceRoot
if (vfile.isDirectory || !vfile.isValid) {
for ((module, data) in cache) {
val sourceRootUrls = module.rootManager.sourceRootUrls
if (sourceRootUrls.any { url ->
vfile.containedInOrContains(url)
}) {
LOG.debugIfEnabled(project) { "Invalidated cache for $module" }
data.clear()
}
}
} else {
val infoByVirtualFile = getModuleInfoByVirtualFile(project, vfile)
if (infoByVirtualFile == null || infoByVirtualFile !is ModuleSourceInfo) {
LOG.debugIfEnabled(project) { "Skip $vfile as it has mismatched ModuleInfo=$infoByVirtualFile" }
}
(infoByVirtualFile as? ModuleSourceInfo)?.let {
invalidateCacheForModuleSourceInfo(it)
}
}
implicitPackagePrefixCache.update(event)
}
pendingKtFileChanges.processPending { file ->
if (file.virtualFile != null && file.virtualFile !in projectScope) {
LOG.debugIfEnabled(project) {
"Skip $file without vFile, or not in scope: ${file.virtualFile?.let { it !in projectScope }}"
}
return@processPending
}
val nullableModuleInfo = file.getNullableModuleInfo()
(nullableModuleInfo as? ModuleSourceInfo)?.let { invalidateCacheForModuleSourceInfo(it) }
if (nullableModuleInfo == null || nullableModuleInfo !is ModuleSourceInfo) {
LOG.debugIfEnabled(project) { "Skip $file as it has mismatched ModuleInfo=$nullableModuleInfo" }
}
implicitPackagePrefixCache.update(file)
}
}
}
private inline fun <T> MutableCollection<T>.processPending(crossinline body: (T) -> Unit) {
this.removeIf { value ->
try {
body(value)
} catch (pce: ProcessCanceledException) {
throw pce
} catch (exc: Exception) {
// Log and proceed. Otherwise pending object processing won't be cleared and exception will be thrown forever.
LOG.error(exc)
}
return@removeIf true
}
}
private fun VirtualFile.containedInOrContains(root: String) =
(VfsUtilCore.isEqualOrAncestor(url, root)
|| isDirectory && VfsUtilCore.isEqualOrAncestor(root, url))
fun packageExists(packageFqName: FqName, moduleInfo: ModuleSourceInfo): Boolean {
val module = moduleInfo.module
checkPendingChanges()
val perSourceInfoCache = cache.getOrPut(module) {
ContainerUtil.createConcurrentSoftMap()
}
val cacheForCurrentModuleInfo = perSourceInfoCache.getOrPut(moduleInfo) {
ContainerUtil.createConcurrentSoftMap()
}
return cacheForCurrentModuleInfo.getOrPut(packageFqName) {
val packageExists = PackageIndexUtil.packageExists(packageFqName, moduleInfo.contentScope(), project)
LOG.debugIfEnabled(project) { "Computed cache value for $packageFqName in $moduleInfo is $packageExists" }
packageExists
}
}
fun getImplicitPackagePrefix(sourceRoot: VirtualFile): FqName {
checkPendingChanges()
return implicitPackagePrefixCache.getPrefix(sourceRoot)
}
override fun dispose() {
clear()
}
companion object {
const val FULL_DROP_THRESHOLD = 1000
private val LOG = Logger.getInstance(this::class.java)
fun getInstance(project: Project): PerModulePackageCacheService =
ServiceManager.getService(project, PerModulePackageCacheService::class.java)
var Project.DEBUG_LOG_ENABLE_PerModulePackageCache: Boolean
by NotNullableUserDataProperty<Project, Boolean>(Key.create("debug.PerModulePackageCache"), false)
}
}
private fun Logger.debugIfEnabled(project: Project, withCurrentTrace: Boolean = false, message: () -> String) {
if (ApplicationManager.getApplication().isUnitTestMode && project.DEBUG_LOG_ENABLE_PerModulePackageCache) {
val msg = message()
if (withCurrentTrace) {
val e = Exception().apply { fillInStackTrace() }
this.debug(msg, e)
} else {
this.debug(msg)
}
}
}
@@ -1,148 +0,0 @@
/*
* Copyright 2010-2017 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.idea.highlighter
import com.intellij.codeHighlighting.Pass
import com.intellij.codeHighlighting.TextEditorHighlightingPass
import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory
import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.daemon.impl.UpdateHighlightersUtil
import com.intellij.lang.annotation.Annotation
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.lang.annotation.HighlightSeverity.*
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.ProjectComponent
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.ui.MessageType
import com.intellij.openapi.wm.WindowManager
import com.intellij.openapi.wm.ex.StatusBarEx
import com.intellij.psi.PsiFile
import com.intellij.util.ui.UIUtil
import org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink
import org.jetbrains.kotlin.idea.script.ScriptDiagnosticFixProvider
import org.jetbrains.kotlin.psi.KtFile
import kotlin.script.experimental.api.ScriptDiagnostic
import kotlin.script.experimental.api.SourceCode
class ScriptExternalHighlightingPass(
private val file: KtFile,
document: Document
) : TextEditorHighlightingPass(file.project, document), DumbAware {
override fun doCollectInformation(progress: ProgressIndicator) = Unit
override fun doApplyInformationToEditor() {
val document = document ?: return
if (!file.isScript()) return
val reports = IdeScriptReportSink.getReports(file)
val annotations = reports.mapNotNull { scriptDiagnostic ->
val (startOffset, endOffset) = scriptDiagnostic.location?.let { computeOffsets(document, it) } ?: 0 to 0
val exception = scriptDiagnostic.exception
val exceptionMessage = if (exception != null) " ($exception)" else ""
val message = scriptDiagnostic.message + exceptionMessage
val annotation = Annotation(
startOffset,
endOffset,
scriptDiagnostic.severity.convertSeverity() ?: return@mapNotNull null,
message,
message
)
// if range is empty, show notification panel in editor
annotation.isFileLevelAnnotation = startOffset == endOffset
for (provider in ScriptDiagnosticFixProvider.EP_NAME.extensions) {
provider.provideFixes(scriptDiagnostic).forEach {
annotation.registerFix(it)
}
}
annotation
}
val infos = annotations.map { HighlightInfo.fromAnnotation(it) }
UpdateHighlightersUtil.setHighlightersToEditor(myProject, myDocument!!, 0, file.textLength, infos, colorsScheme, id)
}
private fun computeOffsets(document: Document, position: SourceCode.Location): Pair<Int, Int> {
val startLine = position.start.line.coerceLineIn(document)
val startOffset = document.offsetBy(startLine, position.start.col)
val endLine = position.end?.line?.coerceAtLeast(startLine)?.coerceLineIn(document) ?: startLine
val endOffset = document.offsetBy(
endLine,
position.end?.col ?: document.getLineEndOffset(endLine)
).coerceAtLeast(startOffset)
return startOffset to endOffset
}
private fun Int.coerceLineIn(document: Document) = coerceIn(0, document.lineCount - 1)
private fun Document.offsetBy(line: Int, col: Int): Int {
return (getLineStartOffset(line) + col).coerceIn(getLineStartOffset(line), getLineEndOffset(line))
}
private fun ScriptDiagnostic.Severity.convertSeverity(): HighlightSeverity? {
return when (this) {
ScriptDiagnostic.Severity.FATAL -> ERROR
ScriptDiagnostic.Severity.ERROR -> ERROR
ScriptDiagnostic.Severity.WARNING -> WARNING
ScriptDiagnostic.Severity.INFO -> INFORMATION
ScriptDiagnostic.Severity.DEBUG -> if (ApplicationManager.getApplication().isInternal) INFORMATION else null
}
}
private fun showNotification(file: KtFile, message: String) {
UIUtil.invokeLaterIfNeeded {
val ideFrame = WindowManager.getInstance().getIdeFrame(file.project)
if (ideFrame != null) {
val statusBar = ideFrame.statusBar as StatusBarEx
statusBar.notifyProgressByBalloon(
MessageType.WARNING,
message,
null,
null
)
}
}
}
class Factory(registrar: TextEditorHighlightingPassRegistrar) : ProjectComponent,
TextEditorHighlightingPassFactory {
init {
registrar.registerTextEditorHighlightingPass(
this,
TextEditorHighlightingPassRegistrar.Anchor.BEFORE,
Pass.UPDATE_FOLDING,
false,
false
)
}
override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass? {
if (file !is KtFile) return null
return ScriptExternalHighlightingPass(file, editor.document)
}
}
}
@@ -1,20 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.klib
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.BaseComponent
// FIX ME WHEN BUNCH 192 REMOVED
class KlibLoadingMetadataCache : KlibLoadingMetadataCacheCompat(), BaseComponent {
companion object {
@JvmStatic
fun getInstance(): KlibLoadingMetadataCache =
ApplicationManager.getApplication().getComponent(KlibLoadingMetadataCache::class.java)
}
}
@@ -1,114 +0,0 @@
/*
* Copyright 2010-2017 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.idea.modules;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiJavaModule;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.light.LightJavaModule;
import kotlin.collections.ArraysKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.core.FileIndexUtilsKt;
import java.io.IOException;
import java.io.InputStream;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import static com.intellij.psi.PsiJavaModule.MODULE_INFO_FILE;
// Copied from com.intellij.codeInsight.daemon.impl.analysis.ModuleHighlightUtil
public class ModuleHighlightUtil2 {
private static final Attributes.Name MULTI_RELEASE = new Attributes.Name("Multi-Release");
@Nullable
static PsiJavaModule getModuleDescriptor(@NotNull VirtualFile file, @NotNull Project project) {
ProjectFileIndex index = ProjectFileIndex.SERVICE.getInstance(project);
if (index.isInLibrary(file)) {
VirtualFile root;
if ((root = index.getClassRootForFile(file)) != null) {
VirtualFile descriptorFile = root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE);
if (descriptorFile == null) {
VirtualFile alt = root.findFileByRelativePath("META-INF/versions/9/" + PsiJavaModule.MODULE_INFO_CLS_FILE);
if (alt != null && isMultiReleaseJar(root)) {
descriptorFile = alt;
}
}
if (descriptorFile != null) {
PsiFile psiFile = PsiManager.getInstance(project).findFile(descriptorFile);
if (psiFile instanceof PsiJavaFile) {
return ((PsiJavaFile) psiFile).getModuleDeclaration();
}
}
else if (root.getFileSystem() instanceof JarFileSystem && "jar".equalsIgnoreCase(root.getExtension())) {
return LightJavaModule.getModule(PsiManager.getInstance(project), root);
}
}
else if ((root = index.getSourceRootForFile(file)) != null) {
VirtualFile descriptorFile = root.findChild(MODULE_INFO_FILE);
if (descriptorFile != null) {
PsiFile psiFile = PsiManager.getInstance(project).findFile(descriptorFile);
if (psiFile instanceof PsiJavaFile) {
return ((PsiJavaFile) psiFile).getModuleDeclaration();
}
}
}
}
else {
Module module = index.getModuleForFile(file);
if (module != null) {
boolean isTest = FileIndexUtilsKt.isInTestSourceContentKotlinAware(index, file);
VirtualFile modularRoot = ArraysKt.singleOrNull(ModuleRootManager.getInstance(module).getSourceRoots(isTest),
root -> root.findChild(MODULE_INFO_FILE) != null);
if (modularRoot != null) {
VirtualFile moduleInfo = modularRoot.findChild(MODULE_INFO_FILE);
assert moduleInfo != null : modularRoot;
PsiFile psiFile = PsiManager.getInstance(project).findFile(moduleInfo);
if (psiFile instanceof PsiJavaFile) {
return ((PsiJavaFile) psiFile).getModuleDeclaration();
}
}
}
}
return null;
}
private static boolean isMultiReleaseJar(VirtualFile root) {
if (root.getFileSystem() instanceof JarFileSystem) {
VirtualFile manifest = root.findFileByRelativePath(JarFile.MANIFEST_NAME);
if (manifest != null) {
try (InputStream stream = manifest.getInputStream()) {
return Boolean.valueOf(new Manifest(stream).getMainAttributes().getValue(MULTI_RELEASE));
}
catch (IOException ignored) {
}
}
}
return false;
}
}