aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/utils/TimeLimitedCache.kt
blob: 4c96c9b3c7c96d30cb1bf02ffe2c57bbccce80a0 (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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package at.hannibal2.skyhanni.utils

import com.google.common.cache.CacheBuilder
import java.util.concurrent.ConcurrentMap
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.time.Duration

class TimeLimitedCache<K : Any, V : Any>(
    expireAfterWrite: Duration,
    private val removalListener: (K?, V?) -> Unit = { _, _ -> },
) : Iterable<Map.Entry<K, V>> {

    private val cacheLock = ReentrantReadWriteLock()

    private val cache = CacheBuilder.newBuilder()
        .expireAfterWrite(expireAfterWrite.inWholeMilliseconds, TimeUnit.MILLISECONDS)
        .removalListener {
            cacheLock.writeLock().lock()
            try {
                removalListener(it.key, it.value)
            } finally {
                cacheLock.writeLock().unlock()
            }
        }
        .build<K, V>()

    // TODO IntelliJ cant replace this, find another way?
//     @Deprecated("outdated", ReplaceWith("[key] = value"))
    @Deprecated("outdated", ReplaceWith("set(key, value)"))
    fun put(key: K, value: V) = set(key, value)

    fun getOrNull(key: K): V? = cache.getIfPresent(key)

    fun getOrPut(key: K, defaultValue: () -> V) = getOrNull(key) ?: defaultValue().also { set(key, it) }

    fun clear() = cache.invalidateAll()

    fun remove(key: K) = cache.invalidate(key)

    fun entries(): Set<Map.Entry<K, V>> = getMap().entries

    fun values(): Collection<V> = getMap().values

    fun keys(): Set<K> = getMap().keys

    /**
     * Modifications to the returned map are not supported and may lead to unexpected behavior.
     * This method is intended for read-only operations such as iteration or retrieval of values.
     *
     * @return A read-only view of the cache's underlying map.
     */
    private fun getMap(): ConcurrentMap<K, V> {
        val asMap: ConcurrentMap<K, V>

        cacheLock.readLock().lock()
        try {
            asMap = cache.asMap()
        } finally {
            cacheLock.readLock().unlock()
        }

        return asMap
    }

    fun containsKey(key: K): Boolean = cache.getIfPresent(key) != null

    override fun iterator(): Iterator<Map.Entry<K, V>> = entries().iterator()

    operator fun set(key: K, value: V) {
        cache.put(key, value)
    }
}