public class

IntLRUCache

extends Object
package org.andengine.util.adt.cache;

import org.andengine.util.adt.pool.GenericPool;

import android.util.SparseArray;

/**
 * (c) Zynga 2011
 *
 * @author Nicolas Gramlich <ngramlich@zynga.com>
 * @since 12:19:22 - 08.12.2011
 */
public class IntLRUCache<V> {
	// ===========================================================
	// Constants
	// ===========================================================

	// ===========================================================
	// Fields
	// ===========================================================

	private final int mCapacity;
	private int mSize;

	private final SparseArray<IntLRUCacheValueHolder<V>> mSparseArray;
	private final IntLRUCacheQueue mIntLRUCacheQueue;

	private final GenericPool<IntLRUCacheValueHolder<V>> mIntLRUCacheValueHolderPool = new GenericPool<IntLRUCache.IntLRUCacheValueHolder<V>>() {
		@Override
		protected IntLRUCacheValueHolder<V> onAllocatePoolItem() {
			return new IntLRUCacheValueHolder<V>();
		}

		@Override
		protected void onHandleRecycleItem(final IntLRUCacheValueHolder<V> pIntLRUCacheValueHolder) {
			pIntLRUCacheValueHolder.mIntLRUCacheQueueNode = null;
			pIntLRUCacheValueHolder.mValue = null;
		}
	};

	// ===========================================================
	// Constructors
	// ===========================================================

	public IntLRUCache(final int pCapacity) {
		this.mCapacity = pCapacity;
		this.mSparseArray = new SparseArray<IntLRUCacheValueHolder<V>>(pCapacity);
		this.mIntLRUCacheQueue = new IntLRUCacheQueue();
	}

	// ===========================================================
	// Getter & Setter
	// ===========================================================

	public int getCapacity() {
		return this.mCapacity;
	}

	public int getSize() {
		return this.mSize;
	}

	// ===========================================================
	// Methods for/from SuperClass/Interfaces
	// ===========================================================

	public boolean isEmpty() {
		return this.mSize == 0;
	}

	public V put(final int pKey, final V pValue) {
		final IntLRUCacheValueHolder<V> existingIntLRUCacheValueHolder = this.mSparseArray.get(pKey);
		if(existingIntLRUCacheValueHolder != null) {
			/* Just heat up that item. */
			this.mIntLRUCacheQueue.moveToTail(existingIntLRUCacheValueHolder.mIntLRUCacheQueueNode);

			return existingIntLRUCacheValueHolder.mValue;
		}

		if(this.mSize >= this.mCapacity) {
			final int deadKey = this.mIntLRUCacheQueue.poll();
			this.mSparseArray.remove(deadKey);
			this.mSize--;
		}

		final IntLRUCacheQueueNode IntLRUCacheQueueNode = this.mIntLRUCacheQueue.add(pKey);

		final IntLRUCacheValueHolder<V> IntLRUCacheValueHolder = this.mIntLRUCacheValueHolderPool.obtainPoolItem();
//		final IntLRUCacheValueHolder<V> IntLRUCacheValueHolder = new IntLRUCacheValueHolder<V>();
		IntLRUCacheValueHolder.mValue = pValue;
		IntLRUCacheValueHolder.mIntLRUCacheQueueNode = IntLRUCacheQueueNode;

		this.mSparseArray.put(pKey, IntLRUCacheValueHolder);

		this.mSize++;

		return null;
	}

	public V get(final int pKey) {
		final IntLRUCacheValueHolder<V> IntLRUCacheValueHolder = this.mSparseArray.get(pKey);
		if(IntLRUCacheValueHolder == null) {
			return null;
		}

		this.mIntLRUCacheQueue.moveToTail(IntLRUCacheValueHolder.mIntLRUCacheQueueNode);

		return IntLRUCacheValueHolder.mValue;
	}

	public void clear() {
		while(!this.mIntLRUCacheQueue.isEmpty()) {
			final int key = this.mIntLRUCacheQueue.poll();
			final IntLRUCacheValueHolder<V> lruCacheValueHolder = this.mSparseArray.get(key);
			if(lruCacheValueHolder == null) {
				throw new IllegalArgumentException();
			}
			this.mSparseArray.remove(key);
			this.mIntLRUCacheValueHolderPool.recyclePoolItem(lruCacheValueHolder);
		}
		this.mSize = 0;
	}

	// ===========================================================
	// Methods
	// ===========================================================

	// ===========================================================
	// Inner and Anonymous Classes
	// ===========================================================

	static class IntLRUCacheQueueNode {
		// ===========================================================
		// Constants
		// ===========================================================

		// ===========================================================
		// Fields
		// ===========================================================

		int mKey;
		IntLRUCacheQueueNode mPrevious;
		IntLRUCacheQueueNode mNext;

