summaryrefslogtreecommitdiff
path: root/BJC-Utils2/src/main/java/bjc/utils/cli/CLICommander.java
blob: 9fff1ac235654235579832cae49b0b027b510ef8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package bjc.utils.cli;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Scanner;

/**
 * Runs a CLI interface from the provided set of streams.
 *
 * @author ben
 *
 */
public class CLICommander {
	/*
	 * The streams used for input and normal/error output.
	 */
	private InputStream	input;
	private OutputStream	output;
	private OutputStream	error;

	/*
	 * The command mode to start execution in.
	 */
	private ICommandMode initialMode;

	/**
	 * Create a new CLI interface powered by streams.
	 *
	 * @param input
	 *                The stream to get user input from.
	 * @param output
	 *                The stream to send normal output to.
	 * @param error
	 *                The stream to send error output to.
	 */
	public CLICommander(InputStream input, OutputStream output, OutputStream error) {
		if(input == null)       throw new NullPointerException("Input stream must not be null");
		else if(output == null) throw new NullPointerException("Output stream must not be null");
		else if(error == null)  throw new NullPointerException("Error stream must not be null");

		this.input  = input;
		this.output = output;
		this.error  = error;
	}

	/**
	 * Start handling commands from the given input stream.
	 */
	public void runCommands() {
		/*
		 *  Setup output streams.
		 */
		PrintStream normalOutput = new PrintStream(output);
		PrintStream errorOutput  = new PrintStream(error);

		/*
		 * Set up input streams.
		 *
		 * We're suppressing the warning because we might use the input
		 * stream multiple times.
		 */
		@SuppressWarnings("resource")
		Scanner inputSource = new Scanner(input);

		/*
		 * The mode currently being used to handle commands.
		 *
		 * Used to preserve the initial mode.
		 */
		ICommandMode currentMode = initialMode;

		/*
		 *  Process commands until we're told to stop.
		 */
		while(currentMode != null) {
			/*
			 * Print out the command prompt, using a custom prompt
			 * if one is specified.
			 */
			if(currentMode.isCustomPromptEnabled()) {
				normalOutput.print(currentMode.getCustomPrompt());
			} else {
				normalOutput.print(currentMode.getName() + ">> ");
			}

			/*
			 *  Read in a command.
			 */
			String currentLine = inputSource.nextLine();

			/*
			 *  Handle commands we can handle.
			 */
			if(currentMode.canHandle(currentLine)) {
				String[] commandTokens = currentLine.split(" ");
				String[] commandArgs   = null;
				int      argCount      = commandTokens.length;

				/*
				 *  Parse args if they are present.
				 */
				if(argCount > 1) {
					commandArgs = Arrays.copyOfRange(commandTokens, 1, argCount);
				}

				/*
				 *  Process command.
				 */
				currentMode = currentMode.process(commandTokens[0], commandArgs);
			} else {
				errorOutput.print("Error: Unrecognized command " + currentLine);
			}
		}

		normalOutput.print("Exiting now.");
	}

	/**
	 * Set the initial command mode to use.
	 *
	 * @param initialMode
	 *                The initial command mode to use.
	 */
	public void setInitialCommandMode(ICommandMode initialMode) {
		if(initialMode == null) throw new NullPointerException("Initial mode must be non-zero");

		this.initialMode = initialMode;
	}
}