import java.io.*;
import java.util.StringTokenizer;

/**
 * <code>JWhich</code> is a utility that takes a Java class name 
 * and displays the absolute pathname of the class file that would 
 * be loaded first by the class loader, as prescribed by the
 * class path.
 * <p>
 * <code>JWhich</code> also validates the class path and reports
 * any non-existent or invalid class path entries.
 * <p>
 * Usage is similar to the UNIX <code>which</code> command.
 * <p>
 * Example uses:
 * <p>
 * <blockquote>
 * 		To find the absolute pathname of <code>MyClass.class</code>
 *		not in a package:
 *		<pre>java JWhich MyClass</pre>
 *
 * 		To find the absolute pathname of <code>MyClass.class</code>
 *		in the <code>my.package</code> package:
 *		<pre>java JWhich my.package.MyClass</pre>
 *
 * 		To validate the class path and report any non-existent 
 *		or invalid class path entries:
 *		<pre>java JWhich -validate</pre>
 *		
 *		To validate the class path and then find the absolute
 *		pathname of <code>MyClass.class</code> in the 
 *		<code>my.package</code> package:
 *		<pre>java JWhich -validate my.package.MyClass</pre>
 * </blockquote>
 *
 * @author <a href="mailto:mike@clarkware.com">Mike Clark</a>
 * @author <a href="http://www.clarkware.com">Clarkware Consulting</a>
 */

public class JWhich {
	
	private static String _classpath;

	/**
	 * Prints the absolute pathname of the class file 
	 * containing the specified class name, as prescribed
	 * by the class path.
	 *
	 * @param className Name of the class.
	 */
	public static void which(String className) {

		//
		// Format the file name into a valid resource name.
		//
		if (!className.startsWith("/")) {
			className = "/" + className;
		}
		className = className.replace('.', '/');
		className = className + ".class";

		//
		// Attempt to locate the file using the class loader.
		//
		java.net.URL classUrl = 
			new JWhich().getClass().getResource(className);
		
		if (classUrl == null) {
			System.out.println("\nClass '" + className + 
				"' not found in \n'" + getClasspath() + "'");
		} else {
			System.out.println("\nClass '" + className + 
				"' found in \n'" + classUrl.getFile() + "'");
		}
	}

	/**
	 * Validates the class path and reports any non-existent
	 * or invalid class path entries.
	 * <p>
	 * Valid class path entries include directories, <code>.zip</code> 
	 * files, and <code>.jar</code> files.
	 */
	public static void validate() {
		
		StringTokenizer tokenizer = 
			new StringTokenizer(getClasspath(), File.pathSeparator);
		
		System.out.println("Validating " + getClasspath());
		
		while (tokenizer.hasMoreTokens()) {
			String element = tokenizer.nextToken();
			File f = new File(element);
			//
			// Existence test.
			//
			if (!f.exists()) {

				System.out.println("\n'" + element + "' " +
					"does not exist.");

			} 
			//
			// Validity test.
			//
			else if ( (!f.isDirectory()) && 
					  (!element.endsWith(".jar")) &&
					  (!element.endsWith(".zip")) ) {

				System.out.println("\n'" + element + "' " +
					"is not a directory, .jar file, or .zip file.");

			}
		}
	}

	/**
	 * Sets the class path.
	 *
	 * @param classpath Class path.
	 */
	public static void setClasspath(String classpath) {
		_classpath = classpath;
	}

	/**
	 * Returns the class path.
	 *
	 * @return Class path.
	 */
	protected static String getClasspath() {
		if (_classpath == null) {
			setClasspath(System.getProperty("java.class.path"));
		}

		return _classpath;
	}

	/*
	 * Entry point which parses and handles the 
	 * command-line arguments.
	 * <p>
	 * @param args Command-line arguments.
	 */
	private static void instanceMain(String[] args) {

		if (args.length == 0) {
			printUsage();
		}

		for (int cmdIndex = 0; cmdIndex < args.length; cmdIndex++) {

			String cmd = args[cmdIndex];

			if ("-validate".equals(cmd)) {
				validate();
			} else if ("-help".equals(cmd)) {
				printUsage();
			} else {
				which(cmd);
			}
		}
	}
		
	/*
	 * Prints usage information.
	 */
	private static void printUsage() {

		System.out.println("\nSyntax: java JWhich [options] className");
		System.out.println("");
		System.out.println("where options include:");
		System.out.println("");
		System.out.println("\t-validate Validates the class path.");	
		System.out.println("\t-help     Prints usage information.");	
		System.out.println("");		
		System.out.println("Examples:");	
		System.out.println("\tjava JWhich MyClass");
		System.out.println("\tjava JWhich my.package.MyClass");
		System.out.println("\tjava JWhich -validate");
		System.out.println("\tjava JWhich -validate my.package.MyClass");
		System.exit(0);
	}

	/**
	 * Main.
	 */
	public static void main(String args[]) {
		JWhich.instanceMain(args);
	}
}
