/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Copyright (c) Non, Inc. 2002 -- All Rights Reserved PACKAGE: JavaWorld Tips & Tricks FILE: StackTrace.java AUTHOR: John D. Mitchell, Feb 20, 2002 REVISION HISTORY: Name Date Description ---- ---- ----------- JDM 2002.02.20 Initial version. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ // (Un)Comment the following package declaration to try the code with a // package (un)defined. Remember to setup your class path and the location // of the file appropriately. // package boo.hoo; // Java core packages: import java.io.* ; import java.lang.* ; import java.util.* ; /** ** Using the new Java v1.4 facilities in the Throwable class ** to pull information out of a stack trace.

** ** @author John D. Mitchell, Non, Inc., Feb 20, 2002 ** ** @version 0.5 ** **/ public class StackTrace { public static void main (String[] args) { // Create a throwable so we can pick it apart. // Note clearly that the line number information will be noted at // the point where the exception is created... Throwable ex = new Throwable(); // Display what the stack trace looks like directly from here. System.out.println ("Dumping from directly in main():"); displayStackTraceInformation (ex, true); // Now, what's it look like deeper in a call stack... // Note how the stack elements are LIFO order. System.out.println ("Dumping from somewhere inside foo():"); foo(); // Let's create an instance to see if that looks any different from // the static case... System.out.println ("Dumping from inside StackTrace constructor():"); StackTrace st = new StackTrace(); System.out.println ("Dumping from inside crashAndBurnout():"); st.crashAndBurnout(); // Now, let's see what happens with an inner class... StackTrace st2 = new StackTrace(true); } // End of main(). public static boolean displayStackTraceInformation (Throwable ex) { // For the default case, just show the top element in the trace. return displayStackTraceInformation (ex, false); } public static boolean displayStackTraceInformation (Throwable ex, boolean displayAll) { if (null == ex) { System.out.println ("Null stack trace reference! Bailing..."); return false; } // Display the entire blob using printStackTrace(). System.out.println ("The stack according to printStackTrace():\n"); ex.printStackTrace(); System.out.println (""); // Get the list of StackTraceElements from the throwable. StackTraceElement[] stackElements = ex.getStackTrace(); // Display each of the elements using the various capabilitiies of // the StackTraceElements class. // Or, just display the top element of the stack (to cut down on // all of the repetition. if (displayAll) { System.out.println ("The " + stackElements.length + " element" + ((stackElements.length == 1) ? "": "s") + " of the stack trace:\n"); } else { System.out.println ("The top element of a " + stackElements.length + " element stack trace:\n"); } for (int lcv = 0; lcv < stackElements.length; lcv++) { System.out.println ("File name: " + stackElements[lcv].getFileName()); System.out.println ("Line number: " + stackElements[lcv].getLineNumber()); String className = stackElements[lcv].getClassName(); String packageName = extractPackageName (className); String simpleClassName = extractSimpleClassName (className); System.out.println ("Package name: " + ("".equals (packageName)? "[default package]" : packageName)); System.out.println ("Full class name: " + className); System.out.println ("Simple class name: " + simpleClassName); System.out.println ("Unmunged class name: " + unmungeSimpleClassName (simpleClassName)); System.out.println ("Direct class name: " + extractDirectClassName (simpleClassName)); System.out.println ("Method name: " + stackElements[lcv].getMethodName()); System.out.println ("Native method?: " + stackElements[lcv].isNativeMethod()); System.out.println ("toString(): " + stackElements[lcv].toString()); System.out.println (""); // Only continue if the caller really wanted all of the // elements displayed. if (!displayAll) return true; } System.out.println (""); return true; } // End of displayStackTraceInformation(). public static String extractPackageName (String fullClassName) { if ((null == fullClassName) || ("".equals (fullClassName))) return ""; // The package name is everything preceding the last dot. // Is there a dot in the name? int lastDot = fullClassName.lastIndexOf ('.'); // Note that by fiat, I declare that any class name that has been // passed in which starts with a dot doesn't have a package name. if (0 >= lastDot) return ""; // Otherwise, extract the package name. return fullClassName.substring (0, lastDot); } public static String extractSimpleClassName (String fullClassName) { if ((null == fullClassName) || ("".equals (fullClassName))) return ""; // The simple class name is everything after the last dot. // If there's no dot then the whole thing is the class name. int lastDot = fullClassName.lastIndexOf ('.'); if (0 > lastDot) return fullClassName; // Otherwise, extract the class name. return fullClassName.substring (++lastDot); } public static String extractDirectClassName (String simpleClassName) { if ((null == simpleClassName) || ("".equals (simpleClassName))) return ""; // The direct class name is everything after the last '$', if there // are any '$'s in the simple class name. Otherwise, it's just // the simple class name. int lastSign = simpleClassName.lastIndexOf ('$'); if (0 > lastSign) return simpleClassName; // Otherwise, extract the last class name. // Note that if you have a multiply-nested class, that this // will only extract the very last one. Extracting the stack of // nestings is left as an exercise for the reader. return simpleClassName.substring (++lastSign); } public static String unmungeSimpleClassName (String simpleClassName) { if ((null == simpleClassName) || ("".equals (simpleClassName))) return ""; // Nested classes are set apart from top-level classes by using // the dollar sign '$' instead of a period '.' as the separator // between them and the top-level class that they sit // underneath. Let's undo that. return simpleClassName.replace ('$', '.'); } public static void foo() { bar(); } public static void bar() { displayStackTraceInformation (new Throwable(), true); } public StackTrace() { // Notice how the constructors are named "". displayStackTraceInformation (new Throwable()); } public void crashAndBurnout() { displayStackTraceInformation (new Throwable()); } public StackTrace (boolean na) { // Let's create some inner classes and abuse them a bit... // Notice how we can't directly tell which constructor we're in // given the information that we've been given. StackTrace.FirstNested nested = new StackTrace.FirstNested(); } public class FirstNested { public FirstNested() { StackTrace.displayStackTraceInformation (new Throwable()); // Let's go hog wild with another inner class. StackTrace.FirstNested.SecondNested yan = new StackTrace.FirstNested.SecondNested(); System.out.println ("Dumping from inside hogwash():"); yan.hogwash(); } public class SecondNested { public SecondNested() { StackTrace.displayStackTraceInformation (new Throwable()); } public void hogwash() { StackTrace.displayStackTraceInformation (new Throwable()); // Let's add in an anonymous inner class into the mix... Whackable whacked = new Whackable() { public void whack() { // Note how this anonymous class is actually // directly a child of the top-level class and // how it's given a numerical name. StackTrace.displayStackTraceInformation (new Throwable()); } }; // End of anonymous member class. whacked.whack(); } // End of hogwash(). } // End of FirstNested.SecondNexted member class. } // End of FirstNested member class. public interface Whackable { public void whack(); } } // End of class StackTrace.