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 }