blob: 3947975ea78779a526c6fd2642758251cc044b6b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2019 Xored Software Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Xored Software Inc - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.rcptt.internal.core.model.cache;
import java.util.Enumeration;
public abstract class OverflowingLRUCache extends LRUCache {
protected int fOverflow = 0;
protected boolean fTimestampsOn = true;
protected double fLoadFactor = 0.333;
public OverflowingLRUCache(int size) {
this(size, 0);
}
public OverflowingLRUCache(int size, int overflow) {
super(size);
fOverflow = overflow;
}
public Object clone() {
OverflowingLRUCache newCache = (OverflowingLRUCache) newInstance(
fSpaceLimit, fOverflow);
LRUCacheEntry qEntry;
/* Preserve order of entries by copying from oldest to newest */
qEntry = this.fEntryQueueTail;
while (qEntry != null) {
newCache.privateAdd(qEntry._fKey, qEntry._fValue, qEntry._fSpace);
qEntry = qEntry._fPrevious;
}
return newCache;
}
protected abstract boolean close(Object key, Object value);
@SuppressWarnings("rawtypes")
public Enumeration elements() {
if (fEntryQueue == null)
return new LRUCacheEnumerator(null);
LRUCacheEnumerator.LRUEnumeratorElement head = new LRUCacheEnumerator.LRUEnumeratorElement(
fEntryQueue._fValue);
LRUCacheEntry currentEntry = fEntryQueue._fNext;
LRUCacheEnumerator.LRUEnumeratorElement currentElement = head;
while (currentEntry != null) {
currentElement.fNext = new LRUCacheEnumerator.LRUEnumeratorElement(
currentEntry._fValue);
currentElement = currentElement.fNext;
currentEntry = currentEntry._fNext;
}
return new LRUCacheEnumerator(head);
}
public double fillingRatio() {
return (fCurrentSpace + fOverflow) * 100.0 / fSpaceLimit;
}
@SuppressWarnings("rawtypes")
public java.util.Hashtable getEntryTable() {
return fEntryTable;
}
public double getLoadFactor() {
return fLoadFactor;
}
public int getOverflow() {
return fOverflow;
}
protected boolean makeSpace(int space) {
int limit = fSpaceLimit;
if (fOverflow == 0 && fCurrentSpace + space <= limit) {
/* if space is already available */
return true;
}
/* Free up space by removing oldest entries */
int spaceNeeded = (int) ((1 - fLoadFactor) * limit);
spaceNeeded = (spaceNeeded > space) ? spaceNeeded : space;
LRUCacheEntry entry = fEntryQueueTail;
try {
// disable timestamps update while making space so that the previous
// and next links are not changed
// (by a call to get(Object) for example)
fTimestampsOn = false;
while (fCurrentSpace + spaceNeeded > limit && entry != null) {
this.privateRemoveEntry(entry, false, false);
entry = entry._fPrevious;
}
} finally {
fTimestampsOn = true;
}
/* check again, since we may have aquired enough space */
if (fCurrentSpace + space <= limit) {
fOverflow = 0;
return true;
}
/* update fOverflow */
fOverflow = fCurrentSpace + space - limit;
return false;
}
protected abstract LRUCache newInstance(int size, int overflow);
@SuppressWarnings({ "rawtypes", "unchecked" })
public void printStats() {
LRUCacheEntry entry = fEntryQueue;
while (entry != null) {
entry = entry._fNext;
}
entry = fEntryQueueTail;
while (entry != null) {
entry = entry._fPrevious;
}
Enumeration keys = fEntryTable.keys();
class Temp {
public Class fClass;
public int fCount;
public Temp(Class aClass) {
fClass = aClass;
fCount = 1;
}
public String toString() {
return "Class: " + fClass + " has " + fCount + " entries."; //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-1$
}
}
java.util.HashMap h = new java.util.HashMap();
while (keys.hasMoreElements()) {
entry = (LRUCacheEntry) fEntryTable.get(keys.nextElement());
Class key = entry._fValue.getClass();
Temp t = (Temp) h.get(key);
if (t == null) {
h.put(key, new Temp(key));
} else {
t.fCount++;
}
}
}
protected void privateRemoveEntry(LRUCacheEntry entry, boolean shuffle) {
privateRemoveEntry(entry, shuffle, true);
}
protected void privateRemoveEntry(LRUCacheEntry entry, boolean shuffle,
boolean external) {
if (!shuffle) {
if (external) {
fEntryTable.remove(entry._fKey);
fCurrentSpace -= entry._fSpace;
privateNotifyDeletionFromCache(entry);
} else {
if (!close(entry._fKey, entry._fValue))
return;
// buffer close will recursively call #privateRemoveEntry with
// external==true
// thus entry will already be removed if reaching this point.
if (fEntryTable.get(entry._fKey) == null) {
return;
} else {
// basic removal
fEntryTable.remove(entry._fKey);
fCurrentSpace -= entry._fSpace;
privateNotifyDeletionFromCache(entry);
}
}
}
LRUCacheEntry previous = entry._fPrevious;
LRUCacheEntry next = entry._fNext;
/* if this was the first entry */
if (previous == null) {
fEntryQueue = next;
} else {
previous._fNext = next;
}
/* if this was the last entry */
if (next == null) {
fEntryQueueTail = previous;
} else {
next._fPrevious = previous;
}
}
public Object put(Object key, Object value) {
/* attempt to rid ourselves of the overflow, if there is any */
if (fOverflow > 0)
shrink();
/* Check whether there's an entry in the cache */
int newSpace = spaceFor(value);
LRUCacheEntry entry = (LRUCacheEntry) fEntryTable.get(key);
if (entry != null) {
privateRemoveEntry(entry, false, false);
// close(key, value);
// return entry._fValue;
}
// attempt to make new spaceG
makeSpace(newSpace);
// add without worring about space, it will
// be handled later in a makeSpace call
privateAdd(key, value, newSpace);
return value;
}
public Object remove(Object key) {
return removeKey(key);
}
public void setLoadFactor(double newLoadFactor)
throws IllegalArgumentException {
if (newLoadFactor <= 1.0 && newLoadFactor > 0.0)
fLoadFactor = newLoadFactor;
else
throw new IllegalArgumentException("Invalid load factor");
}
public void setSpaceLimit(int limit) {
if (limit < fSpaceLimit) {
makeSpace(fSpaceLimit - limit);
}
fSpaceLimit = limit;
}
public boolean shrink() {
if (fOverflow > 0)
return makeSpace(0);
return true;
}
public String toString() {
return toStringFillingRation("OverflowingLRUCache ") + //$NON-NLS-1$
toStringContents();
}
protected void updateTimestamp(LRUCacheEntry entry) {
if (fTimestampsOn) {
entry._fTimestamp = fTimestampCounter++;
if (fEntryQueue != entry) {
this.privateRemoveEntry(entry, true);
this.privateAddEntry(entry, true);
}
}
}
}