public abstract class

GenericPool

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

import java.util.ArrayList;
import java.util.Collections;

import org.andengine.BuildConfig;
import org.andengine.util.debug.Debug;

/**
 * (c) 2010 Nicolas Gramlich
 * (c) 2011 Zynga Inc.
 * 
 * @author Valentin Milea
 * @author Nicolas Gramlich
 * 
 * @since 22:19:55 - 31.08.2010
 */
public abstract class GenericPool<T> {
	// ===========================================================
	// Constants
	// ===========================================================

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

	private final ArrayList<T> mAvailableItems;
	private final int mGrowth;
	private final int mAvailableItemCountMaximum;

	private int mUnrecycledItemCount;

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

	public GenericPool() {
		this(0);
	}

	public GenericPool(final int pInitialSize) {
		this(pInitialSize, 1);
	}

	public GenericPool(final int pInitialSize, final int pGrowth) {
		this(pInitialSize, pGrowth, Integer.MAX_VALUE);
	}

	public GenericPool(final int pInitialSize, final int pGrowth, final int pAvailableItemsMaximum) {
		if(pGrowth <= 0) {
			throw new IllegalArgumentException("pGrowth must be greater than 0!");
		}
		if(pAvailableItemsMaximum < 0) {
			throw new IllegalArgumentException("pAvailableItemsMaximum must be at least 0!");
		}

		this.mGrowth = pGrowth;
		this.mAvailableItemCountMaximum = pAvailableItemsMaximum;
		this.mAvailableItems = new ArrayList<T>(pInitialSize);

		if(pInitialSize > 0) {
			this.batchAllocatePoolItems(pInitialSize);
		}
	}

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

	public synchronized int getUnrecycledItemCount() {
		return this.mUnrecycledItemCount;
	}

	public synchronized int getAvailableItemCount() {
		return this.mAvailableItems.size();
	}

	public int getAvailableItemCountMaximum() {
		return this.mAvailableItemCountMaximum;
	}

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

	protected abstract T onAllocatePoolItem();

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

	/**
	 * @param pItem every item passes this method just before it gets recycled.
	 */
	protected void onHandleRecycleItem(final T pItem) {

	}

	protected T onHandleAllocatePoolItem() {
		return this.onAllocatePoolItem();
	}

	/**
	 * @param pItem every item that was just obtained from the pool, passes this method.
	 */
	protected void onHandleObtainItem(final T pItem) {

	}

	public synchronized void batchAllocatePoolItems(final int pCount) {
		final ArrayList<T> availableItems = this.mAvailableItems;

		int allocationCount = this.mAvailableItemCountMaximum - availableItems.size();
		if(pCount < allocationCount) {
			allocationCount = pCount;
		}

		for(int i = allocationCount - 1; i >= 0; i--) {
			availableItems.add(this.onHandleAllocatePoolItem());
		}
	}

	public synchronized T obtainPoolItem() {
		final T item;

		if(this.mAvailableItems.size() > 0) {
			item = this.mAvailableItems.remove(this.mAvailableItems.size() - 1);
		} else {
			if(this.mGrowth == 1 || this.mAvailableItemCountMaximum == 0) {
				item = this.onHandleAllocatePoolItem();
			} else {
				this.batchAllocatePoolItems(this.mGrowth);
				item = this.mAvailableItems.remove(this.mAvailableItems.size() - 1);
			}
			if(BuildConfig.DEBUG) {
				Debug.v(this.getClass().getName() + "<" + item.getClass().getSimpleName() +"> was exhausted, with " + this.mUnrecycledItemCount + " item not yet recycled. Allocated " + this.mGrowth + " more.");
			}
		}
		this.onHandleObtainItem(item);

		this.mUnrecycledItemCount++;
		return item;
	}

	public synchronized void recyclePoolItem(final T pItem) {
		if(pItem == null) {
			throw new IllegalArgumentException("Cannot recycle null item!");
		}

		this.onHandleRecycleItem(pItem);

		if(this.mAvailableItems.size() < this.mAvailableItemCountMaximum) {
			this.mAvailableItems.add(pItem);
		}

		this.mUnrecycledItemCount--;

		if(this.mUnrecycledItemCount < 0) {
			Debug.e("More items recycled than obtained!");
		}
	}

	public synchronized void shufflePoolItems() {
		Collections.shuffle(this.mAvailableItems);
	}

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