package org.esxx.shell;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.URI;
import java.util.*;
import jline.*;
import org.esxx.*;
import org.esxx.util.*;
import org.mozilla.javascript.*;
import java.sql.SQLException;
public class Shell
implements Runnable {
public Shell(Context cx, Scriptable scope) {
this.cx = cx;
this.scope = scope;
}
public void run() {
try {
final ConsoleReader console = new ConsoleReader();
final StringBuilder sb = new StringBuilder();
console.addCompletor(new Completor() {
@SuppressWarnings("unchecked")
public int complete(String buffer, int cursor, List candidates) {
if (buffer.matches("\\s*\\\\h\\s.*" /* Help command */)) {
return helpCompletor.complete(buffer, cursor, candidates);
}
else {
return propCompletor.complete(buffer, cursor, candidates);
}
}
private Completor helpCompletor = new HelpCompletor(Shell.this);
private Completor propCompletor = new PropertyCompletor(scope);
});
console.setAutoprintThreshhold(150);
console.setUsePagination(true);
System.out.println("Welcome to the ESXX Shell!");
System.out.println("Enter JavaScript statements at the prompt. Tab completion is supported.");
System.out.println("Use Escape to cancel the current statement and Control-D or \\q to quit.");
System.out.println("\\h can be used to search the built-in help documents.");
console.addTriggeredAction((char) 27, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Clear current command and exit from readLine
sb.setLength(0);
console.exitReadLine(true);
}
});
int line_counter = 1;
boolean quit = false;
while (!quit) {
String prompt = line_counter == 1 ? "esxx> " : (line_counter + "> ");
String line = console.readLine(prompt);
if (line == null) {
console.printNewline();
break;
}
sb.append(line);
sb.append('\n');
++line_counter;
String statement = sb.toString().trim();
if (statement.length() == 0) {
line_counter = 1;
}
else if (statement.charAt(0) == '\\') {
char cmd = statement.length() >= 2 ? statement.charAt(1) : '\0';
switch (cmd) {
case 'h':
displayHelp(console, statement.substring(2));
break;
case 'q':
quit = true;
break;
default:
System.out.println("Unknown command");
console.beep();
break;
}
sb.setLength(0);
line_counter = 1;
}
else if (cx.stringIsCompilableUnit(statement)) {
evaluateString(cx, scope, statement);
sb.setLength(0);
line_counter = 1;
}
}
}
catch (Exception ex) {
ex.printStackTrace();
}
}
private void evaluateString(Context cx, Scriptable scope, String statement) {
Object result;
try {
result = cx.evaluateString(scope,
statement,
"ESXX Shell", 1,
null);
}
catch (Exception ex) {
result = ex;
}
JS.printObject(cx, scope, result);
}
private synchronized void displayHelp(ConsoleReader console, String args)
throws IOException, SQLException {
QueryCache help = getHelpQuery();
URI help_uri = getHelpURI();
String[] terms = args.split(" ");
ArrayList<Integer> final_docs = null;
for (String term : terms) {
if (!term.isEmpty()) {
term = term.toLowerCase();
ArrayQueryHandler qh = new ArrayQueryHandler(new String[] { term });
help.executeQuery(help_uri, null,
"select distinct dw.doc_id"
+ " from words w"
+ " inner join doc_words dw on dw.word_id = w.id"
+ " where w.word = {0}"
+ " order by doc_id",
qh);
ArrayList<Integer> docs = qh.<Integer>getColumn(0);
if (final_docs == null) {
final_docs = docs;
}
else {
final_docs.retainAll(docs);
}
}
}
if (final_docs == null) {
ArrayQueryHandler qh = new ArrayQueryHandler(null);
help.executeQuery(help_uri, null,
"select id from docs order by id",
qh);
final_docs = qh.<Integer>getColumn(0);
}
if (final_docs.size() == 0) {
System.out.println("No documents matched the given terms.");
}
else if (final_docs.size() > 1) {
ArrayQueryHandler qh = new ArrayQueryHandler(new Object[] { final_docs });
help.executeQuery(help_uri, null,
"select concat(section, '.', title) as name"
+ " from docs"
+ " where id in ({0})"
+ " order by name",
qh);
System.out.println("The following documents matched the given terms."
+ " Please be more specific.");
console.printColumns(qh.<String>getColumn(0));
}
else {
ArrayQueryHandler qh = new ArrayQueryHandler(new Object[] { final_docs.get(0) });
help.executeQuery(help_uri, null,
"select utf8tostring(expand(text))"
+ " from docs"
+ " where id = {0}",
qh);
console.printString((String) qh.getResult().get(0)[0]);
console.printNewline();
}
}
synchronized QueryCache getHelpQuery() {
if (helpQuery == null) {
helpQuery = new QueryCache(1, 60000, 10, 60000);
}
return helpQuery;
}
synchronized URI getHelpURI()
throws IOException {
if (helpDB == null) {
ESXX esxx = ESXX.getInstance();
helpDB = File.createTempFile(getClass().getName(), ".zip");
helpDB.deleteOnExit();
IO.copyStream(esxx.openCachedURI(URI.create("esxx-rsrc:esxx-help.zip")),
new FileOutputStream(helpDB));
helpURI = URI.create("jdbc:h2:zip:" + helpDB + "!/api;DB_CLOSE_DELAY=-1");
}
return helpURI;
}
private static File helpDB;
private static QueryCache helpQuery;
private static URI helpURI;
private Context cx;
private Scriptable scope;
}