ObjectPool.java

package swingtree.style;

import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.WeakHashMap;

/**
 *  The process-wide weak interner backing {@link Pooled#intern()} — conceptually
 *  {@link String#intern()} for SwingTree's large immutable configuration objects.
 *  It collapses every {@link Pooled} that is {@code equal} to one already seen onto a
 *  single <em>canonical</em> instance, so equal config values can be compared by
 *  reference and, crucially, share a single entry in the various {@link WeakHashMap}-based
 *  rendering caches (style layers, noise, shadows, text images, layouts, component areas).
 *
 *  <h2>Why both the keys and the values are weak</h2>
 *  The backing map holds its keys weakly (it is a {@link WeakHashMap}) <em>and</em> wraps
 *  each canonical value in a {@link WeakReference}. A canonical {@link Pooled} therefore
 *  stays interned only while something <em>outside</em> the pool still references it
 *  strongly; once the last such reference is dropped it is reclaimed and falls out of the
 *  pool on its own. This is the linchpin of SwingTree's self-clearing cache design: a
 *  rendering cache keyed by these canonical instances keeps an entry alive for exactly as
 *  long as a live component or style configuration still holds the corresponding key, and
 *  not a moment longer. The pool itself pins nothing.
 *
 *  <h2>Threading</h2>
 *  Interning happens on the Event Dispatch Thread, like all of SwingTree's painting and
 *  layout work — it is, after all, built on single-threaded Swing. The pool is therefore a
 *  plain, unsynchronized {@link WeakHashMap}, consistent with every cache that funnels its
 *  keys through it.
 */
final class ObjectPool {
    private static final ObjectPool INSTANCE = new ObjectPool();

    static ObjectPool get() {
        return INSTANCE;
    }

    private final Map<Pooled<?>, WeakReference<Pooled<?>>> pool = new WeakHashMap<>();

    private ObjectPool(){}

    public <T> Pooled<T> intern(Pooled<T> value ) {
        WeakReference<Pooled<T>> ref = (WeakReference) pool.get(value);

        if (ref != null) {
            Pooled<T> canonical = ref.get();
            if (canonical != null) {
                return canonical;
            }
        }
        pool.put(value, new WeakReference<>(value));
        return value;
    }
}