1 11 package org.eclipse.jdt.internal.core.util; 12 13 import java.text.NumberFormat ; 14 import java.util.Enumeration ; 15 import java.util.Hashtable ; 16 17 32 public class LRUCache implements Cloneable { 33 34 42 protected static class LRUCacheEntry { 43 44 47 public Object _fKey; 48 49 52 public Object _fValue; 53 54 57 public int _fTimestamp; 58 59 62 public int _fSpace; 63 64 67 public LRUCacheEntry _fPrevious; 68 69 72 public LRUCacheEntry _fNext; 73 74 78 public LRUCacheEntry (Object key, Object value, int space) { 79 _fKey = key; 80 _fValue = value; 81 _fSpace = space; 82 } 83 84 87 public String toString() { 88 89 return "LRUCacheEntry [" + _fKey + "-->" + _fValue + "]"; } 91 } 92 93 96 protected int fCurrentSpace; 97 98 101 protected int fSpaceLimit; 102 103 106 protected int fTimestampCounter; 107 108 111 protected Hashtable fEntryTable; 112 113 116 protected LRUCacheEntry fEntryQueue; 117 118 121 protected LRUCacheEntry fEntryQueueTail; 122 123 126 protected static final int DEFAULT_SPACELIMIT = 100; 127 131 public LRUCache() { 132 133 this(DEFAULT_SPACELIMIT); 134 } 135 139 public LRUCache(int size) { 140 141 fTimestampCounter = fCurrentSpace = 0; 142 fEntryQueue = fEntryQueueTail = null; 143 fEntryTable = new Hashtable (size); 144 fSpaceLimit = size; 145 } 146 151 public Object clone() { 152 153 LRUCache newCache = newInstance(fSpaceLimit); 154 LRUCacheEntry qEntry; 155 156 157 qEntry = this.fEntryQueueTail; 158 while (qEntry != null) { 159 newCache.privateAdd (qEntry._fKey, qEntry._fValue, qEntry._fSpace); 160 qEntry = qEntry._fPrevious; 161 } 162 return newCache; 163 } 164 public double fillingRatio() { 165 return (fCurrentSpace) * 100.0 / fSpaceLimit; 166 } 167 170 public void flush() { 171 172 fCurrentSpace = 0; 173 LRUCacheEntry entry = fEntryQueueTail; fEntryTable = new Hashtable (); fEntryQueue = fEntryQueueTail = null; 176 while (entry != null) { entry = entry._fPrevious; 178 } 179 } 180 186 public void flush (Object key) { 187 188 LRUCacheEntry entry; 189 190 entry = (LRUCacheEntry) fEntryTable.get(key); 191 192 193 if (entry == null) return; 194 195 this.privateRemoveEntry (entry, false); 196 } 197 204 public Object get(Object key) { 205 206 LRUCacheEntry entry = (LRUCacheEntry) fEntryTable.get(key); 207 if (entry == null) { 208 return null; 209 } 210 211 this.updateTimestamp (entry); 212 return entry._fValue; 213 } 214 217 public int getCurrentSpace() { 218 return fCurrentSpace; 219 } 220 223 public int getSpaceLimit() { 224 return fSpaceLimit; 225 } 226 229 public Enumeration keys() { 230 231 return fEntryTable.keys(); 232 } 233 237 public ICacheEnumeration keysAndValues() { 238 return new ICacheEnumeration() { 239 240 Enumeration fValues = fEntryTable.elements(); 241 LRUCacheEntry fEntry; 242 243 public boolean hasMoreElements() { 244 return fValues.hasMoreElements(); 245 } 246 247 public Object nextElement() { 248 fEntry = (LRUCacheEntry) fValues.nextElement(); 249 return fEntry._fKey; 250 } 251 252 public Object getValue() { 253 if (fEntry == null) { 254 throw new java.util.NoSuchElementException (); 255 } 256 return fEntry._fValue; 257 } 258 }; 259 } 260 267 protected boolean makeSpace (int space) { 268 269 int limit; 270 271 limit = this.getSpaceLimit(); 272 273 274 if (fCurrentSpace + space <= limit) { 275 return true; 276 } 277 278 279 if (space > limit) { 280 return false; 281 } 282 283 284 while (fCurrentSpace + space > limit && fEntryQueueTail != null) { 285 this.privateRemoveEntry (fEntryQueueTail, false); 286 } 287 return true; 288 } 289 292 protected LRUCache newInstance(int size) { 293 return new LRUCache(size); 294 } 295 301 public Object peek(Object key) { 302 303 LRUCacheEntry entry = (LRUCacheEntry) fEntryTable.get(key); 304 if (entry == null) { 305 return null; 306 } 307 return entry._fValue; 308 } 309 312 protected void privateAdd (Object key, Object value, int space) { 313 314 LRUCacheEntry entry; 315 316 entry = new LRUCacheEntry(key, value, space); 317 this.privateAddEntry (entry, false); 318 } 319 324 protected void privateAddEntry (LRUCacheEntry entry, boolean shuffle) { 325 326 if (!shuffle) { 327 fEntryTable.put (entry._fKey, entry); 328 fCurrentSpace += entry._fSpace; 329 } 330 331 entry._fTimestamp = fTimestampCounter++; 332 entry._fNext = this.fEntryQueue; 333 entry._fPrevious = null; 334 335 if (fEntryQueue == null) { 336 337 fEntryQueueTail = entry; 338 } else { 339 fEntryQueue._fPrevious = entry; 340 } 341 342 fEntryQueue = entry; 343 } 344 349 protected void privateRemoveEntry (LRUCacheEntry entry, boolean shuffle) { 350 351 LRUCacheEntry previous, next; 352 353 previous = entry._fPrevious; 354 next = entry._fNext; 355 356 if (!shuffle) { 357 fEntryTable.remove(entry._fKey); 358 fCurrentSpace -= entry._fSpace; 359 } 360 361 362 if (previous == null) { 363 fEntryQueue = next; 364 } else { 365 previous._fNext = next; 366 } 367 368 369 if (next == null) { 370 fEntryQueueTail = previous; 371 } else { 372 next._fPrevious = previous; 373 } 374 } 375 382 public Object put(Object key, Object value) { 383 384 int newSpace, oldSpace, newTotal; 385 LRUCacheEntry entry; 386 387 388 newSpace = spaceFor(value); 389 entry = (LRUCacheEntry) fEntryTable.get (key); 390 391 if (entry != null) { 392 393 398 oldSpace = entry._fSpace; 399 newTotal = getCurrentSpace() - oldSpace + newSpace; 400 if (newTotal <= getSpaceLimit()) { 401 updateTimestamp (entry); 402 entry._fValue = value; 403 entry._fSpace = newSpace; 404 this.fCurrentSpace = newTotal; 405 return value; 406 } else { 407 privateRemoveEntry (entry, false); 408 } 409 } 410 if (makeSpace(newSpace)) { 411 privateAdd (key, value, newSpace); 412 } 413 return value; 414 } 415 422 public Object removeKey (Object key) { 423 424 LRUCacheEntry entry = (LRUCacheEntry) fEntryTable.get(key); 425 if (entry == null) { 426 return null; 427 } 428 Object value = entry._fValue; 429 this.privateRemoveEntry (entry, false); 430 return value; 431 } 432 437 public void setSpaceLimit(int limit) { 438 if (limit < fSpaceLimit) { 439 makeSpace(fSpaceLimit - limit); 440 } 441 fSpaceLimit = limit; 442 } 443 446 protected int spaceFor (Object value) { 447 448 if (value instanceof ILRUCacheable) { 449 return ((ILRUCacheable) value).getCacheFootprint(); 450 } else { 451 return 1; 452 } 453 } 454 458 public String toString() { 459 return 460 toStringFillingRation("LRUCache") + toStringContents(); 462 } 463 464 468 protected String toStringContents() { 469 StringBuffer result = new StringBuffer (); 470 int length = fEntryTable.size(); 471 Object [] unsortedKeys = new Object [length]; 472 String [] unsortedToStrings = new String [length]; 473 Enumeration e = this.keys(); 474 for (int i = 0; i < length; i++) { 475 Object key = e.nextElement(); 476 unsortedKeys[i] = key; 477 unsortedToStrings[i] = 478 (key instanceof org.eclipse.jdt.internal.core.JavaElement) ? 479 ((org.eclipse.jdt.internal.core.JavaElement)key).getElementName() : 480 key.toString(); 481 } 482 ToStringSorter sorter = new ToStringSorter(); 483 sorter.sort(unsortedKeys, unsortedToStrings); 484 for (int i = 0; i < length; i++) { 485 String toString = sorter.sortedStrings[i]; 486 Object value = this.get(sorter.sortedObjects[i]); 487 result.append(toString); 488 result.append(" -> "); result.append(value); 490 result.append("\n"); } 492 return result.toString(); 493 } 494 495 public String toStringFillingRation(String cacheName) { 496 StringBuffer buffer = new StringBuffer (cacheName); 497 buffer.append('['); 498 buffer.append(getSpaceLimit()); 499 buffer.append("]: "); buffer.append(NumberFormat.getInstance().format(fillingRatio())); 501 buffer.append("% full"); return buffer.toString(); 503 } 504 505 509 protected void updateTimestamp (LRUCacheEntry entry) { 510 511 entry._fTimestamp = fTimestampCounter++; 512 if (fEntryQueue != entry) { 513 this.privateRemoveEntry (entry, true); 514 this.privateAddEntry (entry, true); 515 } 516 return; 517 } 518 } 519 | Popular Tags |