/*
* Copyright (c) Holger Pfaff - http://pfaff.ws
*
* This software maybe used for any purpose provided the
* above copyright notice is retained. It is supplied as is.
* No warranty expressed or implied - Use at your own risk.
*/
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;
import java.util.TimeZone;
import java.util.Enumeration;
import java.util.StringTokenizer;
/**
* (#)Debug.java
* @author Holger Pfaff
*
* Standalone class with static methods to aid with debugging.
* Debugging output can be controlled with different methods:
*
* 1) By specifying the class with should be debugged
* 2) By specifying an optional "level" with should be debugged
* 3) By specifying the "warning" flag
*
* The format can be controlled via some flags
*
* 1) Thread (p): Shows the name of the current Thread
* 2) Location (l): Shows the location within the source file
* 3) Time (t): Shows the current time including millisec.
* 4) Delta (d): Shows the time delta from the last debugging output
* 5) Warning (w): Shows warning messages (see above)
* 6) Method (m): Shows the name of the current method
* 7) Class (c): Shows the class name
*
* Location printing only works correct if there is no JIT Compiler active.
*
*/
public class Debug extends Object {
/**
* Stream to write to
*/
private static PrintWriter _writer = new PrintWriter(System.err);
/**
* format to use for timestamps
*/
private static DateFormat _datef = new SimpleDateFormat("HH:mm:ss.SSS ");
/**
* this is where we get our time from ...
*/
private static Date _date = new Date();
/**
* show thread id ? (p)
*/
private static boolean _thread = false;
/**
* show warnings ? (w)
*/
private static boolean _warn = false;
/**
* show location in source file ? (l)
*/
private static boolean _loc = false;
/**
* show methodname ? (m)
*/
private static boolean _meth = false;
/**
* show an absolute timestamp ? (t)
*/
private static boolean _time = false;
/**
* show a delta timestamp ? (d)
*/
private static boolean _delta = false;
/**
* show class name ? (c)
*/
private static boolean _cname = false;
/**
* store levels to debug
*/
private static Vector _level = new Vector();
/**
* store classes to debug
*/
private static Vector _class = new Vector();
/**
* empty string array - used internally for error conditions
*/
private static String[] _nloc = new String[0];
/**
* Configure debugging from command line arg.
* Format shoud be: :,...:,...
* e.g. plt:*:Main
* shows thread name, locaton & time for any level (*) and class Main
*
* @param a the command line arg
*/
public static void setArgs(String a) {
StringTokenizer t = new StringTokenizer(a, ":");
if(t.hasMoreTokens()) setFlags(t.nextToken(), true);
if(t.hasMoreTokens()) setLevel(t.nextToken(), true);
if(t.hasMoreTokens()) setClass(t.nextToken(), true);
}
/**
* Set debugging flags
*
* @param f flaf to set
* @param b set or re-set ?
*/
public static void setFlags(String f, boolean b) {
Compiler.disable();
for(int i = 0; i < f.length(); ++i) {
switch(f.charAt(i)) {
case 'p': _thread = b; break;
case 'w': _warn = b; break;
case 'm': _meth = b; break;
case 'l': _loc = b; break;
case 't': _time = b; break;
case 'c': _cname = b; break;
case 'd': _delta = b; _datef.setTimeZone(TimeZone.getTimeZone("GMT")); break;
case '*': setFlags("pwltmc", b); break;
}
}
}
/**
* Set/unset debugging levels,classes
*
* @param v vector to use - _levels, _classes
* @param s level or class to set/unset
* @param a set or unset?
*/
private static void set(Vector v, String s, boolean a) {
if(s.indexOf(",") > -1) {
for(Enumeration e = new StringTokenizer(s, ",") ; e.hasMoreElements() ;) {
set(v, (String) e.nextElement(), a);
}
} else {
if(a) {
if(v.contains(s) == false) v.addElement(s);
} else {
v.removeElement(s);
}
}
}
/**
* Set/unset debugging levels
*
* @param l level to set/unset
* @param a set or unset?
*/
public static void setLevel(String l, boolean a) {
set(_level, l, a);
}
/**
* Set/unset debugging classes
*
* @param c class to set/unset
* @param a set or unset?
*/
public static void setClass(String c, boolean a) {
set(_class, c, a);
}
/**
* Set/unset debugging classes
*
* @param c class to set/unset
* @param a set or unset?
*/
public static void setClass(Class c, boolean a) {
set(_class, c.getName(), a);
}
/**
* Return classname of Object. o ist eiter:
* - a Class object
* - a String containing the class name
* - or the Object itself
*
* @param o object to return classname of
* @return classname of Object as string
*/
public static String getClassName(Object o) {
if(o instanceof Class) {
return ((Class) o).getName();
} else if(o instanceof String) {
return o.toString();
} else {
return o.getClass().getName();
}
}
/**
* Report an error
*
* @param m usually the error message or Exception
*/
public static void error(Object m) {
println("###ERROR: ", m);;
}
/**
* Report a warning
*
* @param m usually the warning message or Exception
*/
public static void warn(Object m) {
if(_warn) println("###WARN: ", m);
}
/**
* Debug object o if object o is to debug - no additional message
*
* @param o Object to debug
*/
public static void debug(Object o) {
debug(o, "<>");
}
/**
* Debug object o if object o is to debug on all levels - add message m
*
* @param o Object to debug
*
* @param m the message
*/
public static void debug(Object o, Object m) {
debug("*", o, m);
}
/**
* Debug object o if object o and level l is to debug - add message m
*
* @param l level
* @param o Object to debug
* @param m the message
*/
public static void debug(Object l, Object o, Object m) {
if(isLevel(l) && isClass(o)) println(cname(o), m);
}
/**
* Check if level l was requested to debug.
*
* @param l level
*/
public static boolean isLevel(Object l) {
if(_level.contains("*")) return true;
if(l == null) return false;
return _level.contains(l);
}
/**
* Check if class o was requested to debug.
*
* @param o class
*/
public static boolean isClass(Object o) {
if(_class.contains("*")) return true;
if(o == null) return false;
if(o instanceof String) return _class.contains(o.toString());
if(o instanceof Class == false) o = o.getClass();
for(Class c = (Class) o; c != null; c = c.getSuperclass()) {
if(_class.contains(c.getName())) return true;
}
return false;
}
/**
* form a debug string
*
* @param p prefix (e.g. timestamp)
* @param m the message
*
* @return a nice formatted string
*/
public static String toString(String p, Object m) {
String[] l = _meth || _loc ? stackline(0) : _nloc;
return time() + thread() + stackline2meth(l) + p + msg(m) + stackline2loc(l);
}
/**
* print a debug string
*
* @param p prefix (e.g. timestamp)
* @param m the message
*/
public static void println(String p, Object m) {
_writer.println(toString(p, m));
_writer.flush();
}
/**
* put out a timestamp if requested
*/
public static String time() {
if(_time || _delta) {
Date d = _date;
_date = new Date();
d.setTime(_delta ? _date.getTime() - d.getTime() : _date.getTime());
return _datef.format(d);
} else {
return "";
}
}
/**
* put out a classname if requested
*
* @param o object to put out classname for
*/
public static String cname(Object o) {
return _cname ? "(" + o.getClass().getName() + ") " : "";
}
/**
* put out a threadname if requested
*/
public static String thread() {
return _thread ? "[" + Thread.currentThread().getName() + "] " : "";
}
/**
* format the error msg
*
* @param o object to format
*/
public static String msg(Object o){
return o == null ? "" : (o instanceof Exception ? msg((Exception) o) : o.toString());
}
/**
* format the error msg
*
* @param x Exception to format
*/
public static String msg(Exception x) {
String m = x.getMessage();
String n = x.getClass().getName();
n = n.substring(n.lastIndexOf('.') + 1);
if(m == null || m.length() == 0) {
return n;
} else {
return n + ": " + m;
}
}
/**
* return stack trace as string
*
* @param x Exception to format
*/
public static String stack(Exception X) {
try {
StringWriter b = new StringWriter();
PrintWriter w = new PrintWriter(b);
X.printStackTrace(w);
w.close();
b.close();
return b.toString().replace('\r', ' ');
} catch(Exception x) {
return "";
}
}
/**
* return specific stackline - 0 is where exception occured
*
* @param 0 position in stack to return
*/
public static String[] stackline(int s){
StringTokenizer t = new StringTokenizer(stack(new Exception()), "\n\r");
for(int i = 0; t.hasMoreTokens();) {
String l = t.nextToken().trim(); // get next line & remove blanks
if(l.startsWith("java") || l.startsWith("at Debug")) {
continue; // remove first line & self
}
if(i++ == s) { // reached desired depth ?
StringTokenizer st = new StringTokenizer(l, " ()");
String[] sl = new String[st.countTokens()];
for(int j = 0; j < sl.length; ++j) sl[j] = st.nextToken();
return sl;
}
}
return _nloc; // return empty array on failure
}
/**
* extract methodname from stackline
*/
public static String stackline2meth(String[] l) {
return _meth && l.length > 1 ? "{" + l[1] + "} " : "";
}
/**
* extract location in source from stackline
*/
public static String stackline2loc(String[] l) {
return _loc && l.length > 2 ? " (at " + l[2] + ")" : "";
}
}