/* "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; } } }