/* "Copyright (c) 2012-2016 by Fritz Sieker."
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written
* agreement is hereby granted, provided that the above copyright notice
* and the following two paragraphs appear in all copies of this software,
*
* IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
* INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE AUTHOR
* HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
* BASIS, AND THE AUTHOR NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
* UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
*/
import java.io.PrintStream;
import java.io.FileNotFoundException;
import java.lang.StackTraceElement;
import java.util.Arrays;
/**
* In writing your code, you may be tempted to use System.out.printf()
* to determine what is happening. As an alternative, the method
* Debug.printf() is provided. The nice thing about Debug.printf()
* in comparison to System.out.printf() is that Debug.printf()
* only prints when debug is turned on. You can turn
* debug on and off by setting the debug_level variable to non zero.
* This is normally done by a command line parameter to the main()
* of your program. This module allows you to write debug code, but not need
* to change your program in any
* way before turning it in. If you use System.out.printf() you
* MUST either comment out the lines or remove them before you submit
* your code for grading. This is a simple alternative to the many logging
* packages that have more advanced capabilities. See
* this page for examples.
*
* @author Fritz Sieker
*/
public class Debug {
/** A variable controlling how much debug output is produced. A value of
* 0 means no output. This is normally set by your program's main()
* by a command line argument. With the Debug.lDebug() you can vary
* the amount of output from none, to a lot.
*/
public static int debugLevel = 0;
/** A variable to allow debug output to go someplace other than
* System.err. See the toFile(), close() methods.
* Default to System.err.
*/
/** Define prefix of flag to turn on debugging */
private static final String prefix = "-debug"; // use "-dbg" ???
private static java.io.PrintStream ps = System.err;
/** No instances, a purely static class */
private Debug() {
}
/** This routine will set the debugLevel from the arguments
* pased to main(). If the first argument is -d or
* -dDigit. the value will be used to initialize
* debugLevel and that argument will be removed.
* @param args the array of arguments passed to main
* @return the args with the first value removed (if appropriate)
*/
public static String[] init (String[] args) {
if (args.length > 0) {
String arg0 = args[0];
if (arg0.startsWith(prefix)) {
debugLevel = 1;
int len = prefix.length();
if (arg0.length() > len) { // look for level
try {
debugLevel = Integer.parseInt(arg0.substring(len));
}
catch (NumberFormatException nfe) {
System.err.printf("Bad debug specifier '%s'\n", arg0);
System.exit(-1);
}
}
int newLen = args.length - 1;
String[] newArgs = new String[newLen];
System.arraycopy(args, 1, newArgs, 0, newLen);
args = newArgs;
}
}
return args;
}
/** Print the output prefix "DEBUG fileName[lineNumber] methodName"
* @param format the format string for the output
* @param args arguments for the format string (variable number)
*/
private static void printIt (String format, Object ... args) {
StackTraceElement ste = (new Throwable()).getStackTrace()[2];
String msg = String.format("DEBUG %s[%d] %s() ", ste.getFileName(),
ste.getLineNumber(), ste.getMethodName())
+ Debug.format(format, args) +"\n";
System.out.flush();
ps.print(msg);
ps.flush();
}
/** An "extension" of String.format() to automatically convert
* arrays to Strings using the Arrays.toString() methods.
* Arrays of arrays are also handled. The argument list is exactly the same
* as that of String.format(), so any of the features of that
* code may be used. If there is only a single argument after the
* format and if that argument is an array, cast it to an Object using
* ((Object))
* @param format the format string for the output (use %s for arrays)
* @param args arguments for the format string (variable number)
* @return the formatted string with all array objects expanded
*/
public static String format (String format, Object ... args) {
for (int i = 0; i < args.length; i++) {
if ((args[i] != null) && args[i].getClass().isArray()) {
if (args[i] instanceof boolean[])
args[i] = Arrays.toString((boolean[]) args[i]);
else if (args[i] instanceof byte[])
args[i] = Arrays.toString((byte[]) args[i]);
else if (args[i] instanceof char[])
args[i] = Arrays.toString((char[]) args[i]);
else if (args[i] instanceof double[])
args[i] = Arrays.toString((double[]) args[i]);
else if (args[i] instanceof float[])
args[i] = Arrays.toString((float[]) args[i]);
else if (args[i] instanceof int[])
args[i] = Arrays.toString((int[]) args[i]);
else if (args[i] instanceof long[])
args[i] = Arrays.toString((long[]) args[i]);
else if (args[i] instanceof Object[])
args[i] = Arrays.deepToString((Object[]) args[i]);
else if (args[i] instanceof short[])
args[i] = Arrays.toString((short[]) args[i]);
}
}
return String.format(format, args);
}
/** Simple routine to print the fileName, lineNumber and methodName. Used
* for quick debugging to see if your code even gets to a particular place.
* @return always returns true. Allows you to "abuse" the
* assert statement for high performance.
*/
public static boolean HERE () {
if (debugLevel != 0)
printIt("HERE");
return true;
}
/** Print a message if the variable Debug.debugLevel is non-zero.
* The argument list is exactly the same as that of String.format(),
* so any of the features of that code may be used. See the documentation of
* Debug.format() for details on printing arrays.
* @param format the format string for the output
* @param args arguments for the format string (variable number)
* @return always returns true. Allows you to "abuse" the
* assert statement for high performance.
*/
public static boolean printf (String format, Object ... args) {
if (debugLevel != 0)
printIt(format, args);
return true;
}
/** Print a message if the parameter level is less than or equal to
* debugLevel. The argument list following level
* is exactly the same as that of String.format(), so any of
* the features of that code may be used. See the documentation of
* Debug.format() for details on printing arrays.
* @param level controls whether message is printed or not
* @param format the format string for the output
* @param args argments for the format string (variable number)
* @return always returns true. Allows you to "abuse" the
* assert statement for high performance.
*/
public static boolean printf (int level, String format, Object ... args) {
if (level <= debugLevel)
printIt(format, args);
return true;
}
/** Send debugging output to a file.
* @param fileName name of the file to send output to
*/
public static void toFile (String fileName) throws FileNotFoundException {
close();
ps = new PrintStream(fileName);
}
/** Close the output stream if it is not System.err. For use in
* conjunction with toFile()
*/
public static void close () {
if (ps != System.err) {
ps.close();
ps = System.err;
}
}
}