defensive programming


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;
      }
   }


last | | contents | | next