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 }