001    /*******************************************************************************
002     * Portions created by Sebastian Thomschke are copyright (c) 2005-2011 Sebastian
003     * Thomschke.
004     * 
005     * All Rights Reserved. This program and the accompanying materials
006     * are made available under the terms of the Eclipse Public License v1.0
007     * which accompanies this distribution, and is available at
008     * http://www.eclipse.org/legal/epl-v10.html
009     * 
010     * Contributors:
011     *     Sebastian Thomschke - initial implementation.
012     *******************************************************************************/
013    package net.sf.oval.internal.util;
014    
015    import java.lang.ref.SoftReference;
016    import java.util.HashMap;
017    import java.util.LinkedList;
018    import java.util.Map;
019    
020    /**
021     * @author Sebastian Thomschke
022     */
023    public final class ObjectCache<K, V>
024    {
025            private final Map<K, SoftReference<V>> map = new HashMap<K, SoftReference<V>>();
026    
027            private final LinkedList<V> objectsLastAccessed = new LinkedList<V>();
028            private final int objectsToKeepCount;
029    
030            /**
031             * Creates a new cache keeping all objects.
032             */
033            public ObjectCache()
034            {
035                    objectsToKeepCount = -1;
036            }
037    
038            /**
039             * @param maxObjectsToKeep the number of cached objects that should stay in memory when GC 
040             * starts removing SoftReferences to free memory 
041             */
042            public ObjectCache(final int maxObjectsToKeep)
043            {
044                    this.objectsToKeepCount = maxObjectsToKeep;
045            }
046    
047            public void compact()
048            {
049                    for (final Map.Entry<K, SoftReference<V>> entry : map.entrySet())
050                    {
051                            final SoftReference<V> ref = entry.getValue();
052                            if (ref.get() == null) map.remove(entry.getKey());
053                    }
054            }
055    
056            public boolean contains(final K key)
057            {
058                    return map.containsKey(key);
059            }
060    
061            public V get(final K key)
062            {
063                    final SoftReference<V> softReference = map.get(key);
064                    if (softReference != null)
065                    {
066                            final V value = softReference.get();
067    
068                            if (value == null)
069                                    map.remove(key);
070                            else if (objectsToKeepCount > 0 && value != objectsLastAccessed.getFirst())
071                            {
072                                    objectsLastAccessed.remove(value);
073                                    objectsLastAccessed.addFirst(value);
074                                    if (objectsLastAccessed.size() > objectsToKeepCount) objectsLastAccessed.removeLast();
075                            }
076                            return softReference.get();
077                    }
078                    return null;
079            }
080    
081            public void put(final K key, final V value)
082            {
083                    map.remove(key);
084                    map.put(key, new SoftReference<V>(value));
085            }
086    }