001 /* 002 * (c) 2007 Tasecurity Group S.L, Spain 003 * This library is free software; you can redistribute it and/or 004 * modify it under the terms of the GNU Lesser General Public 005 * License as published by the Free Software Foundation; either 006 * version 2.1 of the License, or (at your option) any later version. 007 * 008 * This library is distributed in the hope that it will be useful, 009 * but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 011 * Lesser General Public License for more details. 012 * 013 * You should have received a copy of the GNU Lesser General Public 014 * License along with this library; if not, write to the Free Software 015 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 016 */ 017 package mondrian.util; 018 019 import java.lang.ref.WeakReference; 020 import java.util.ArrayList; 021 import java.util.Collection; 022 import java.util.HashMap; 023 import java.util.HashSet; 024 import java.util.List; 025 import java.util.Map; 026 import java.util.Set; 027 import java.util.WeakHashMap; 028 029 /** 030 * Map with limited size to be used as cache. 031 * 032 * @author lcanals, www.tasecurity.net 033 */ 034 public class CacheMap<S, T> implements Map<S, T> { 035 private LinkedNode head; 036 private LinkedNode tail; 037 private final Map<S, Pair<S,T>> map; 038 private final int maxSize; 039 040 /** 041 * Creates an empty map with limited size. 042 * 043 * @param size Maximum number of mapped elements. 044 */ 045 public CacheMap(final int size) { 046 this.head = new LinkedNode(null, null); 047 this.tail = new LinkedNode(head, null); 048 this.map = new WeakHashMap<S,Pair<S,T>>(size); 049 this.maxSize = size; 050 } 051 052 public void clear() { 053 this.head = new LinkedNode(null, null); 054 this.tail = new LinkedNode(head, null); 055 map.clear(); 056 } 057 058 public boolean containsKey(final Object key) { 059 return map.containsKey(key); 060 } 061 062 public boolean containsValue(final Object value) { 063 return this.values().contains(value); 064 } 065 066 public Set entrySet() { 067 final Set<Map.Entry<S,T>> set = new HashSet<Map.Entry<S,T>>(); 068 for (final Map.Entry<S, Pair<S,T>> entry : this.map.entrySet()) { 069 set.add(new Map.Entry<S,T>() { 070 public boolean equals(Object s) { 071 if (s instanceof Map.Entry) { 072 return ((Map.Entry) s).getKey().equals( 073 entry.getKey()) 074 && ((Map.Entry) s).getValue().equals( 075 entry.getValue().value); 076 } else { 077 return false; 078 } 079 } 080 public S getKey() { 081 return entry.getKey(); 082 } 083 public T getValue() { 084 return entry.getValue().value; 085 } 086 public int hashCode() { 087 return entry.hashCode(); 088 } 089 public T setValue(final T x) { 090 return entry.setValue(new Pair<S,T>( 091 x, new LinkedNode(head, entry.getKey()))) 092 .value; 093 } 094 }); 095 } 096 return set; 097 } 098 099 public T get(final Object key) { 100 final Pair<S,T> pair = map.get(key); 101 if (pair != null) { 102 final LinkedNode<S> node = pair.getNode(); 103 if (node == null) { 104 map.remove(key); 105 return null; 106 } 107 node.moveTo(head); 108 return pair.value; 109 } else { 110 return null; 111 } 112 } 113 114 public boolean isEmpty() { 115 return map.isEmpty(); 116 } 117 118 public Set<S> keySet() { 119 return map.keySet(); 120 } 121 122 public T put(final S key, final T value) { 123 final Pair<S, T> pair = new Pair<S,T>(value, new LinkedNode(head, key)); 124 final Pair<S, T> obj = map.put(key, pair); 125 if (map.size() > maxSize) { 126 tail.getPrevious().remove(); 127 map.remove(key); 128 } 129 if (obj != null) { 130 return obj.value; 131 } else { 132 return null; 133 } 134 } 135 136 public void putAll(final Map t) { 137 throw new UnsupportedOperationException(); 138 } 139 140 public T remove(final Object key) { 141 final Pair<S,T> pair = map.get(key); 142 if (pair == null) { 143 return null; 144 } 145 pair.getNode().remove(); 146 return map.remove(key).value; 147 } 148 149 public int size() { 150 return map.size(); 151 } 152 153 public Collection<T> values() { 154 final List<T> vals = new ArrayList<T>(); 155 for (final Pair<S,T> pair : map.values()) { 156 vals.add(pair.value); 157 } 158 return vals; 159 } 160 161 public int hashCode() { 162 return map.hashCode(); 163 } 164 165 public String toString() { 166 return "Ordered keys: " + head.toString() + "\n" 167 + "Map:" + map.toString(); 168 } 169 170 public boolean equals(Object o) { 171 CacheMap c = (CacheMap) o; 172 return map.equals(c.map); 173 } 174 175 // 176 // PRIVATE STUFF ------------------ 177 // 178 179 /** 180 * Pair of linked key - value 181 */ 182 private final class Pair<S, T> implements java.io.Serializable { 183 private final T value; 184 private final WeakReference<LinkedNode<S>> node; 185 private Pair(final T value, final LinkedNode<S> node) { 186 this.node = new WeakReference<LinkedNode<S>>(node); 187 this.value = value; 188 } 189 190 private LinkedNode<S> getNode() { 191 return node.get(); 192 } 193 194 public boolean equals(final Object o) { 195 return o != null && o.equals(this.value); 196 } 197 } 198 199 200 /** 201 * Represents a node in a linked list. 202 */ 203 private class LinkedNode<S> implements java.io.Serializable { 204 private LinkedNode<S> next, prev; 205 private S key; 206 207 public LinkedNode(final LinkedNode<S> prev, final S key) { 208 this.key = key; 209 insertAfter(prev); 210 } 211 212 public void remove() { 213 if (this.prev != null) { 214 this.prev.next = this.next; 215 } 216 if (this.next != null) { 217 this.next.prev = this.prev; 218 } 219 } 220 221 public void moveTo(final LinkedNode<S> prev) { 222 remove(); 223 insertAfter(prev); 224 } 225 226 public LinkedNode<S> getPrevious() { 227 return this.prev; 228 } 229 230 public String toString() { 231 if (this.next != null) { 232 if (key != null) { 233 return key.toString() + ", " + this.next.toString(); 234 } else { 235 return "<null>, " + this.next.toString(); 236 } 237 } else { 238 if (key != null) { 239 return key.toString(); 240 } else { 241 return "<null>"; 242 } 243 } 244 } 245 246 private void insertAfter(final LinkedNode<S> prev) { 247 if (prev != null) { 248 this.next = prev.next; 249 } else { 250 this.prev = null; 251 } 252 this.prev = prev; 253 254 if (prev != null) { 255 if (prev.next != null) { 256 prev.next.prev = this; 257 } 258 prev.next = this; 259 } 260 } 261 } 262 } 263 264 // End CacheMap.java 265