001    package org.bridj.util;
002    
003    import java.lang.reflect.Constructor;
004    import java.util.HashMap;
005    import java.util.Map;
006    import java.util.Map.Entry;
007    import java.util.concurrent.ConcurrentHashMap;
008    
009    /**
010     * Cache that creates its missing values automatically, using the value class' default constructor (override {@link ConcurrentCache#newInstance(Object)} to call another constructor)
011     */
012    public class ConcurrentCache<K, V> { 
013        protected final ConcurrentHashMap<K, V> map = new ConcurrentHashMap<K, V>();
014            protected final Class<V> valueClass;
015        
016        public ConcurrentCache(Class<V> valueClass) {
017            this.valueClass = valueClass;
018            }
019            
020        private volatile Constructor<V> valueConstructor;
021        private Constructor<V> getValueConstructor() {
022            if (valueConstructor == null) {
023                try {
024                    valueConstructor = valueClass.getConstructor();
025                    if (valueConstructor != null && valueConstructor.isAccessible())
026                        valueConstructor.setAccessible(true);
027                } catch (Exception ex) {
028                    throw new RuntimeException("No accessible default constructor in class " + (valueClass == null ? "null" : valueClass.getName()), ex);
029                }
030            }
031            return valueConstructor;
032        }
033        
034            protected V newInstance(K key) {
035                    try {
036                            return getValueConstructor().newInstance();
037                    } catch (Exception ex) {
038                            throw new RuntimeException("Failed to call constructor " + valueConstructor, ex);
039                    }
040            }
041        
042            public V get(K key) {
043            V v = map.get(key);
044            if (v == null) {
045                V newV = newInstance(key);
046                V oldV = map.putIfAbsent(key, newV);
047                if (oldV != null)
048                    v = oldV;
049                else
050                    v = newV;
051            }
052            return v;
053            }
054        
055        public void clear() {
056            map.clear();
057        }
058    
059        public Iterable<Entry<K, V>> entrySet() {
060            return map.entrySet();
061        }
062    }