001    /**
002     *   GRANITE DATA SERVICES
003     *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004     *
005     *   This file is part of Granite Data Services.
006     *
007     *   Granite Data Services is free software; you can redistribute it and/or modify
008     *   it under the terms of the GNU Library General Public License as published by
009     *   the Free Software Foundation; either version 2 of the License, or (at your
010     *   option) any later version.
011     *
012     *   Granite Data Services is distributed in the hope that it will be useful, but
013     *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014     *   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015     *   for more details.
016     *
017     *   You should have received a copy of the GNU Library General Public License
018     *   along with this library; if not, see <http://www.gnu.org/licenses/>.
019     */
020    package org.granite.client.util;
021    
022    import java.lang.ref.ReferenceQueue;
023    import java.lang.ref.WeakReference;
024    import java.util.Collection;
025    import java.util.Collections;
026    import java.util.HashMap;
027    import java.util.HashSet;
028    import java.util.Map;
029    import java.util.Set;
030    
031    /**
032     * @author William DRAI
033     */
034    public class WeakIdentityHashMap<K, V> implements Map<K, V> {
035        
036        private final ReferenceQueue<K> queue = new ReferenceQueue<K>();
037        private Map<IdentityWeakReference, V> map;
038    
039        
040        public WeakIdentityHashMap() {
041            map = new HashMap<IdentityWeakReference, V>();
042        }
043        
044        public WeakIdentityHashMap(int size) {
045            map = new HashMap<IdentityWeakReference, V>(size);
046        }
047    
048        public void clear() {
049            map.clear();
050            reap();
051        }
052    
053        public boolean containsKey(Object key) {
054            reap();
055            return map.containsKey(new IdentityWeakReference(key));
056        }
057    
058        public boolean containsValue(Object value)  {
059            reap();
060            return map.containsValue(value);
061        }
062    
063        public Set<Map.Entry<K, V>> entrySet() {
064            reap();
065            Set<Map.Entry<K, V>> ret = new HashSet<Map.Entry<K, V>>();
066            for (Map.Entry<IdentityWeakReference, V> ref : map.entrySet()) {
067                final K key = ref.getKey().get();
068                final V value = ref.getValue();
069                Map.Entry<K, V> entry = new Map.Entry<K, V>() {
070                    public K getKey() {
071                        return key;
072                    }
073                    public V getValue() {
074                        return value;
075                    }
076                    public V setValue(V value) {
077                        throw new UnsupportedOperationException();
078                    }
079                };
080                ret.add(entry);
081            }
082            return Collections.unmodifiableSet(ret);
083        }
084        
085        public Set<K> keySet() {
086            reap();
087            Set<K> ret = new HashSet<K>();
088            for (IdentityWeakReference ref : map.keySet())
089                ret.add(ref.get());
090            
091            return Collections.unmodifiableSet(ret);
092        }
093    
094        public boolean equals(Object o) {
095            return map.equals(((WeakIdentityHashMap<?, ?>)o).map);
096        }
097    
098        public V get(Object key) {
099            reap();
100            return map.get(new IdentityWeakReference(key));
101        }
102        public V put(K key, V value) {
103            reap();
104            return map.put(new IdentityWeakReference(key), value);
105        }
106    
107        public int hashCode() {
108            reap();
109            return map.hashCode();
110        }
111        
112        public boolean isEmpty() {
113            reap();
114            return map.isEmpty();
115        }
116        
117        public void putAll(Map<? extends K, ? extends V> m) {
118            throw new UnsupportedOperationException();
119        }
120        
121        public V remove(Object key) {
122            reap();
123            return map.remove(new IdentityWeakReference(key));
124        }
125        
126        public int size() {
127            reap();
128            return map.size();
129        }
130        
131        public Collection<V> values() {
132            reap();
133            return map.values();
134        }
135    
136        private synchronized void reap() {
137            Object zombie = queue.poll();
138    
139            while (zombie != null) {
140                @SuppressWarnings("unchecked")
141                IdentityWeakReference victim = (IdentityWeakReference)zombie;
142                map.remove(victim);
143                zombie = queue.poll();
144            }
145        }
146    
147        class IdentityWeakReference extends WeakReference<K> {
148            
149            private final int hash;
150            
151            @SuppressWarnings("unchecked")
152            IdentityWeakReference(Object obj) {
153                super((K)obj, queue);
154                hash = System.identityHashCode(obj);
155            }
156    
157            public int hashCode() {
158                return hash;
159            }
160    
161            public boolean equals(Object o) {
162                if (this == o)
163                    return true;
164                
165                @SuppressWarnings("unchecked")
166                IdentityWeakReference ref = (IdentityWeakReference)o;
167                if (this.get() == ref.get())
168                    return true;
169                
170                return false;
171            }
172        }
173    }