/* "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.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.
*
* @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);
}
}
/** Random number generator */
private static Random random = null;
/** 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 result 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 < strResult.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.debugLevel = paramScanner.nextInt();
break;
case "echo":
System.out.println(params);
break;
case "exit":
case "quit":
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.debugLevel != 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();
}
}
/** 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) {
args = Debug.init(args);
if ((args == null) || (args.length == 0)) {
System.out.println("Enter commands:");
processCommands(new Scanner(System.in));
}
else for (String fileName : args) {
processCmdsFromFile(fileName);
}
}
/** Entry point to shell.
* @param args - an array of Strings
*/
public static void main (String[] args) {
Shell shell = new Shell();
shell.ooMain(args);
}
}