| 
68
 | 
     1 /*
 | 
| 
 | 
     2 Copyright (c) 2008  Franklin Schmidt <fschmidt@gmail.com>
 | 
| 
 | 
     3 
 | 
| 
 | 
     4 Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
| 
 | 
     5 of this software and associated documentation files (the "Software"), to deal
 | 
| 
 | 
     6 in the Software without restriction, including without limitation the rights
 | 
| 
 | 
     7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
| 
 | 
     8 copies of the Software, and to permit persons to whom the Software is
 | 
| 
 | 
     9 furnished to do so, subject to the following conditions:
 | 
| 
 | 
    10 
 | 
| 
 | 
    11 The above copyright notice and this permission notice shall be included in
 | 
| 
 | 
    12 all copies or substantial portions of the Software.
 | 
| 
 | 
    13 
 | 
| 
 | 
    14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
| 
 | 
    15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
| 
 | 
    16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
| 
 | 
    17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
| 
 | 
    18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
| 
 | 
    19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
| 
 | 
    20 THE SOFTWARE.
 | 
| 
 | 
    21 */
 | 
| 
 | 
    22 
 | 
| 
 | 
    23 package fschmidt.db.util;
 | 
| 
 | 
    24 
 | 
| 
 | 
    25 import java.lang.ref.ReferenceQueue;
 | 
| 
 | 
    26 import java.util.Map;
 | 
| 
 | 
    27 import java.util.AbstractMap;
 | 
| 
 | 
    28 import java.util.HashMap;
 | 
| 
 | 
    29 import java.util.Set;
 | 
| 
 | 
    30 import java.util.AbstractSet;
 | 
| 
 | 
    31 import java.util.Iterator;
 | 
| 
 | 
    32 
 | 
| 
 | 
    33 
 | 
