package util;

/**
 * RTSI.java
 *
 * Created: Wed Jan 24 11:15:02 2001
 *
 */

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.Vector;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

/**
 * This utility class is looking for all the classes implementing or 
 * inheriting from a given interface or class.
 * (RunTime Subclass Identification)
 *
 * @author <a href="mailto:daniel@satlive.org">Daniel Le Berre</a>
 * @version 1.0
 */
public class RTSI {


    /**
     * Display all the classes inheriting or implementing a given
     * class in the currently loaded packages.
     * @param tosubclassname the name of the class to inherit from
     */
    public static void find(String tosubclassname) {
	try {
	    Class tosubclass = Class.forName(tosubclassname);
	    Package [] pcks = Package.getPackages();
	    for (int i=0;i<pcks.length;i++) {
		find(pcks[i].getName(),tosubclass);
	    }
	} catch (ClassNotFoundException ex) {
	    System.err.println("Class "+tosubclassname+" not found!");
	}
    }

    /**
     * Display all the classes inheriting or implementing a given
     * class in a given package.
     * @param pckgname the fully qualified name of the package
     * @param tosubclass the name of the class to inherit from
     */
    public static void find(String pckname, String tosubclassname) {
	try {
	    Class tosubclass = Class.forName(tosubclassname);
	    find(pckname,tosubclass);
	} catch (ClassNotFoundException ex) {
	    System.err.println("Class "+tosubclassname+" not found!");
	}
    }

    /**
     * Display all the classes inheriting or implementing a given
     * class in a given package.
     * @param pckgname the fully qualified name of the package
     * @param tosubclass the Class object to inherit from
     */
    public static void find(String pckgname, Class tosubclass) {
        Vector s = findnames(pckgname, tosubclass);

        for(int i = 0; i < s.size(); i++)
            System.out.println(s.get(i));
    }


    /**
     * Return names of all of the classes inheriting or implementing a
     * given class in a given package.
     * @param pckgname the fully qualified name of the package
     * @param tosubclass the Class object to inherit from
     */
    public static Vector findnames(String pckgname, Class tosubclass) {
    Vector v = new Vector();        
	// Code from JWhich
	// ======
	// Translate the package name into an absolute path
    String name = new String(pckgname);
    if (!name.startsWith("/")) {
        name = "/" + name;
    }	
    name = name.replace('.','/');

  	// Get a File object for the package
  	URL url = RTSI.class.getResource(name);
	// URL url = tosubclass.getResource(name);
	// URL url = ClassLoader.getSystemClassLoader().getResource(name);
	//System.out.println(name+"->"+url);

	// Happens only if the jar file is not well constructed, i.e.
	// if the directories do not appear alone in the jar file like here:
	// 
	//          meta-inf/
	//          meta-inf/manifest.mf
	//          commands/                  <== IMPORTANT
	//          commands/Command.class
	//          commands/DoorClose.class
	//          commands/DoorLock.class
	//          commands/DoorOpen.class
	//          commands/LightOff.class
	//          commands/LightOn.class
	//          RTSI.class
	//
	if (url==null) return null;

	File directory = new File(url.getFile());

	// New code
	// ======
	if (directory.exists()) {
	    // Get the list of the files contained in the package
	    String [] files = directory.list();
	    for (int i=0;i<files.length;i++) {
		
		// we are only interested in .class files
		if (files[i].endsWith(".class")) {
		    // removes the .class extension
		    String classname = files[i].substring(0,files[i].length()-6);
		    try {
			// Try to create an instance of the object
			Object o = Class.forName(pckgname+"."+classname).newInstance();
			if (tosubclass.isInstance(o)) {
                //System.out.println(classname);
			    v.add(classname);
			}
		    } catch (ClassNotFoundException cnfex) {
			System.err.println(cnfex);
		    } catch (InstantiationException iex) {
			// We try to instanciate an interface
			// or an object that does not have a 
			// default constructor
		    } catch (IllegalAccessException iaex) {
			// The class is not public
		    }
		}
	    }
	} else {
	    try {
		// It does not work with the filesystem: we must
		// be in the case of a package contained in a jar file.
		JarURLConnection conn = (JarURLConnection)url.openConnection();
		String starts = conn.getEntryName();
		JarFile jfile = conn.getJarFile();
		Enumeration e = jfile.entries();
		while (e.hasMoreElements()) {
		    ZipEntry entry = (ZipEntry)e.nextElement();
		    String entryname = entry.getName();
		    if (entryname.startsWith(starts)
			&&(entryname.lastIndexOf('/')<=starts.length())
			&&entryname.endsWith(".class")) {
			String classname = entryname.substring(0,entryname.length()-6);
			if (classname.startsWith("/")) 
			    classname = classname.substring(1);
			classname = classname.replace('/','.');
			try {
			    // Try to create an instance of the object
			    Object o = Class.forName(classname).newInstance();
			    if (tosubclass.isInstance(o)) {
                    //System.out.println(classname.substring(classname.lastIndexOf('.')+1));
                    v.add(classname.substring(classname.lastIndexOf('.')+1));
			    }
			} catch (ClassNotFoundException cnfex) {
			    System.err.println(cnfex);
			} catch (InstantiationException iex) {
			    // We try to instanciate an interface
			    // or an object that does not have a 
			    // default constructor
			} catch (IllegalAccessException iaex) {
			    // The class is not public
			}
		    }
		}
	    } catch (IOException ioex) {
		System.err.println(ioex);
	    }	
	}
    
    return v;
    }
    
    public static void main(String []args) {
	if (args.length==2) {
	    find(args[0],args[1]);
	} else {
	    if (args.length==1) {
		find(args[0]);
	    } else {
		System.out.println("Usage: java RTSI [<package>] <subclass>");
	    }
	}
    }
}// RTSI
