/*
 * @(#)FactoryEnumeration.java	1.7 03/01/23
 *
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package workflow.client.factory;

import java.util.List;

import javax.naming.NamingException;

/**
  * The FactoryEnumeration is used for returning factory instances.
  * 
  * @author Rosanna Lee
  * @author Scott Seligman
  * @version 1.7 03/01/23
 */

// no need to implement Enumeration since this is only for internal use
public final class FactoryEnumeration
{
	private List factories;
	private int posn = 0;
	private ClassLoader loader;

	/**
	 * Records the input list and uses it directly to satisfy
	 * hasMore()/next() requests. An alternative would have been to use
	 * an enumeration/iterator from the list, but we want to update the 
	 * list so we keep the
	 * original list. The list initially contains Class objects.
	 * As each element is used, the Class object is replaced by an
	 * instance of the Class itself; eventually, the list contains
	 * only a list of factory instances and no more updates are required.
	 *
	 * <p> Both Class objects and factories are wrapped in weak
	 * references so as not to prevent GC of the class loader.  Each
	 * weak reference is tagged with the factory's class name so the
	 * class can be reloaded if the reference is cleared.
	
	 * @param factories	A non-null list
	 * @param loader	The class loader of the list's contents
	 */
	FactoryEnumeration(List factories, ClassLoader loader)
	{
		this.factories = factories;
		this.loader = loader;
	}

	public Object next() throws NamingException
	{
		synchronized (factories)
		{

			NamedWeakReference ref = (NamedWeakReference) factories.get(posn++);
			Object answer = ref.get();
			if ((answer != null) && !(answer instanceof Class))
			{
				return answer;
			}

			String className = ref.getName();

			try
			{
				if (answer == null)
				{ // reload class if weak ref cleared
					answer = Class.forName(className, true, loader);
				}
				// Instantiate Class to get factory
				answer = ((Class) answer).newInstance();
				ref = new NamedWeakReference(answer, className);
				factories.set(posn - 1, ref); // replace Class object or null
				return answer;
			}
			catch (ClassNotFoundException e)
			{
				NamingException ne = new NamingException("No longer able to load " + className);
				ne.setRootCause(e);
				throw ne;
			}
			catch (InstantiationException e)
			{
				NamingException ne = new NamingException("Cannot instantiate " + answer);
				ne.setRootCause(e);
				throw ne;
			}
			catch (IllegalAccessException e)
			{
				NamingException ne = new NamingException("Cannot access " + answer);
				ne.setRootCause(e);
				throw ne;
			}
		}
	}

	public boolean hasMore()
	{
		synchronized (factories)
		{
			return posn < factories.size();
		}
	}
}
