/* 
 * E-XML Library:  For XML, XML-RPC, HTTP, and related.
 * Copyright (C) 2002-2008  Elias Ross
 * 
 * genman@noderunner.net
 * http://noderunner.net/~genman
 * 
 * 1025 NE 73RD ST
 * SEATTLE WA 98115
 * USA
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * $Id$
 */

package net.noderunner.exml;

/**
 * An object pool in which objects must be returned in the order that they were
 * checked out.
 */
class ObjectPool {

	private Object items[];
	private Class prototype;
	private int at;
	private int initialSize;

	/** 
	 * Constructs an object pool using a class, which will be used to
	 * create new instances.
	 * @param prototype an instantiable class that has a default constructor
	 * @param initialSize number of objects to allocate at first
	 * @throws IllegalArgumentException if the size is invalid or the
	 * prototype class cannot be instantiated
	 */
	public ObjectPool(Class prototype, int initialSize) {
		if (initialSize <= 0)
			throw new IllegalArgumentException("Invalid initial size: " + initialSize);
		if (prototype == null)
			throw new IllegalArgumentException("Null propotype");
		this.initialSize = initialSize;
		this.prototype = prototype;
		at = 0;
		items = new Object[initialSize];
		for (int i = 0; i < items.length; i++)
			items[i] = newObject();
	}

	private Object newObject() {
		try {
			return prototype.newInstance();
		} catch (InstantiationException e) {
			throw new IllegalArgumentException("Cannot instantiate " + prototype + ": " + e);
		} catch (IllegalAccessException e) {
			throw new IllegalArgumentException("Cannot access " + prototype + ": " + e);
		}
	}

	private void expandIfNeeded() {
		if (at == items.length) {
			Object newItems[] = new Object[items.length * 2];
			System.arraycopy(items, 0, newItems, 0, items.length);
			for (int i = items.length; i < newItems.length; i++)
				newItems[i] = newObject();
			items = newItems;
		}
	}

	private void shrinkIfNeeded() {
		if (items.length > initialSize && at < items.length / 2) {
			Object newItems[];
			if (at <= initialSize / 2)
				newItems = new Object[initialSize];
			else
				newItems = new Object[items.length / 2];
			System.arraycopy(items, 0, newItems, 0, newItems.length);
			items = newItems;
		}
	}

	public Object checkOut() {
		expandIfNeeded();
		return items[at++];
	}

	/**
	 * Returns the last object returned by the checkOut() method.
	 */
	public Object checkIn() {
		if (at == 0)
			throw new IllegalStateException("Cannot check in more than available");
		Object last = items[--at];
		shrinkIfNeeded();
		return last;
	}

	/**
	 * Returns all objects to the pool.
	 */
	public void clear() {
		at = 0;
		shrinkIfNeeded();
	}

	public String toString() 
	{
		StringBuffer sb = new StringBuffer(items.length * 8);
		sb.append("ObjectPool out=[");
		for (int i = 0; i < at; i++) {
			sb.append(items[i]);
			sb.append(' ');
		}
		sb.append("] capacity=" + items.length);
		return sb.toString();
	}

}
