testing, debugging, and defensive programming
Events
Exceptions
Threads
Locks
User Interfaces
How To Build A World
other possible chapter titles: Ill-Met by Moonlight, Pity and Terror, Behind the Scenes, Bit Players and Other Extras, The Stage Hands, Playing To Type, The Band Played On, Dress Rehersal
package com.knownspace.tools;
import java.io.CharArrayWriter;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.util.NoSuchElementException;
public class Debug
{
/*
A simple debugger to implement breakpoint assertion testing.
Public Methods:
---------------
assert(booleanCondition) //fires if booleanCondition==false
assert(booleanCondition, messageString) //fires if booleanCondition==false
//and prints messageString
In both cases the debugger prints the offending method's name.
Since assert() is a static method, you can call it with "Debug.assert(...)".
Every method should have its own assertions to test the state of its
arguments on entry into the method. It should also test assertions on exit
as well.
Set FATAL_RESPONSE to true when you want even one assertion failure to kill
your program.
Set DEBUG_MODE to false when you want to turn off assertion testing.
(Be absolutely sure that your code is working perfectly first!)
*/
private final static boolean FATAL_RESPONSE = false;
private final static boolean DEBUG_MODE = true;
//number of method calls to step back in the stack to find the offending
//method name and line number using the sun JDK
private final static int STACK_LEVEL = 2;
///////////////////////////////////////////////////////////////////////
public final static void assert(boolean condition)
{
if (DEBUG_MODE)
{
if (! condition)
printError("assertion failed in: " + getCaller(STACK_LEVEL));
checkFatality();
}
}
///////////////////////////////////////////////////////////////////////
public final static void assert(boolean condition, String string)
{
if (DEBUG_MODE)
{
if (! condition)
{
printError("assertion failed: " + string);
printError("in: " + getCaller(STACK_LEVEL));
}
checkFatality();
}
}
///////////////////////////////////////////////////////////////////////
private final static void printError(String string)
{
System.out.println(string);
}
///////////////////////////////////////////////////////////////////////
private final static void checkFatality()
{
if (FATAL_RESPONSE)
System.exit(1);
}
///////////////////////////////////////////////////////////////////////
private final static String getCaller(int callerNumber)
{
/*
Determine the callerNumber'th method that called this method
and return its name.
NOTE: This method may not work with code compiled using a JIT
or with a non-JDK JVM since in either case the stack trace
format is unspecified in the language spec.
Stack trace format assumed:
---------------------------
java.lang.Throwable
at
at (callerNumber == 1)
at (callerNumber == 2)
at (callerNumber == 3)
*/
if (callerNumber < 1)
return "Debugger";
//transform the caller number into the token number to search for
int tokenNumber = (callerNumber * 2) + 3;
//create a stream to catch the stack trace
CharArrayWriter stream = new CharArrayWriter();
//send the stack trace to the stream
Throwable throwable = new Throwable();
throwable.printStackTrace(new PrintWriter(stream));
//parse the stack trace to find the callerNumber'th method call
StringTokenizer stackTrace = new StringTokenizer(stream.toString());
String token = "";
try
{
for (int i = 0; i < tokenNumber; i++)
token = stackTrace.nextToken();
}
catch (NoSuchElementException ignored)
{
token = "unknown";
}
return token;
}
}