001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2012 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    
021    package org.granite.client.util;
022    
023    import java.lang.ref.ReferenceQueue;
024    import java.lang.ref.WeakReference;
025    import java.util.Collection;
026    import java.util.Collections;
027    import java.util.HashMap;
028    import java.util.HashSet;
029    import java.util.Map;
030    import java.util.Set;
031    
032    /**
033     * @author William DRAI
034     */
035    public class WeakIdentityHashMap<K, V> implements Map<K, V> {
036        
037        private final ReferenceQueue<K> queue = new ReferenceQueue<K>();
038        private Map<IdentityWeakReference, V> map = new HashMap<IdentityWeakReference, V>();
039    
040    
041        public void clear() {
042            map.clear();
043            reap();
044        }
045    
046        public boolean containsKey(Object key) {
047            reap();
048            return map.containsKey(new IdentityWeakReference(key));
049        }
050    
051        public boolean containsValue(Object value)  {
052            reap();
053            return map.containsValue(value);
054        }
055    
056        public Set<Map.Entry<K, V>> entrySet() {
057            reap();
058            Set<Map.Entry<K, V>> ret = new HashSet<Map.Entry<K, V>>();
059            for (Map.Entry<IdentityWeakReference, V> ref : map.entrySet()) {
060                final K key = ref.getKey().get();
061                final V value = ref.getValue();
062                Map.Entry<K, V> entry = new Map.Entry<K, V>() {
063                    public K getKey() {
064                        return key;
065                    }
066                    public V getValue() {
067                        return value;
068                    }
069                    public V setValue(V value) {
070                        throw new UnsupportedOperationException();
071                    }
072                };
073                ret.add(entry);
074            }
075            return Collections.unmodifiableSet(ret);
076        }
077        
078        public Set<K> keySet() {
079            reap();
080            Set<K> ret = new HashSet<K>();
081            for (IdentityWeakReference ref : map.keySet())
082                ret.add(ref.get());
083            
084            return Collections.unmodifiableSet(ret);
085        }
086    
087        public boolean equals(Object o) {
088            return map.equals(((WeakIdentityHashMap<?, ?>)o).map);
089        }
090    
091        public V get(Object key) {
092            reap();
093            return map.get(new IdentityWeakReference(key));
094        }
095        public V put(K key, V value) {
096            reap();
097            return map.put(new IdentityWeakReference(key), value);
098        }
099    
100        public int hashCode() {
101            reap();
102            return map.hashCode();
103        }
104        
105        public boolean isEmpty() {
106            reap();
107            return map.isEmpty();
108        }
109        
110        public void putAll(Map<? extends K, ? extends V> m) {
111            throw new UnsupportedOperationException();
112        }
113        
114        public V remove(Object key) {
115            reap();
116            return map.remove(new IdentityWeakReference(key));
117        }
118        
119        public int size() {
120            reap();
121            return map.size();
122        }
123        
124        public Collection<V> values() {
125            reap();
126            return map.values();
127        }
128    
129        private synchronized void reap() {
130            Object zombie = queue.poll();
131    
132            while (zombie != null) {
133                @SuppressWarnings("unchecked")
134                IdentityWeakReference victim = (IdentityWeakReference)zombie;
135                map.remove(victim);
136                zombie = queue.poll();
137            }
138        }
139    
140        class IdentityWeakReference extends WeakReference<K> {
141            
142            private final int hash;
143            
144            @SuppressWarnings("unchecked")
145            IdentityWeakReference(Object obj) {
146                super((K)obj, queue);
147                hash = System.identityHashCode(obj);
148            }
149    
150            public int hashCode() {
151                return hash;
152            }
153    
154            public boolean equals(Object o) {
155                if (this == o)
156                    return true;
157                
158                @SuppressWarnings("unchecked")
159                IdentityWeakReference ref = (IdentityWeakReference)o;
160                if (this.get() == ref.get())
161                    return true;
162                
163                return false;
164            }
165        }
166    }