summaryrefslogtreecommitdiff
path: root/plugins/kotlin/analysis/src/org/jetbrains/kotlin/idea/caches/lightClasses/ClsJavaStubByVirtualFileCache.kt
blob: 9f76b09c0e76f3baaade6293dba4a562f2e6c702 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.

package org.jetbrains.kotlin.idea.caches.lightClasses

import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl
import com.intellij.util.cls.ClsFormatException
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.idea.util.application.getServiceSafe
import java.io.IOException

class ClsJavaStubByVirtualFileCache {
    private class CachedJavaStub(val modificationStamp: Long, val javaFileStub: PsiJavaFileStubImpl)

    private val cache = ContainerUtil.createConcurrentWeakKeySoftValueMap<VirtualFile, CachedJavaStub>()

    fun get(classFile: VirtualFile): PsiJavaFileStubImpl? {
        val cached = cache.get(classFile)
        val fileModificationStamp = classFile.modificationStamp
        if (cached != null && cached.modificationStamp == fileModificationStamp) {
            return cached.javaFileStub
        }
        val stub = createStub(classFile) as PsiJavaFileStubImpl? ?: return null
        cache.put(classFile, CachedJavaStub(fileModificationStamp, stub))
        return stub
    }

    private fun createStub(file: VirtualFile): PsiJavaFileStub? {
        if (file.fileType !== JavaClassFileType.INSTANCE) return null

        try {
            return ClsFileImpl.buildFileStub(file, file.contentsToByteArray(false))
        } catch (e: ClsFormatException) {
            LOG.warn("Failed to build java cls class for " + file.canonicalPath!!, e)
        } catch (e: IOException) {
            LOG.warn("Failed to build java cls class for " + file.canonicalPath!!, e)
        }

        return null
    }

    companion object {
        private val LOG = Logger.getInstance(ClsJavaStubByVirtualFileCache::class.java)

        fun getInstance(project: Project): ClsJavaStubByVirtualFileCache = project.getServiceSafe()
    }
}