/* "Copyright (c) 2012-2019 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 turn 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. */ private static java.io.PrintStream ps = System.err; /** Define prefix of flag to turn on debugging */ private static final String prefix = "-debug"; /** Purely static, no instances may be created */ private Debug() { } /** This routine will initialize the class from the arguments * passed to main(). If the first argument is, or starts with * -debug the class is initialized and the first parameter is * removed from the args. The first parameter may have one of * three forms: *
    *
  1. -debug - turn on debugging at level 1
  2. *
  3. -debugValue - set debugging level to Value * (e.g. -debug5)
  4. *
  5. -debugValue@fileName set debugging level to Value * and send debugging output to the file fileName. If you use * this option, the file must b closed using Debug.close().
  6. *
*
NOTE: If any initialization fails, an error message is printed * and the program terminates. * @param args the array of arguments passed to main * @return args with the first value removed (if appropriate) */ public static String[] init (String[] args) { int numArgs = args.length; if ((numArgs > 0) && (args[0].startsWith(prefix))) { String debugArg = args[0].substring(prefix.length()); int newLen = numArgs - 1; String[] newArgs = new String[newLen]; System.arraycopy(args, 1, newArgs, 0, newLen); args = newArgs; debugLevel = 1; if (debugArg.length() > 0) { String[] parts = debugArg.split("@"); try { switch (parts.length) { case 2: toFile(parts[1]); // FALL THRU case 1: debugLevel = Integer.parseInt(parts[0]); break; default: throw new Exception("Wrong number of debug args"); } } catch (Exception ex) { System.err.printf("Bad debug specifier '%s'\n", debugArg); System.err.println(ex); System.exit(-1); } } } 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() %s\n", ste.getFileName(), ste.getLineNumber(), ste.getMethodName(), Debug.format(format, args)); System.out.flush(); ps.print(msg); // "\n" in msg outputs single value (ps.println() is two) 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 higher 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 higher 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 arguments for the format string (variable number) * @return always returns true. Allows you to "abuse" the * assert statement for higher performance. */ public static boolean printf (int level, String format, Object ... args) { if ((level > 0) && (level <= debugLevel)) printIt(format, args); return true; } /** Send debugging output to a file. This may be called from the * init() method. If you send output to a file, be sure and close * it with Debug.close(). * @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() or initialization of output to a file * in Debug.init(). */ public static void close () { if (ps != System.err) { ps.close(); ps = System.err; } } }