/*
 * @(#)FactoryEnumeration.java	1.8 03/12/19
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.naming.internal;

import java.util.List;
import javax.naming.NamingException;

/**
  * The FactoryEnumeration is used for returning factory instances.
  * 
  * @author Rosanna Lee
  * @author Scott Seligman
  * @version 1.8 03/12/19
 */

// 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();
	}
    }
}
