001 /* 002 * Copyright (c) Holger Pfaff - http://pfaff.ws 003 * 004 * This software maybe used for any purpose provided the 005 * above copyright notice is retained. It is supplied as is. 006 * No warranty expressed or implied - Use at your own risk. 007 */ 008 009 import java.io.PrintWriter; 010 import java.io.StringWriter; 011 import java.text.DateFormat; 012 import java.text.SimpleDateFormat; 013 import java.util.Date; 014 import java.util.Vector; 015 import java.util.TimeZone; 016 import java.util.Enumeration; 017 import java.util.StringTokenizer; 018 019 /** 020 * (#)Debug.java 021 * @author Holger Pfaff 022 * @version 3.2 19-Mar-2004<br><br> 023 * 024 * Standalone class with static methods to aid with debugging. 025 * Debugging output can be controlled with different methods:<br><br> 026 * 027 * 1) By specifying the class with should be debugged<br> 028 * 2) By specifying an optional "level" with should be debugged<br> 029 * 3) By specifying the "warning" flag<br> 030 * 031 * The format can be controlled via some flags<br><br> 032 * 033 * 1) Thread (p): Shows the name of the current Thread<br> 034 * 2) Location (l): Shows the location within the source file<br> 035 * 3) Time (t): Shows the current time including millisec.<br> 036 * 4) Delta (d): Shows the time delta from the last debugging output<br> 037 * 5) Warning (w): Shows warning messages (see above)<br> 038 * 6) Method (m): Shows the name of the current method<br> 039 * 7) Class (c): Shows the class name<br><br> 040 * 041 * Location printing only works correct if there is no JIT Compiler active. 042 * 043 */ 044 045 public class Debug extends Object { 046 047 /** 048 * Stream to write to 049 */ 050 private static PrintWriter _writer = new PrintWriter(System.err); 051 052 /** 053 * format to use for timestamps 054 */ 055 private static DateFormat _datef = new SimpleDateFormat("HH:mm:ss.SSS "); 056 057 /** 058 * this is where we get our time from ... 059 */ 060 private static Date _date = new Date(); 061 062 /** 063 * show thread id ? (p) 064 */ 065 private static boolean _thread = false; 066 067 /** 068 * show warnings ? (w) 069 */ 070 private static boolean _warn = false; 071 072 /** 073 * show location in source file ? (l) 074 */ 075 private static boolean _loc = false; 076 077 /** 078 * show methodname ? (m) 079 */ 080 private static boolean _meth = false; 081 082 /** 083 * show an absolute timestamp ? (t) 084 */ 085 private static boolean _time = false; 086 087 /** 088 * show a delta timestamp ? (d) 089 */ 090 private static boolean _delta = false; 091 092 /** 093 * show class name ? (c) 094 */ 095 private static boolean _cname = false; 096 097 /** 098 * store levels to debug 099 */ 100 private static Vector _level = new Vector(); 101 102 /** 103 * store classes to debug 104 */ 105 private static Vector _class = new Vector(); 106 107 /** 108 * empty string array - used internally for error conditions 109 */ 110 private static String[] _nloc = new String[0]; 111 112 /** 113 * Configure debugging from command line arg. 114 * Format shoud be: <flags>:<level>,...:<class>,... 115 * e.g. plt:*:Main 116 * shows thread name, locaton & time for any level (*) and class Main 117 * 118 * @param a the command line arg 119 */ 120 public static void setArgs(String a) { 121 StringTokenizer t = new StringTokenizer(a, ":"); 122 if(t.hasMoreTokens()) setFlags(t.nextToken(), true); 123 if(t.hasMoreTokens()) setLevel(t.nextToken(), true); 124 if(t.hasMoreTokens()) setClass(t.nextToken(), true); 125 } 126 127 /** 128 * Set debugging flags 129 * 130 * @param f flaf to set 131 * @param b set or re-set ? 132 */ 133 public static void setFlags(String f, boolean b) { 134 Compiler.disable(); 135 for(int i = 0; i < f.length(); ++i) { 136 switch(f.charAt(i)) { 137 case 'p': _thread = b; break; 138 case 'w': _warn = b; break; 139 case 'm': _meth = b; break; 140 case 'l': _loc = b; break; 141 case 't': _time = b; break; 142 case 'c': _cname = b; break; 143 case 'd': _delta = b; _datef.setTimeZone(TimeZone.getTimeZone("GMT")); break; 144 case '*': setFlags("pwltmc", b); break; 145 } 146 } 147 } 148 149 /** 150 * Set/unset debugging levels,classes 151 * 152 * @param v vector to use - _levels, _classes 153 * @param s level or class to set/unset 154 * @param a set or unset? 155 */ 156 private static void set(Vector v, String s, boolean a) { 157 if(s.indexOf(",") > -1) { 158 for(Enumeration e = new StringTokenizer(s, ",") ; e.hasMoreElements() ;) { 159 set(v, (String) e.nextElement(), a); 160 } 161 } else { 162 if(a) { 163 if(v.contains(s) == false) v.addElement(s); 164 } else { 165 v.removeElement(s); 166 } 167 } 168 } 169 170 /** 171 * Set/unset debugging levels 172 * 173 * @param l level to set/unset 174 * @param a set or unset? 175 */ 176 public static void setLevel(String l, boolean a) { 177 set(_level, l, a); 178 } 179 180 /** 181 * Set/unset debugging classes 182 * 183 * @param c class to set/unset 184 * @param a set or unset? 185 */ 186 public static void setClass(String c, boolean a) { 187 set(_class, c, a); 188 } 189 190 /** 191 * Set/unset debugging classes 192 * 193 * @param c class to set/unset 194 * @param a set or unset? 195 */ 196 public static void setClass(Class c, boolean a) { 197 set(_class, c.getName(), a); 198 } 199 200 /** 201 * Return classname of Object. o ist eiter:<br> 202 * - a Class object<br> 203 * - a String containing the class name<br> 204 * - or the Object itself<br> 205 * 206 * @param o object to return classname of 207 * @return classname of Object as string 208 */ 209 public static String getClassName(Object o) { 210 if(o instanceof Class) { 211 return ((Class) o).getName(); 212 } else if(o instanceof String) { 213 return o.toString(); 214 } else { 215 return o.getClass().getName(); 216 } 217 } 218 219 /** 220 * Report an error 221 * 222 * @param m usually the error message or Exception 223 */ 224 public static void error(Object m) { 225 println("###ERROR: ", m);; 226 } 227 228 /** 229 * Report a warning 230 * 231 * @param m usually the warning message or Exception 232 */ 233 public static void warn(Object m) { 234 if(_warn) println("###WARN: ", m); 235 } 236 237 /** 238 * Debug object o if object o is to debug - no additional message 239 * 240 * @param o Object to debug 241 */ 242 public static void debug(Object o) { 243 debug(o, "<>"); 244 } 245 246 /** 247 * Debug object o if object o is to debug on all levels - add message m 248 * 249 * @param o Object to debug 250 * 251 * @param m the message 252 */ 253 public static void debug(Object o, Object m) { 254 debug("*", o, m); 255 } 256 257 /** 258 * Debug object o if object o and level l is to debug - add message m 259 * 260 * @param l level 261 * @param o Object to debug 262 * @param m the message 263 */ 264 public static void debug(Object l, Object o, Object m) { 265 if(isLevel(l) && isClass(o)) println(cname(o), m); 266 } 267 268 /** 269 * Check if level l was requested to debug. 270 * 271 * @param l level 272 */ 273 public static boolean isLevel(Object l) { 274 if(_level.contains("*")) return true; 275 if(l == null) return false; 276 return _level.contains(l); 277 } 278 279 /** 280 * Check if class o was requested to debug. 281 * 282 * @param o class 283 */ 284 public static boolean isClass(Object o) { 285 if(_class.contains("*")) return true; 286 if(o == null) return false; 287 if(o instanceof String) return _class.contains(o.toString()); 288 if(o instanceof Class == false) o = o.getClass(); 289 for(Class c = (Class) o; c != null; c = c.getSuperclass()) { 290 if(_class.contains(c.getName())) return true; 291 } 292 return false; 293 } 294 295 296 /** 297 * form a debug string 298 * 299 * @param p prefix (e.g. timestamp) 300 * @param m the message 301 * 302 * @return a nice formatted string 303 */ 304 public static String toString(String p, Object m) { 305 String[] l = _meth || _loc ? stackline(0) : _nloc; 306 return time() + thread() + stackline2meth(l) + p + msg(m) + stackline2loc(l); 307 } 308 309 /** 310 * print a debug string 311 * 312 * @param p prefix (e.g. timestamp) 313 * @param m the message 314 */ 315 public static void println(String p, Object m) { 316 _writer.println(toString(p, m)); 317 _writer.flush(); 318 } 319 320 /** 321 * put out a timestamp if requested 322 */ 323 public static String time() { 324 if(_time || _delta) { 325 Date d = _date; 326 _date = new Date(); 327 d.setTime(_delta ? _date.getTime() - d.getTime() : _date.getTime()); 328 return _datef.format(d); 329 } else { 330 return ""; 331 } 332 } 333 334 /** 335 * put out a classname if requested 336 * 337 * @param o object to put out classname for 338 */ 339 public static String cname(Object o) { 340 return _cname ? "(" + o.getClass().getName() + ") " : ""; 341 } 342 343 /** 344 * put out a threadname if requested 345 */ 346 public static String thread() { 347 return _thread ? "[" + Thread.currentThread().getName() + "] " : ""; 348 } 349 350 /** 351 * format the error msg 352 * 353 * @param o object to format 354 */ 355 public static String msg(Object o){ 356 return o == null ? "<null>" : (o instanceof Exception ? msg((Exception) o) : o.toString()); 357 } 358 359 /** 360 * format the error msg 361 * 362 * @param x Exception to format 363 */ 364 public static String msg(Exception x) { 365 String m = x.getMessage(); 366 String n = x.getClass().getName(); 367 n = n.substring(n.lastIndexOf('.') + 1); 368 if(m == null || m.length() == 0) { 369 return n; 370 } else { 371 return n + ": " + m; 372 } 373 } 374 375 /** 376 * return stack trace as string 377 * 378 * @param x Exception to format 379 */ 380 public static String stack(Exception X) { 381 try { 382 StringWriter b = new StringWriter(); 383 PrintWriter w = new PrintWriter(b); 384 X.printStackTrace(w); 385 w.close(); 386 b.close(); 387 return b.toString().replace('\r', ' '); 388 } catch(Exception x) { 389 return ""; 390 } 391 } 392 393 /** 394 * return specific stackline - 0 is where exception occured 395 * 396 * @param 0 position in stack to return 397 */ 398 public static String[] stackline(int s){ 399 StringTokenizer t = new StringTokenizer(stack(new Exception()), "\n\r"); 400 for(int i = 0; t.hasMoreTokens();) { 401 String l = t.nextToken().trim(); // get next line & remove blanks 402 if(l.startsWith("java") || l.startsWith("at Debug")) { 403 continue; // remove first line & self 404 } 405 if(i++ == s) { // reached desired depth ? 406 StringTokenizer st = new StringTokenizer(l, " ()"); 407 String[] sl = new String[st.countTokens()]; 408 for(int j = 0; j < sl.length; ++j) sl[j] = st.nextToken(); 409 return sl; 410 } 411 } 412 return _nloc; // return empty array on failure 413 } 414 415 /** 416 * extract methodname from stackline 417 */ 418 public static String stackline2meth(String[] l) { 419 return _meth && l.length > 1 ? "{" + l[1] + "} " : ""; 420 } 421 422 /** 423 * extract location in source from stackline 424 */ 425 public static String stackline2loc(String[] l) { 426 return _loc && l.length > 2 ? " (at " + l[2] + ")" : ""; 427 } 428 }