		// ===========================================================
		// Constructors
		// ===========================================================

		// ===========================================================
		// Getter & Setter
		// ===========================================================

		// ===========================================================
		// Methods for/from SuperClass/Interfaces
		// ===========================================================

		// ===========================================================
		// Methods
		// ===========================================================

		// ===========================================================
		// Inner and Anonymous Classes
		// ===========================================================
	}

	static class IntLRUCacheValueHolder<V> {
		// ===========================================================
		// Constants
		// ===========================================================

		// ===========================================================
		// Fields
		// ===========================================================

		V mValue;
		IntLRUCacheQueueNode mIntLRUCacheQueueNode;

		// ===========================================================
		// Constructors
		// ===========================================================

		// ===========================================================
		// Getter & Setter
		// ===========================================================

		// ===========================================================
		// Methods for/from SuperClass/Interfaces
		// ===========================================================

		// ===========================================================
		// Methods
		// ===========================================================

		// ===========================================================
		// Inner and Anonymous Classes
		// ===========================================================
	}

	static class IntLRUCacheQueue {
		// ===========================================================
		// Constants
		// ===========================================================

		// ===========================================================
		// Fields
		// ===========================================================

		private IntLRUCacheQueueNode mHead;
		private IntLRUCacheQueueNode mTail;

		private final GenericPool<IntLRUCacheQueueNode> mIntLRUCacheQueueNodePool = new GenericPool<IntLRUCache.IntLRUCacheQueueNode>() {
			@Override
			protected IntLRUCacheQueueNode onAllocatePoolItem() {
				return new IntLRUCacheQueueNode();
			}

			@Override
			protected void onHandleRecycleItem(final IntLRUCacheQueueNode pIntLRUCacheQueueNode) {
				pIntLRUCacheQueueNode.mKey = 0;
				pIntLRUCacheQueueNode.mPrevious = null;
				pIntLRUCacheQueueNode.mNext = null;
			}
		};

		// ===========================================================
		// Constructors
		// ===========================================================

		// ===========================================================
		// Getter & Setter
		// ===========================================================

		// ===========================================================
		// Methods for/from SuperClass/Interfaces
		// ===========================================================

		// ===========================================================
		// Methods
		// ===========================================================

		public boolean isEmpty() {
			return this.mHead == null;
		}

		public IntLRUCacheQueueNode add(final int pKey) {
			final IntLRUCacheQueueNode IntLRUCacheQueueNode = this.mIntLRUCacheQueueNodePool.obtainPoolItem();
//			final IntLRUCacheQueueNode IntLRUCacheQueueNode = new IntLRUCacheQueueNode();
			IntLRUCacheQueueNode.mKey = pKey;

			return this.add(IntLRUCacheQueueNode);
		}

		private IntLRUCacheQueueNode add(final IntLRUCacheQueueNode pIntLRUCacheQueueNode) {
			if(this.isEmpty()) {
				this.mHead = pIntLRUCacheQueueNode;
				this.mTail = this.mHead;
			} else {
				this.mTail.mNext = pIntLRUCacheQueueNode;
				pIntLRUCacheQueueNode.mPrevious = this.mTail;
				this.mTail = pIntLRUCacheQueueNode;
			}

			return this.mTail;
		}

		public int poll() {
			final IntLRUCacheQueueNode head = this.mHead;
			final int key = this.mHead.mKey;
			if(key == 0) {
				throw new IllegalStateException();
			}

			/* Check if item to poll is the tail. */
			if(this.mHead.mNext == null) {
				this.mHead = null;
				this.mTail = null;
			} else {
				this.mHead = this.mHead.mNext;
				this.mHead.mPrevious = null;
			}

			this.mIntLRUCacheQueueNodePool.recyclePoolItem(head);
			return key;
		}

		public void moveToTail(final IntLRUCacheQueueNode pIntLRUCacheQueueNode) {
			final IntLRUCacheQueueNode next = pIntLRUCacheQueueNode.mNext;

			/* Check if the node already is the tail. */
			if(next == null) {
				return;
			} else {
				final IntLRUCacheQueueNode previous = pIntLRUCacheQueueNode.mPrevious;
				next.mPrevious = previous;

				/* Check if item to bump is the head. */
				if(previous == null) {
					this.mHead = next;
				} else {
					previous.mNext = next;
				}

				this.mTail.mNext = pIntLRUCacheQueueNode;
				pIntLRUCacheQueueNode.mPrevious = this.mTail;
				pIntLRUCacheQueueNode.mNext = null;
				this.mTail = pIntLRUCacheQueueNode;
			}
		}

		// ===========================================================
		// Inner and Anonymous Classes
		// ===========================================================
	}
}