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 }