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    }