/* "Copyright (c) 2012-2015 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.util.Arrays; import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; import java.util.NoSuchElementException; import java.util.Random; /** * This is a base class for several assignments you will write. Do NOT * change anything in this file. Its purpose it to executes commands, * one command per line. Commands may be entered via the keyboard or read from * a file. It contains a variey of utility routines to make it easy to enter * for null values and to enter arrays (String/int) * as a list of comma separated values. *

* In writing your code, you may be tempted to use System.out.println() * to determine what is happening. As an alternative, the method * debug() is provided. The nice thing about debug() * in comparison to System.out.println() is that debug() * only prints when debug is turn on. You can turn * debug on and off by giving the debug command to this program. * Thus, you can write debug code, but not need to change your program in any * way before turning it in. If you use System.out.println() 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 Shell { /** A class to help end processing that is only used internally by Shell. */ private static class StopProcessing extends RuntimeException { private static final long serialVersionUID = 1L; // keep Eclipse happy public StopProcessing (String msg) { super(msg); } } /** Any non-zero value turns debugging on */ protected static int debug = 0; /** Random number generator */ private static Random random = null; /** 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 - argments for the format string (variable number) */ 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); } /** Print a message if the debug value 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 * Shell.format() for details on printing arrays. * @param format - the format string for the output * @param args - argments for the format string (variable number) */ public static void debug (String format, Object ... args) { if (debug != 0) { System.out.println(format(format, args)); } } /** Get the shells random number generator. If one has not yet been created, * create one. To get repeatable results, use the random * command with a seed. */ public static Random getRandom() { if (random == null) random = new Random(); return random; } /** Display a brief help summary for commands implemented by this class*/ public void showHelp() { System.out.println("Shell commands:"); System.out.println(" debug "); System.out.println(" echo "); System.out.println(" exit"); System.out.println(" help"); System.out.println(" input "); System.out.println(" random [seed]"); System.out.println(); } /** Filter input for some special cases. * @param input - the original string * @return The string is trimmed. * If it is the String "empty!", return "". * If it is the String "null!", return null. * Otherwise, return the trimmed String. Override this for more special * conditions. */ protected String filter (String input) { input = input.trim(); if ("null!".equals(input)) input = null; else if ("empty!".equals(input)) input = ""; return input; } /** Retrieve a String from the scanner, filtering it for convenience. A * future enhancement will include quoting so that a "parameter" may have * embedded blanks, quotes, etc. * @param scanner - retrieve String from this Scanner. * @return the filtered String (possibly null). */ public String getStringOrNull (Scanner scanner) { String result = null; if (scanner.hasNext()) { result = filter(scanner.next()); } return result; } /** Retrieve a String from the scanner using nextLine(), to get * everything else in the line and filter it for special values. This differs * from Scaner.nextLine() in that the reult is trimmed of * leading and trailing blanks, and the special string * null!null and * empty!"". * @param scanner - retrieve String from this Scanner. * @return the trimmed, filtered String (possibly null). */ public String rest (Scanner scanner) { String result = null; if (scanner.hasNextLine()) { result = filter(scanner.nextLine()); } return result; } /** Get an array of String separated by commas from the scanner. * A future enhancement will allow quoting so that individual elements may * contain escaped commas. * @param scanner - source of the array * @return an array (possibly null, containing the * filtered values. */ public String[] getStrArray (Scanner scanner) { String[] result = null; String cslString = getStringOrNull(scanner); if (cslString != null) { if (cslString.length() == 0) { result = new String[0]; } else { result = cslString.split(","); for (int i = 0; i < result.length; i++) { result[i] = filter(result[i]); } } } return result; } /** Get an array of int separated by commas from the scanner. * @param scanner - source of the array * @return an array (possibly null, containing the values */ public int[] getIntArray (Scanner scanner) { return strArrayToIntArray(getStrArray(scanner)); } /** Convert an array of String to an array of int. * @param strResult - array of Strings to be converted to ints * @return Each String is converted to an int */ public int[] strArrayToIntArray (String[] strResult) { int[] result = null; if (strResult != null) { result = new int[strResult.length]; for (int i = 0; i < result.length; i++) { result[i] = Integer.parseInt(strResult[i]); } } return result; } /** Process one command. * @param cmd - the command to process * @param params - a string containing the parameter(s) (if any) */ public void processOneCommand (String cmd, String params) throws FileNotFoundException, NoSuchElementException { Scanner paramScanner = new Scanner(params); switch (cmd) { case "debug": debug = paramScanner.nextInt(); break; case "echo": System.out.println(params); break; case "exit": throw new StopProcessing ("exit command"); case "help": case "?": showHelp(); break; case "input": processCmdsFromFile(paramScanner.next()); break; case "random": if (paramScanner.hasNextLong()) random = new Random(paramScanner.nextLong()); else random = new Random(); break; default: System.err.println("Unknown cmd: " + cmd + " " + params); } paramScanner.close(); } /** Command interpreter to test code. Process the commands where * each command is contained on a separate line. End-of-line comments * begin with '#'. Comments are removed and the resulting * string trimmed. Empty lines are ignored. * @param scanner - the source of the commands */ public void processCommands (Scanner scanner) { while (scanner.hasNextLine()) { String line = scanner.nextLine().trim(); int pound = line.indexOf('#'); // end-of-line comments start with # if (pound != -1) line = line.substring(0, pound).trim(); // remove comment if (line.length() == 0) continue; // ignore blank lines Scanner cs = new Scanner(line); String cmd = cs.next().toLowerCase(); // make cmd case independent String params = (cs.hasNextLine() ? cs.nextLine().trim() : ""); cs.close(); try { processOneCommand(cmd, params); } catch (StopProcessing sp) { break; } catch (FileNotFoundException fnfe) { System.err.println(fnfe); } catch (NoSuchElementException nsee) { System.err.println("syntax error: " + line); if (debug != 0) nsee.printStackTrace(); } catch (Throwable t) { t.printStackTrace(); } } } /** Process commands from a file. * @param fileName - name of file containing commands */ public void processCmdsFromFile (String fileName) { try { processCommands(new Scanner(new File(fileName))); } catch (FileNotFoundException fnfe) { System.err.println("can not input commands from file '" + fileName + "'"); System.err.flush(); } } /** Process commands from System.in. */ public void processCommands() { processCommands(new Scanner(System.in)); } /** Provide a main() like method that can be inherited and/or overridden. * * @param args - arguments to process. If args is * null or of length 0, commands are taken from * System.in. If args.length > 0, the * args are assumed to be one or more file to be processed * using the input command.. */ public void ooMain (String[] args) { if ((args == null) || (args.length == 0)) { System.out.println("Enter commands:"); processCommands(); } else for (int i = 0; i < args.length; i++) { processCmdsFromFile(args[i]); } } /** Entry point to shell. * @param args - an array of Strings */ public static void main (String[] args) { Shell shell = new Shell(); shell.ooMain(args); } }