All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.intermine.util.ReferenceMap Maven / Gradle / Ivy

package org.intermine.util;

/*
 * Copyright (C) 2002-2022 FlyMine
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  See the LICENSE file for more
 * information or http://www.gnu.org/copyleft/lesser.html.
 *
 */

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.Collection;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

/**
 * This is a Map implementation designed specifically for people intending to create a cache.
 * The class should be subclassed to provide soft or weak reference behaviour. The keys are held
 * strongly, but the values are held weakly or softly, so the values can be garbage-collected.
 * When an entry is garbage-collected, its key is removed from the Map on the next Map activity.
 * 

* The entrySet() and values() methods of this class do not work. * * @see java.lang.ref.SoftReference * @param the type of keys maintained by this map * @param the type of mapped values * @author Matthew Wakeling */ public abstract class ReferenceMap implements Map { private static final Logger LOG = Logger.getLogger(ReferenceMap.class); protected static final NullValue NULL_VALUE = new NullValue(); protected Map> subMap; protected ReferenceQueue queue = new ReferenceQueue(); protected String name; /** * Internal method to clean out stale entries. * Note that the garbage collector has very nicely and thoughtfully placed all such value * References into the queue for us to pick up. */ @SuppressWarnings("unchecked") private void expungeStaleEntries() { int oldSize = subMap.size(); ReferenceWithKey r; while ((r = (ReferenceWithKey) queue.poll()) != null) { K key = r.getKey(); Reference ref = subMap.get(key); if (r == ref) { subMap.remove(key); } } int newSize = subMap.size(); if (newSize != oldSize) { LOG.debug(name + ": Expunged stale entries - size " + oldSize + " -> " + newSize); } } /** * {@inheritDoc} */ @Override public int size() { expungeStaleEntries(); return subMap.size(); } /** * {@inheritDoc} */ @Override public boolean isEmpty() { return size() == 0; } /** * {@inheritDoc} */ @Override public Set keySet() { expungeStaleEntries(); return subMap.keySet(); } /** * {@inheritDoc} */ @Override public void clear() { expungeStaleEntries(); subMap.clear(); } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public V get(Object key) { expungeStaleEntries(); Reference ref = subMap.get(key); if (ref != null) { Object value = ref.get(); if (value instanceof NullValue) { return null; } return (V) value; } return null; } /** * {@inheritDoc} */ @Override public boolean containsKey(Object key) { expungeStaleEntries(); // Note - expungeEntries is NOT guaranteed to remove everything from the subMap that has // been cleared. The garbage collector may insert a gap between clearing a Reference and // enqueueing it. if (subMap.containsKey(key)) { Reference ref = subMap.get(key); if (ref != null) { Object value = ref.get(); if (value == null) { return false; } return true; } return false; } return false; } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public V put(K key, V value) { expungeStaleEntries(); Object v = value; if (v == null) { v = NULL_VALUE; } Reference ref = subMap.put(key, newRef(v, queue, key)); if (ref != null) { v = ref.get(); if (v instanceof NullValue) { return null; } return (V) v; } return null; } /** * Private method to create a new ReferenceWithKey object. This should be overridden by * subclasses wishing to create soft/weak behaviour. * * @param value the value put into the Reference * @param queue the ReferenceQueue to register the Reference in * @param key the key * @return a ReferenceWithKey, that is also a Reference (long story, no multiple inheritance in * Java, and Reference is daftly an abstract class rather than an interface) */ protected abstract Reference newRef(Object value, ReferenceQueue queue, K key); /** * {@inheritDoc} */ @Override public void putAll(Map t) { for (Map.Entry entry : t.entrySet()) { put(entry.getKey(), entry.getValue()); } } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public V remove(Object key) { expungeStaleEntries(); Reference ref = subMap.remove(key); if (ref != null) { Object value = ref.get(); if (value instanceof NullValue) { return null; } return (V) value; } return null; } /** * {@inheritDoc} */ @Override public boolean containsValue(@SuppressWarnings("unused") Object value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public Set> entrySet() { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public Collection values() { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public boolean equals(@SuppressWarnings("unused") Object o) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public int hashCode() { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public String toString() { return subMap.toString(); } /** * Interface for entries in the map. * @param the key */ protected interface ReferenceWithKey { /** * Returns the key in the entry. * * @return an Object */ K getKey(); } private static class NullValue { NullValue() { } } }