import java.io.*;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.jar.*;

/** A class that can find Plugins and other classes in jar files....
 *
 * @author <a href="mailto:robo@digpro.se">Robert Olofsson</a>, Digpro AB
 */
public class PluginFinder {
    
    /** get an iterator of classes with specified type.
     * @param jis the stream to read from.
     * @param type a class that all returned classes can be assigned to (ie a superclass).
     */
    public static Iterator getClassesForType (JarInputStream jis, Class type) {
	return new PIterator (jis, type);
    }

    private static class PIterator implements Iterator {
	private JarInputStream jis;
	private Class nextClass;
	private Class matcher; 
	
	public PIterator (JarInputStream jis, Class matcher) {
	    this.jis = jis;
	    this.matcher = matcher;
	    findNext ();
	}
	
	public boolean hasNext () {
	    return nextClass != null;
	}

	public Object next () {
	    if (nextClass == null)
		throw new NoSuchElementException ("Unable to find more plugins");
	    Class r = nextClass;
	    findNext ();
	    return r;
	}

	private void findNext () {
	    JarEntry je;
	    try {
		while ((je = jis.getNextJarEntry ()) != null) {
		    if (je.isDirectory ())
			continue;
		    String name = je.getName ();
		    int i = name.indexOf ('$');
		    if (i >= 0)
			continue;
		
		    i = name.lastIndexOf (".class");
		    if (i < 0)
			continue;
		
		    name = name.substring (0, i);
		    String className = name.replace ('/', '.');
		    try {
			Class cls = Class.forName (className);
			if (matcher.isAssignableFrom (cls) && 
			    !Modifier.isAbstract (cls.getModifiers ())) {
			    nextClass = cls;
			    return;
			}
		    } catch (ClassNotFoundException e) {
			System.out.println ("ClassNotFoundException while loading " + name);
		    } catch (NoClassDefFoundError e) {
			System.out.println ("NoClassDefFoundError while loading " + name);
		    } catch (java.security.AccessControlException e) {
			// some classes tries to read system variables etc...
			System.out.println ("AccessControlException while loading " + name);
		    } catch (ExceptionInInitializerError e) {
			System.out.println ("ExceptionInInitializerError while loading " + name);
		    }
		}
		nextClass = null;
	    } catch (IOException e) {
		e.printStackTrace ();
		nextClass = null;
	    }
	}

	public void remove () {
	    throw new RuntimeException ("remove is not implemented..");
	}
    }
}
