1 package org.lsst.ccs.shell;
2
3 import java.io.IOException;
4 import java.io.PrintWriter;
5 import java.util.List;
6 import jline.console.ConsoleReader;
7 import jline.console.completer.Completer;
8 import jline.console.history.History;
9 import org.apache.commons.cli.BasicParser;
10 import org.apache.commons.cli.CommandLine;
11 import org.apache.commons.cli.CommandLineParser;
12 import org.apache.commons.cli.HelpFormatter;
13 import org.apache.commons.cli.Options;
14 import org.lsst.ccs.command.CommandInvocationException;
15 import org.lsst.ccs.command.CommandSet;
16 import org.lsst.ccs.command.CommandSetBuilder;
17 import org.lsst.ccs.command.CompositeCommandSet;
18 import org.lsst.ccs.command.Dictionary;
19 import org.lsst.ccs.command.DictionaryCompleter;
20 import org.lsst.ccs.command.HelpGenerator;
21 import org.lsst.ccs.command.TokenizedCommand;
22 import org.lsst.ccs.command.annotations.Argument;
23 import org.lsst.ccs.command.annotations.Command;
24
25
26
27
28
29
30
31
32
33 public class JLineShell {
34
35 private final CommandSet commands;
36 private boolean exitRequested;
37 private final ConsoleReader reader;
38 private final PrintWriter printWriter;
39 private CommandInvocationException lastException;
40
41
42
43
44
45
46
47
48
49
50 public JLineShell(CommandSet userCommands) throws IOException {
51 this(userCommands, new ConsoleReader(), null);
52 }
53
54 public JLineShell(CommandSet userCommands, ConsoleReader reader) {
55 this(userCommands, reader, null);
56 }
57
58 public JLineShell(CommandSet userCommands, String prompt) throws IOException {
59 this(userCommands, new ConsoleReader(), prompt);
60 }
61
62 public JLineShell(CommandSet userCommands, ConsoleReader reader, String prompt) {
63 this.reader = reader;
64 reader.setPrompt(prompt != null ? prompt : ">>>");
65 printWriter = new PrintWriter(reader.getOutput(), true);
66 printWriter.println("Type help for list of available commands");
67 CompositeCommandSet allCommands = new CompositeCommandSet();
68 CommandSetBuilder builder = new CommandSetBuilder();
69 allCommands.add(builder.buildCommandSet(new BuiltIns()));
70 allCommands.add(userCommands);
71 Dictionary commandDictionary = allCommands.getCommandDictionary();
72 final DictionaryCompleter dictionaryCompleter = new DictionaryCompleter(commandDictionary);
73 allCommands.add(builder.buildCommandSet(new HelpGenerator(printWriter, commandDictionary)));
74 commands = allCommands;
75 Completer completer = new Completer() {
76 @Override
77 public int complete(String string, int i, List<CharSequence> list) {
78 return dictionaryCompleter.complete(string, i, list);
79 }
80 };
81 reader.addCompleter(completer);
82 reader.setCompletionHandler(new CommandCompletionHandler());
83 }
84
85
86
87
88
89
90
91 public void run() throws IOException {
92 while (!exitRequested) {
93 String command = reader.readLine();
94 if (command == null) {
95 printWriter.println();
96 break;
97 }
98 try {
99 TokenizedCommand tc = new TokenizedCommand(command);
100 if (!tc.isEmpty()) {
101 Object result = commands.invoke(tc);
102 if (result != null) {
103 printWriter.println(result.toString());
104 }
105 }
106 } catch (CommandInvocationException ex) {
107 printWriter.println(ex.getMessage());
108 lastException = ex;
109 }
110 }
111 }
112
113
114
115
116
117 public enum SetCommands {
118
119 PROMPT
120 };
121
122
123
124
125 public class BuiltIns {
126
127 @Command(description = "Exit from the shell")
128 public void exit() {
129 exitRequested = true;
130 }
131
132 @Command(description = "Show command history")
133 public void history() {
134 History history = reader.getHistory();
135 for (int i = 0; i < history.size(); i++) {
136 printWriter.printf("%3d: %s\n", i, history.get(i));
137 }
138 }
139
140 @Command(description = "Show the full stacktrace of the most recent error", alias = "st")
141 public void stacktrace() {
142 if (lastException != null) {
143 lastException.printStackTrace(printWriter);
144 }
145 }
146
147 @Command(description = "Modify various settings")
148 public void set(@Argument(name = "item") SetCommands what, @Argument(name = "value") String value) {
149 switch (what) {
150 case PROMPT:
151 reader.setPrompt(value);
152 }
153 }
154 }
155
156
157
158
159
160
161
162
163
164 public static void main(String[] argv) throws Exception {
165
166 Options shellOptions = new Options();
167 shellOptions.addOption("h","help",false,"Print the help message");
168
169 shellOptions.addOption("dc","dictionaryClasses",true,"The comma separated list of classes to be used to build the command dictionary.\n"
170 + "The dictionary classes must have an empty constructor in order to be loaded.");
171
172 shellOptions.addOption("p", "prompt", true, "Set the initial prompt");
173
174 CommandLineParser parser = new BasicParser();
175 CommandLine line = parser.parse(shellOptions, argv, true);
176
177 if (line.hasOption("help")) {
178 HelpFormatter formatter = new HelpFormatter();
179 formatter.printHelp(80,"CommandShell", "", shellOptions, "", true);
180 } else {
181
182 CommandSetBuilder builder = new CommandSetBuilder();
183 CompositeCommandSet compositeSet = new CompositeCommandSet();
184
185 String dictionaryClasses = line.getOptionValue("dictionaryClasses");
186 if (dictionaryClasses != null) {
187 String[] classTokens = dictionaryClasses.split(",");
188 for (int i = 0; i < classTokens.length; i++) {
189 String className = classTokens[i];
190 try {
191 Class<?> c = Class.forName(className);
192 Object obj = c.newInstance();
193 compositeSet.add(builder.buildCommandSet(obj));
194 } catch (Exception e) {
195 System.out.println("** Skipping class " + className
196 + ". It could not be loaded or created.");
197 e.printStackTrace();
198 }
199 }
200
201 }
202 JLineShell shell = new JLineShell(compositeSet, line.getOptionValue("prompt"));
203 shell.run();
204 }
205 System.exit(0);
206 }
207 }