//----------------------------BEGIN LICENSE----------------------------
/* 
 * Willow : the Open Source WorkFlow Project
 * Distributable under GNU LGPL license by gun.org
 *
 * Copyright (C) 2004-2010 huihoo.org 
 * Copyright (C) 2004-2010  ZosaTapo <dertyang@hotmail.com> 
 * 
 * ====================================================================
 * Project Homepage : http://www.huihoo.org/willow
 * Source Forge     : http://sourceforge.net/projects/huihoo
 * Mailing list     : willow@lists.sourceforge.net 
 */
//----------------------------END  LICENSE----------------------------
package org.huihoo.workflow.desktop.helpers;

import java.util.List;

import org.huihoo.workflow.WorkflowException;

// 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  WorkflowException
	{
		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)
			{
				WorkflowException ne = new WorkflowException("No longer able to load " + className);
				ne.setRootCause(e);
				throw ne;
			}
			catch (InstantiationException e)
			{
				WorkflowException ne = new WorkflowException("Cannot instantiate " + answer);
				ne.setRootCause(e);
				throw ne;
			}
			catch (IllegalAccessException e)
			{
				WorkflowException ne = new WorkflowException("Cannot access " + answer);
				ne.setRootCause(e);
				throw ne;
			}
		}
	}

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

