import java.lang.reflect.*; import java.util.*; public class ShowListenerMethods { protected static boolean verbose = false; protected static String beanClassName; // e.g. "java.awt.Component" protected static String beanPackageName;// e.g. "java.awt" //------------------------------------------------------------------- // ShowListenerMethods Constructor //------------------------------------------------------------------- public ShowListenerMethods(Class beanClass) { Method[] allPublicMethods; Hashtable addRemoves, remainingMethods; // find bean's public methods allPublicMethods = beanClass.getMethods(); // extract all methods starting with add/remove and ending with Listener addRemoves = extractAddRemoves(allPublicMethods); // for all keys, find matching remove method. Print these remainingMethods = listMatchingPairs(addRemoves); // dump remaining methods (these can be misspelled method names) System.out.println("----------------------------------------------"); listLonelyMethods(remainingMethods); } //------------------------------------------------------------------- // find all methods that fit addXXXListener or removeXXXListener sig //------------------------------------------------------------------- protected Hashtable extractAddRemoves(Method[] methods) { Method aMethod; String methodName; Hashtable wantedMethods; // maps method names to Method objects wantedMethods = new Hashtable(); for (int i=0; i < methods.length; i++) { aMethod = methods[i]; methodName = aMethod.getName(); if ( methodName.endsWith("Listener") ) { if ( methodName.startsWith("add") || methodName.startsWith("remove") ) { // record this method in our hashtable database wantedMethods.put(methodName, aMethod); } } } // endfor return wantedMethods; } //------------------------------------------------------------------- // List all matching pairs of add/remove-XXXListener() methods and // remove those pairs from the database. //------------------------------------------------------------------- protected Hashtable listMatchingPairs(Hashtable methods) { String adder, remover; Method addMethod, removeMethod; Enumeration methodIterator = methods.keys(); while ( methodIterator.hasMoreElements() ) { adder = (String) methodIterator.nextElement(); if ( adder.startsWith("add") ) { addMethod = (Method) methods.get(adder); remover = "remove" + adder.substring(3); // find matching "removeXXXListener()" removeMethod = (Method) methods.get(remover); if ( removeMethod != null ) { if ( isMatchingPair(addMethod, removeMethod)) { listEventInfo (addMethod, removeMethod); // remove pair from database methods.remove(adder); methods.remove(remover); } } } // end if starts with add } // end while return methods; } //------------------------------------------------------------------- // We identified a matching add/remove pair, so print pair. // If in verbose mode, check the associated Listener interface and // print its methods (to show event set). //------------------------------------------------------------------- protected void listEventInfo(Method add, Method rem) { String addStr, remStr; System.out.println("----------------------------------------------"); // get the full method signatures in String format // this is of the form "modifiers className.methodName(params) addStr = add.toString(); remStr = rem.toString(); // strip all superfluous strings (in the hope of fitting Str on 1 line) addStr = shortenMethodSig(addStr, beanPackageName); remStr = shortenMethodSig(remStr, beanPackageName); // print compact method names System.out.println(addStr); System.out.println(remStr); Class listenerInterface = add.getParameterTypes()[0]; if ( verbose ) { listListenerInterfaceMethods(listenerInterface); } } //------------------------------------------------------------------- // After we found all matching pairs, the remaining methods are // listed to highlight possibly misspelled add/remove method names. //------------------------------------------------------------------- protected void listLonelyMethods(Hashtable methods) { String method; if ( methods.isEmpty() ) { System.out.println("All add/removeXXXListener methods accounted for."); return; } System.out.println("WARNING: These add/remove methods could not be matched:"); Enumeration methodIterator = methods.keys(); while ( methodIterator.hasMoreElements() ) { method = (String) methodIterator.nextElement(); System.out.println(method); } } //------------------------------------------------------------------- // Determine if an addXXXListener() really matches a removeXXXListener() // Check complete signature for equality. //------------------------------------------------------------------- protected boolean isMatchingPair(Method add, Method rem) { Class[] addParamList, remParamList; // add/remove methods need void return types if ( add.getReturnType() != void.class || rem.getReturnType() != void.class ) { return false; } // add/remove methods need identical parameter lists of a single // Listener addParamList = add.getParameterTypes(); remParamList = rem.getParameterTypes(); // both need a single Listener argument if (addParamList.length != 1 || remParamList.length != 1) { return false; } if ( ! addParamList[0].equals(remParamList[0]) ) { return false; } if ( ! isListenerClass(addParamList[0]) ) { return false; } return true; } //------------------------------------------------------------------- // Check whether a type (ie a Class object) is an EventListener or not //------------------------------------------------------------------- protected boolean isListenerClass(Class someClass) { Class eventListenerClass = EventListener.class; Class[] itsInterfaces; itsInterfaces = someClass.getInterfaces(); for (int i=0; i < itsInterfaces.length; i++) { if ( itsInterfaces[i].equals(eventListenerClass) ) { return true; } } return false; } //------------------------------------------------------------------- // Turn a verbose method signature string obtained from Method.toString() // into a more compact string (so it fits in 80 columns). //------------------------------------------------------------------- protected String shortenMethodSig(String signature, String beanPackageName) { // chop off the "public" modifier (we know add/remove are public) signature = signature.substring(7); // similarly, remove "synchronized" modifier // similarly, remove "abstract" modifier signature = removeStartString(signature, "synchronized "); signature = removeStartString(signature, "abstract "); // if there are 1 or 2 "java.awt.event." substrings in there: delete // (we know the java.awt.event event types by heart) signature = removeSubstring(signature, "java.awt.event."); signature = removeSubstring(signature, "java.awt.event."); // if there's a "java.awt." substring in there: delete it // (we know java.awt classes by heart) signature = removeSubstring(signature, "java.awt."); // remove bean's package name // e.g. "java.awt.Component" becomes "Component" if ( beanPackageName.length() > 0 ) { signature = removeSubstring(signature, beanPackageName + "."); } return signature; } //------------------------------------------------------------------- // Display all the event methods of a listener interface. //------------------------------------------------------------------- protected void listListenerInterfaceMethods(Class listenerInterface) { Method[] methods; System.out.println("This means the following events can be fired:"); methods = listenerInterface.getMethods(); for (int i=0; i < methods.length; i++) { String methodSignature = methods[i].toString(); String packageName = packageName(listenerInterface.getName()); methodSignature = shortenMethodSig( methodSignature, packageName); System.out.println("\t" + methodSignature); } } //------------------------------------------------------------------- // Utility method to isolate a class' package name //------------------------------------------------------------------- public static String packageName (String fullClassName) { int lastDotIndex; lastDotIndex = fullClassName.lastIndexOf("."); if ( lastDotIndex == -1 ) { return ""; } else { return fullClassName.substring(0, lastDotIndex); } } //------------------------------------------------------------------- protected static void usage() { System.out.println("Usage: ShowListenerMethods [-v] "); System.exit(10); } //------------------------------------------------------------------- // ShowListenerMethods main() program entry point. //------------------------------------------------------------------- public static void main (String[] args) { Class beanClass=null; // we need at least one argument, max two if ( args.length < 1 || args.length > 2 || args[0].equals("-h") || args[0].equals("?") ) { usage(); } int argIndex = 0; if ( args.length == 2 ) { if ( args[0].equals("-v") ) { verbose = true; argIndex++; } else { usage(); } } // if class name ends with .class, strip this. beanClassName = args[argIndex]; if ( beanClassName.endsWith(".class") ) { int len = beanClassName.length(); beanClassName = beanClassName.substring(0, len-6); } beanPackageName = packageName(beanClassName); // locate the class in the JVM try { beanClass = Class.forName(beanClassName); } catch (ClassNotFoundException spelling) { System.out.print("Bean '"); System.out.print(beanClassName); System.out.println("' not found. Please check spelling."); System.exit(10); } System.out.print("Event Listener Management Methods for Bean '"); System.out.print(beanClassName); System.out.println("':"); System.out.println(""); // invoke the heart of the program as an object new ShowListenerMethods(beanClass); } //------------------------------------------------------------------- // Remove a substring from a larger string (if present). //------------------------------------------------------------------- public static String removeSubstring (String large, String substring) { int index; index = large.indexOf(substring); if ( index != -1 ) { large = large.substring(0, index) + large.substring(index + substring.length()); } return large; } //------------------------------------------------------------------- // Remove a starting substring from a larger string (if present). //------------------------------------------------------------------- public static String removeStartString (String large, String substring) { if ( large.startsWith(substring) ) { large = large.substring(substring.length()); } return large; } } // End of Class ShowListenerMethods