001 /* 002 * LAPIS lightweight structured text processing system 003 * 004 * Copyright (C) 1998-2002 Carnegie Mellon University, 005 * Copyright (C) 2003 Massachusetts Institute of Technology. 006 * All rights reserved. 007 * 008 * This library is free software; you can redistribute it 009 * and/or modify it under the terms of the GNU General 010 * Public License as published by the Free Software 011 * Foundation, version 2. 012 * 013 * LAPIS homepage: http://graphics.lcs.mit.edu/lapis/ 014 */ 015 016 017 package lapisx.util; 018 019 import lapisx.io.NullOutputStream; 020 import java.lang.reflect.Field; 021 import java.lang.reflect.Modifier; 022 import java.io.PrintStream; 023 import java.io.OutputStream; 024 025 /** 026 * Debugging output that can be enabled and disabled on a per-class 027 * basis. Using Debug is a three-step process. First, declare a public 028 * static (but not final) variable called debug in your class. Its 029 * default value should generally be Debug.QUIET: 030 * 031 * <pre> 032 * import lapisx.util.Debug; 033 * 034 * public class MyClass { 035 * public static Debug debug = Debug.QUIET; 036 * </pre> 037 * 038 * Second, whenever you want to print debugging output, use the debug 039 * variable instead of System.out or System.err: 040 * 041 * <pre> 042 * debug.println ("this is my debugging message"); 043 * // instead of System.err.println() 044 * ... 045 * 046 * try { 047 * ... 048 * } catch (Exception e) { 049 * debug.report (e); 050 * // instead of e.printStackTrace() 051 * ... 052 * } 053 * 054 * debug.assertion (obj != null); 055 * // to check assertions 056 * </pre> 057 * 058 * Third, when you run your program, use {@link #setDebugLevel} to enable 059 * and disable debugging on a class-by-class basis: 060 * 061 * <pre> 062 * Debug.setDebugLevel (MyClass.class, Debug.VERBOSE); 063 * </pre> 064 * 065 * In LAPIS, you can use the -debug command-line parameter to enable 066 * debugging output for the classes you want to debug: 067 * 068 * <pre> 069 * lapis -debug MyClass,MyOtherClass,... 070 * </pre> 071 * 072 * If you have a set of related classes that are always debugged 073 * simultaneously, then you may want them to share a single debug 074 * variable for convenience. 075 */ 076 public abstract class Debug { 077 /** 078 * Enables all debugging output and assertion checks. Assertion failures 079 * throw a RuntimeException. Output is sent to System.err. 080 */ 081 public static final Debug VERBOSE = new Verbose (); 082 083 /** 084 * Enables only exception reports and assertion checks. 085 * Disables other printing. Assertion failures print an exception report, 086 * but don't throw any exceptions, so computation can proceed. 087 * Output is sent to System.err. 088 */ 089 public static final Debug QUIET = new Quiet (); 090 091 /** 092 * Disables all debugging output and assertion checks. 093 */ 094 public static final Debug NONE = new NoDebug (); 095 096 097 /** 098 * Get the debugging object currently in use by a class. 099 * @param cls Class to query 100 * @return Debug object found in the public static field "debug" of cls. 101 * @throws NoSuchFieldException if cls has no public static field named "debug" 102 */ 103 public static Debug getDebugLevel (Class cls) 104 throws NoSuchFieldException { 105 try { 106 Field fld = cls.getField ("debug"); 107 if (fld.getType () != Debug.class 108 || !Modifier.isStatic (fld.getModifiers ())) 109 throw new NoSuchFieldException (); 110 return (Debug) fld.get (null); 111 } catch (IllegalArgumentException e) { 112 throw new NoSuchFieldException (); 113 } catch (IllegalAccessException e) { 114 throw new NoSuchFieldException (); 115 } catch (SecurityException e) { 116 throw new NoSuchFieldException (); 117 } 118 } 119 120 /** 121 * Set the debugging object used by a class. Call this method to enable or 122 * disable the class's debugging output, e.g. 123 * <code>Debug.setDebugLevel (MyClass.class, Debug.VERBOSE)</code>. 124 * @modifies cls 125 * @param cls Class to modify 126 * @throws NoSuchFieldException if cls has no public static field named "debug" 127 */ 128 public static void setDebugLevel (Class cls, Debug level) 129 throws NoSuchFieldException { 130 try { 131 Field fld = cls.getField ("debug"); 132 if (fld.getType () != Debug.class 133 || !Modifier.isStatic (fld.getModifiers ())) 134 throw new NoSuchFieldException (); 135 fld.set (null, level); 136 } catch (IllegalArgumentException e) { 137 throw new NoSuchFieldException (); 138 } catch (IllegalAccessException e) { 139 throw new NoSuchFieldException (); 140 } catch (SecurityException e) { 141 throw new NoSuchFieldException (); 142 } 143 } 144 145 /** 146 * Test whether calling print() or println() on this debug object will print anything. 147 * @return true if print() prints, false if it's disabled. 148 */ 149 public abstract boolean printEnabled (); 150 151 /** 152 * Test whether calling report() on this debug object will print anything. 153 * @return true if report() prints, false if it's disabled. 154 */ 155 public abstract boolean reportEnabled (); 156 157 /** 158 * Test whether calling assertion() on this debug object will do anything. 159 * @return true if assertion() checks assertions, false if it's disabled. 160 */ 161 public abstract boolean assertionEnabled (); 162 163 /** 164 * Print message (if this is enabled for printing) 165 * @modifies this debug object's output stream (by default, System.err) 166 * @param message message to print 167 * @effects prints message to output stream, if this is enabled for printing 168 */ 169 public abstract void print (String message); 170 171 /** 172 * Print message (if this is enabled for printing) 173 * @modifies this debug object's output stream (by default, System.err) 174 * @param message message to print 175 * @effects prints message to output stream, followed by a newline, if 176 * this is enabled for printing 177 */ 178 public abstract void println (String message); 179 180 /** 181 * Print an object (if this is enabled for printing) 182 * @modifies this debug object's output stream (by default, System.err) 183 * @param obj object to print 184 * @effects prints obj.toString() to output stream, if this is enabled for printing 185 */ 186 public abstract void print (Object obj); 187 188 /** 189 * Print an object (if this is enabled for printing) 190 * @modifies this debug object's output stream (by default, System.err) 191 * @param obj object to print 192 * @effects prints obj.toString() to output stream, followed by a newline, if 193 * this is enabled for printing 194 */ 195 public abstract void println (Object obj); 196 197 /** 198 * Print the stack trace of an exception (if this is enabled for reporting exceptions) 199 * @modifies this debug object's output stream (by default, System.err) 200 * @param t exception 201 * @effects prints t's stack trace to output stream, if this is enabled for reporting 202 * exceptions. 203 */ 204 public abstract void report (Throwable t); 205 206 /** 207 * Print a list of active threads (if this is enabled for printing) 208 * @modifies this debug object's output stream (by default, System.err) 209 * @effects prints list of active threads to output stream, if 210 * this is enabled for printing 211 */ 212 public abstract void printThreadInfo (); 213 214 /** 215 * Print stack trace of current execution point (if this is 216 * enabled for debugging printing) 217 * @modifies this debug object's output stream (by default, System.err) 218 * @effects prints stack trace (starting with the call to this.printStackTrace), 219 * if this is enabled for printing 220 */ 221 public abstract void printStackTrace (); 222 223 /** 224 * Test an assertion (if this is enabled for assertions) 225 * @modifies this debug object's output stream (by default, System.err) 226 * @param f result of assertion expression 227 * @throws RuntimeException if assertion fails and this is Verbose. 228 * @effects if assertion fails and this is Quiet, then merely reports the assertion 229 * failure (in the form of a stack trace) to the output stream. 230 */ 231 public abstract void assertion (boolean f); 232 233 /** 234 * Returns the OutputStream associated with this debug object. 235 */ 236 public abstract OutputStream getOutputStream(); 237 238 239 /** 240 * Debug object that prints all messages, exception stack traces, and checks all 241 * assertions. Useful during test and debug. 242 * <p>If you want to print to System.err, just use the singleton instance 243 * Debug.VERBOSE rather than constructing a new Debug.Verbose instance. 244 */ 245 public static class Verbose extends Debug { 246 protected PrintStream out; 247 248 /** 249 * Makes a Verbose debug object that prints to System.err. 250 */ 251 public Verbose () { 252 this (System.err); 253 } 254 255 /** 256 * Makes a Verbose debug object that prints to the given stream. 257 * @param out stream that will receive debugging output from this object. 258 */ 259 public Verbose (PrintStream out) { 260 this.out = out; 261 } 262 263 public boolean printEnabled () { 264 return true; 265 } 266 267 public boolean reportEnabled () { 268 return true; 269 } 270 271 public boolean assertionEnabled () { 272 return true; 273 } 274 275 public void print (String message) { 276 out.print (message); 277 out.flush (); 278 } 279 280 public void println (String message) { 281 out.println (message); 282 out.flush (); 283 } 284 285 public void print (Object obj) { 286 print (obj.toString ()); 287 } 288 289 public void println (Object obj) { 290 println (obj.toString ()); 291 } 292 293 public void report (Throwable t) { 294 t.printStackTrace (out); 295 out.flush (); 296 } 297 298 public void printThreadInfo () { 299 ThreadGroup g = Thread.currentThread().getThreadGroup (); 300 Thread[] t = new Thread[g.activeCount ()]; 301 g.enumerate (t); 302 out.println ("Active threads in " + g); 303 for (int i=0; i<t.length; ++i) 304 out.println (t[i]); 305 out.flush (); 306 } 307 308 public void printStackTrace () { 309 try { 310 throw new Exception (); 311 } catch (Exception e) { 312 e.printStackTrace (out); 313 out.flush (); 314 } 315 } 316 317 public void assertion (boolean f) { 318 if (!f) 319 throw new RuntimeException ("assertion failure"); 320 } 321 322 public OutputStream getOutputStream() { 323 return out; 324 } 325 326 } 327 328 /** 329 * Debug object that only prints exception stack traces and 330 * assertion failures. No other debug messages are printed. 331 * Assertion failures only print a report, without throwing an 332 * exception. This object is a good default debug level, even for 333 * released code. 334 * 335 * <p>If you want to print to System.err, just use the singleton 336 * instance Debug.QUIET rather than constructing a new Debug.Quiet 337 * instance. 338 */ 339 public static class Quiet extends Verbose { 340 /** 341 * Makes a Quiet debug object that sends exception reports to System.err. 342 */ 343 public Quiet () { 344 } 345 346 /** 347 * Makes a Quiet debug object that sends exception reports to the given stream. 348 * @param out stream to receive exception reports. 349 */ 350 public Quiet (PrintStream out) { 351 super (out); 352 } 353 354 public boolean printEnabled () { 355 return false; 356 } 357 358 public boolean reportEnabled () { 359 return true; 360 } 361 362 public boolean assertionEnabled () { 363 return true; 364 } 365 366 public void print (String message) { 367 } 368 public void println (String message) { 369 } 370 public void print (Object message) { 371 } 372 public void println (Object message) { 373 } 374 public void report (Throwable t) { 375 t.printStackTrace (out); 376 out.flush (); 377 } 378 public void printThreadInfo () { 379 } 380 public void printStackTrace () { 381 } 382 public void assertion (boolean f) { 383 if (!f) { 384 try { 385 throw new RuntimeException ("assertion failure"); 386 } catch (RuntimeException e) { 387 e.printStackTrace (out); 388 out.flush (); 389 } 390 } 391 } 392 } 393 394 /** 395 * Debug object that prints nothing and checks no assertions. 396 * Rarely useful. 397 * 398 * <p>It is preferable to use the singleton instance Debug.NONE 399 * rather than constructing new Debug.NoDebug instances. The 400 * Debug.NoDebug class is made public in case someone wants to 401 * extend it. 402 */ 403 public static class NoDebug extends Debug { 404 protected NullOutputStream out = NullOutputStream.getInstance(); 405 406 public boolean printEnabled () { 407 return false; 408 } 409 410 public boolean reportEnabled () { 411 return false; 412 } 413 414 public boolean assertionEnabled () { 415 return false; 416 } 417 418 public void print (String message) { 419 } 420 public void println (String message) { 421 } 422 public void print (Object message) { 423 } 424 public void println (Object message) { 425 } 426 public void report (Throwable t) { 427 } 428 public void printThreadInfo () { 429 } 430 public void printStackTrace () { 431 } 432 public void assertion (boolean f) { 433 } 434 435 public OutputStream getOutputStream() { 436 return out; 437 } 438 } 439 440 }