SimpleCacheBack.java

package space.sunqian.fs.cache;

import space.sunqian.annotation.Nonnull;
import space.sunqian.annotation.Nullable;
import space.sunqian.fs.Fs;
import space.sunqian.fs.base.value.Val;
import space.sunqian.fs.base.value.Var;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

final class SimpleCacheBack {

    static <K, V> @Nonnull SimpleCache<K, V> ofWeak() {
        return new WeakCache<>();
    }

    static <K, V> @Nonnull SimpleCache<K, V> ofSoft() {
        return new SoftCache<>();
    }

    static <K, V> @Nonnull SimpleCache<K, V> ofPhantom() {
        return new PhantomCache<>();
    }

    static <K, V> @Nonnull SimpleCache<K, V> ofStrong() {
        return new StrongCache<>();
    }

    static <K, V> @Nonnull SimpleCache<K, V> ofMap(@Nonnull Map<K, V> map) {
        return new MapCache<>(map);
    }

    private static <K, V> void compareAndRemove(@Nonnull Map<K, V> map, K key, V value) {
        V v = map.get(key);
        if (v == value) {
            map.remove(key);
        }

        // map.computeIfPresent(key, (k, old) -> {
        //     if (old == value) {
        //         return null;
        //     }
        //     return old;
        // });
    }

    private static abstract class ReferenceCache<K, V> extends AbstractSimpleCache<K, V> {

        protected final @Nonnull ReferenceQueue<Object> queue = new ReferenceQueue<>();

        private ReferenceCache() {
            super(new ConcurrentHashMap<>());
        }

        @Override
        public void clean() {
            while (true) {
                @Nullable Reference<?> reference = queue.poll();
                if (reference == null) {
                    return;
                }
                Value<K> rv = Fs.as(reference);
                compareAndRemove(cacheMap, rv.key(), rv);
            }
        }
    }

    private static final class WeakCache<K, V> extends ReferenceCache<K, V> {

        @Override
        protected @Nonnull Value<K> generate(@Nonnull K key, @Nonnull Object value) {
            return new WeakValue(key, value);
        }

        private final class WeakValue extends WeakReference<Object> implements Value<K> {

            private final @Nonnull K key;

            WeakValue(@Nonnull K key, @Nonnull Object value) {
                super(value, queue);
                this.key = key;
            }

            @Override
            public @Nonnull K key() {
                return key;
            }

            @Override
            public @Nullable Object refValue() {
                return get();
            }

            @Override
            public void invalid() {
                enqueue();
            }
        }
    }

    private static final class SoftCache<K, V> extends ReferenceCache<K, V> {

        @Override
        protected @Nonnull Value<K> generate(@Nonnull K key, @Nonnull Object value) {
            return new SoftValue(key, value);
        }

        private final class SoftValue extends SoftReference<Object> implements Value<K> {

            private final @Nonnull K key;

            SoftValue(@Nonnull K key, @Nonnull Object value) {
                super(value, queue);
                this.key = key;
            }

            @Override
            public @Nonnull K key() {
                return key;
            }

            @Override
            public @Nullable Object refValue() {
                return get();
            }

            @Override
            public void invalid() {
                enqueue();
            }
        }
    }

    private static final class PhantomCache<K, V> extends ReferenceCache<K, V> {

        @Override
        protected @Nonnull Value<K> generate(@Nonnull K key, @Nonnull Object value) {
            return new PhantomValue(key, value);
        }

        private final class PhantomValue extends PhantomReference<Object> implements Value<K> {

            private final @Nonnull K key;

            PhantomValue(@Nonnull K key, @Nonnull Object value) {
                super(value, queue);
                this.key = key;
            }

            @Override
            public @Nonnull K key() {
                return key;
            }

            @Override
            public @Nullable Object refValue() {
                return get();
            }

            @Override
            public void invalid() {
                enqueue();
            }
        }
    }

    private static final class StrongCache<K, V> extends AbstractSimpleCache<K, V> {

        private StrongCache() {
            super(new ConcurrentHashMap<>());
        }

        @Override
        public void clean() {
        }

        @Override
        protected @Nonnull Value<K> generate(@Nonnull K key, @Nonnull Object value) {
            return new StrongValue(key, value);
        }

        private final class StrongValue implements Value<K> {

            private final @Nonnull K key;
            private @Nullable Object value;

            StrongValue(@Nonnull K key, @Nonnull Object value) {
                this.key = key;
                this.value = value;
            }

            @Override
            public @Nonnull K key() {
                return key;
            }

            @Override
            public @Nullable Object refValue() {
                return value;
            }

            @Override
            public void invalid() {
                value = null;
                compareAndRemove(cacheMap, key(), StrongValue.this);
            }
        }
    }

    private static final class MapCache<K, V> implements SimpleCache<K, V> {

        private static final @Nonnull Object NONE = new Object();
        private final @Nonnull Map<K, V> cacheMap;

        private MapCache(@Nonnull Map<K, V> cacheMap) {
            this.cacheMap = cacheMap;
        }

        @Override
        public V get(@Nonnull K key) {
            return cacheMap.get(key);
        }

        @Override
        public @Nullable Val<V> getVal(@Nonnull K key) {
            Var<Object> var = Var.of(null);
            V v = cacheMap.computeIfAbsent(key, k -> {
                var.set(NONE);
                return null;
            });
            if (var.get() == NONE) {
                return null;
            }
            return Val.of(v);
        }

        @Override
        public V get(@Nonnull K key, @Nonnull Function<? super @Nonnull K, ? extends V> loader) {
            return cacheMap.computeIfAbsent(key, loader);
        }

        @Override
        public @Nullable Val<V> getVal(
            @Nonnull K key,
            @Nonnull Function<? super @Nonnull K, ? extends @Nullable Val<? extends V>> loader
        ) {
            Var<Object> var = Var.of(null);
            V v = cacheMap.computeIfAbsent(key, k -> {
                Val<? extends V> newV = loader.apply(key);
                if (newV == null) {
                    var.set(NONE);
                    return null;
                } else {
                    return newV.get();
                }
            });
            if (var.get() == NONE) {
                return null;
            }
            return Val.of(v);
        }

        @Override
        public void put(@Nonnull K key, V value) {
            cacheMap.put(key, value);
        }

        @Override
        public void remove(@Nonnull K key) {
            cacheMap.remove(key);
        }

        @Override
        public int size() {
            return cacheMap.size();
        }

        @Override
        public void clear() {
            cacheMap.clear();
        }

        @Override
        public void clean() {
        }

        @Override
        public @Nonnull Map<K, V> copyEntries() {
            return new LinkedHashMap<>(cacheMap);
        }
    }

    private SimpleCacheBack() {
    }
}