| 
 | 
    34 public abstract class CacheMap<K,V> extends AbstractMap<K,V> {
 | 
| 
 | 
    35 
 | 
| 
 | 
    36 	protected static interface MyReference<K,V> {
 | 
| 
 | 
    37 		public K key();
 | 
| 
 | 
    38 		public V get();
 | 
| 
 | 
    39 		public void clear();
 | 
| 
 | 
    40 	}
 | 
| 
 | 
    41 
 | 
| 
 | 
    42 	protected abstract MyReference<K,V> newReference(K key,V value,ReferenceQueue<V> q);
 | 
| 
 | 
    43  
 | 
| 
 | 
    44 	private final Map<K,MyReference<K,V>> cache = new HashMap<K,MyReference<K,V>>();
 | 
| 
 | 
    45 	private final ReferenceQueue<V> queue = new ReferenceQueue<V>();
 | 
| 
 | 
    46 
 | 
| 
 | 
    47 	private void sweep() {
 | 
| 
 | 
    48 		while(true) {
 | 
| 
 | 
    49 			@SuppressWarnings("unchecked")
 | 
| 
 | 
    50 			MyReference<K,V> ref = (MyReference<K,V>)queue.poll();
 | 
| 
 | 
    51 			if( ref == null )
 | 
| 
 | 
    52 				return;
 | 
| 
 | 
    53 			MyReference<K,V> mappedRef = cache.remove(ref.key());
 | 
| 
 | 
    54 			if( mappedRef != ref && mappedRef != null )
 | 
| 
 | 
    55 				cache.put( mappedRef.key(), mappedRef );  // put it back
 | 
| 
 | 
    56 		}
 | 
| 
 | 
    57 	}
 | 
| 
 | 
    58 
 | 
| 
 | 
    59 	public int size() {
 | 
| 
 | 
    60 		return cache.size();
 | 
| 
 | 
    61 	}
 | 
| 
 | 
    62 
 | 
| 
 | 
    63 	public boolean isEmpty() {
 | 
| 
 | 
    64 		return cache.isEmpty();
 | 
| 
 | 
    65 	}
 | 
| 
 | 
    66 
 | 
| 
 | 
    67 	public boolean containsKey(Object key) {
 | 
| 
 | 
    68 		return cache.containsKey(key);
 | 
| 
 | 
    69 	}
 | 
| 
 | 
    70 
 | 
| 
 | 
    71 	public V get(Object key) {
 | 
| 
 | 
    72 		MyReference<K,V> ref = cache.get(key);
 | 
| 
 | 
    73 		return ref==null ? null : ref.get();
 | 
| 
 | 
    74 	}
 | 
| 
 | 
    75 
 | 
| 
 | 
    76 	public V put(K key,V value) {
 | 
| 
 | 
    77 		sweep();
 | 
| 
 | 
    78 		MyReference<K,V> ref = cache.put( key, newReference(key,value,queue) );
 | 
| 
 | 
    79 		return ref==null ? null : ref.get();
 | 
| 
 | 
    80 	}
 | 
| 
 | 
    81 
 | 
| 
 | 
    82 	public V remove(Object key) {
 | 
| 
 | 
    83 		sweep();
 | 
| 
 | 
    84 		MyReference<K,V> ref = cache.remove(key);
 | 
| 
 | 
    85 		return ref==null ? null : ref.get();
 | 
| 
 | 
    86 	}
 | 
| 
 | 
    87 
 | 
| 
 | 
    88 	public void clear() {
 | 
| 
 | 
    89 		sweep();
 | 
| 
 | 
    90 		cache.clear();
 | 
| 
 | 
    91 	}
 | 
| 
 | 
    92 
 | 
| 
 | 
    93 /*
 | 
| 
 | 
    94 	public Object clone() {
 | 
| 
 | 
    95 		GCCacheMap map = new GCCacheMap();
 | 
| 
 | 
    96 		map.cache = (HashMap)cache.clone();
 | 
| 
 | 
    97 		return map;
 | 
| 
 | 
    98 	}
 | 
| 
 | 
    99 */
 | 
| 
 | 
   100 	public Set<K> keySet() {
 | 
| 
 | 
   101 		return cache.keySet();
 | 
| 
 | 
   102 	}
 | 
| 
 | 
   103 
 | 
| 
 | 
   104 	public Set<Map.Entry<K,V>> entrySet() {
 | 
| 
 | 
   105 		return new MySet();
 | 
| 
 | 
   106 	}
 | 
| 
 | 
   107 
 | 
| 
 | 
   108 
 | 
| 
 | 
   109 	private class MySet extends AbstractSet<Map.Entry<K,V>> {
 | 
| 
 | 
   110 
 | 
| 
 | 
   111 		public int size() {
 | 
| 
 | 
   112 			return CacheMap.this.size();
 | 
| 
 | 
   113 		}
 | 
| 
 | 
   114 
 | 
| 
 | 
   115 		public Iterator<Map.Entry<K,V>> iterator() {
 | 
| 
 | 
   116 			return new MyIterator(cache.entrySet().iterator());
 | 
| 
 | 
   117 		}
 | 
| 
 | 
   118 
 | 
| 
 | 
   119 	}
 | 
| 
 | 
   120 
 | 
| 
 | 
   121 	private class MyIterator implements Iterator<Map.Entry<K,V>> {
 | 
| 
 | 
   122 		Iterator<Map.Entry<K,MyReference<K,V>>> iter;
 | 
| 
 | 
   123 
 | 
| 
 | 
   124 		MyIterator(Iterator<Map.Entry<K,MyReference<K,V>>> iter) {
 | 
| 
 | 
   125 			this.iter = iter;
 | 
| 
 | 
   126 		}
 | 
| 
 | 
   127 
 | 
| 
 | 
   128 		public boolean hasNext() {
 | 
| 
 | 
   129 			return iter.hasNext();
 | 
| 
 | 
   130 		}
 | 
| 
 | 
   131 
 | 
| 
 | 
   132 		public void remove() {
 | 
| 
 | 
   133 			iter.remove();
 | 
| 
 | 
   134 		}
 | 
| 
 | 
   135 
 | 
| 
 | 
   136 		public Map.Entry<K,V> next() {
 | 
| 
 | 
   137 			return new MyEntry( iter.next() );
 | 
| 
 | 
   138 		}
 | 
| 
 | 
   139 	}
 | 
| 
 | 
   140 
 | 
| 
 | 
   141 	private class MyEntry implements Map.Entry<K,V> {
 | 
| 
 | 
   142 		Map.Entry<K,MyReference<K,V>> entry;
 | 
| 
 | 
   143 
 | 
| 
 | 
   144 		MyEntry(Map.Entry<K,MyReference<K,V>> entry) {
 | 
| 
 | 
   145 			this.entry = entry;
 | 
| 
 | 
   146 		}
 | 
| 
 | 
   147 
 | 
| 
 | 
   148 		public K getKey() {
 | 
| 
 | 
   149 			return entry.getKey();
 | 
| 
 | 
   150 		}
 | 
| 
 | 
   151 
 | 
| 
 | 
   152 		public V getValue() {
 | 
| 
 | 
   153 			MyReference<K,V> ref = entry.getValue();
 | 
| 
 | 
   154 			return ref.get();
 | 
| 
 | 
   155 		}
 | 
| 
 | 
   156 
 | 
| 
 | 
   157 		public V setValue(V value) {
 | 
| 
 | 
   158 			MyReference<K,V> ref = entry.setValue( newReference(getKey(),value,queue) );
 | 
| 
 | 
   159 			return ref.get();
 | 
| 
 | 
   160 		}
 | 
| 
 | 
   161 
 | 
| 
 | 
   162 		public boolean equals(Object o) {
 | 
| 
 | 
   163 			if( o==null || !(o instanceof CacheMap.MyEntry) )
 | 
| 
 | 
   164 				return false;
 | 
| 
 | 
   165 			MyEntry m = (MyEntry)o;
 | 
| 
 | 
   166 			return entry.equals(m.entry);
 | 
| 
 | 
   167 		}
 | 
| 
 | 
   168 
 | 
| 
 | 
   169 		public int hashCode() {
 | 
| 
 | 
   170 			K key = getKey();
 | 
| 
 | 
   171 			V value = getValue();
 | 
| 
 | 
   172 			return (key==null ? 0 : key.hashCode()) ^
 | 
| 
 | 
   173 					(value==null ? 0 : value.hashCode());
 | 
| 
 | 
   174 		}
 | 
| 
 | 
   175 	}
 | 
| 
 | 
   176 
 | 
| 
 | 
   177 }
 |