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