--- /dev/null
+package org.ibex.xt;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import org.ibex.util.*;
+import org.ibex.util.Collections;
+import org.ibex.js.*;
+
+import org.prevayler.*;
+
+public class Shell {
+
+ public static void main(String[] args) throws Exception {
+ if (args.length == 0 || args.length > 2|| !args[0].startsWith("http://")) {
+ printUsage(); return;
+ }
+ Shell shell = new Shell(new URL(args[0]));
+
+ if (args.length == 2) {
+ System.out.println(shell.execute(args[1]));
+ } else {
+ shell.listen(new InputStreamReader(System.in), new OutputStreamWriter(System.out));
+ }
+ }
+
+ private static void printUsage() {
+ System.out.println("Usage: xish url [command]");
+ }
+
+ /** URL of server. */
+ private URL server;
+
+ /** Current JS path using '.' as a seperator. */
+ private String pwd = ".";
+
+ /** Create a new Shell using the given url for the server. */
+ public Shell(URL url) { server = url; }
+
+ public void listen(Reader r, Writer w) throws IOException {
+ LineNumberReader in = new LineNumberReader(r);
+ PrintWriter out = new PrintWriter(w);
+
+ out.println("Ibex xt shell. Type help or exit.");
+ out.print("xt: ");
+ out.flush();
+
+ String line;
+ String buffer = "";
+ while ((line = in.readLine()) != null) {
+ if (line.length() > 0) {
+ if (line.startsWith("exit")) return;
+ if (line.charAt(line.length() - 1) == '\\') {
+ buffer += line.substring(0, line.length() - 1);
+ out.print('>');
+ out.flush(); continue;
+ }
+
+ buffer += line;
+ }
+
+ if (buffer.length() > 0) {
+ execute(buffer, out);
+ buffer = "";
+ } else {
+ out.println("");
+ }
+ out.print("xt: ");
+ out.flush();
+ }
+ }
+
+ public String execute(String cmd) throws IOException {
+ StringWriter s = new StringWriter();
+ execute(cmd, s);
+ return s.toString();
+ }
+
+ public void execute(String cmd, Writer w) throws IOException {
+ if (cmd == null || cmd.length() == 0) return;
+ String[] c = cmd.split(" ");
+
+ //#switch(c[0])
+ case "ls":
+ if (c.length > 2) { w.write("usage: ls [path]\n"); return; }
+ String path = pwd;
+ String matcher = ".*";
+ if (c.length == 2) {
+ int pos = c[1].lastIndexOf('/');
+ path += c[1].substring(0, pos);
+ path.replaceAll("/", "\\.");
+ if (pos + 1 < c[1].length()) {
+ matcher = c[1].substring(pos + 1);
+ matcher.replaceAll("\\.", "\\.");
+ matcher.replaceAll("\\*", ".*");
+ }
+ }
+
+ Object ret = send(new KeyRequest(path, matcher));
+ if (ret == null) {
+ error(w, "returned object is null"); return;
+ } else if (ret instanceof JSExn) {
+ error(w, (JSExn)ret); return;
+ } else if (ret instanceof List) {
+ List l = (List)ret; Collections.sort(l);
+ Iterator i = l.iterator(); while (i.hasNext()) {
+ w.write(" ");
+ w.write(i.next().toString());
+ w.write("\n");
+ }
+ return;
+ } else {
+ error(w, "returned object of unknown type: "+ret.getClass().getName());return;
+ }
+ case "pwd":
+ w.write(pwd.replace('.', '/'));
+ w.write("\n");
+ case "cd":
+ throw new Error("not yet implemented");
+ case "help":
+ w.write("Available commands:\n");
+ w.write(" ls - list entries in current object\n");
+ w.write(" pwd - path of current object\n");
+ w.write(" help - this message\n");
+ w.write(" exit - close xt shell\n");
+ //#end
+ }
+
+ public void error(Writer w, String s) throws IOException {
+ w.write("Unexpected error: ");
+ w.write(s);
+ w.write("\n");
+ }
+
+ public void error(Writer w, JSExn e) throws IOException {
+ w.write("\nerror: ");
+ w.write(e.getMessage());
+ w.write('\n');
+ }
+
+ public Object send(Request request) throws IOException {
+ URLConnection c = server.openConnection();
+ ((HttpURLConnection)c).setRequestMethod("POST");
+ c.setDoOutput(true);
+ c.connect();
+
+ ObjectOutputStream out = new ObjectOutputStream(c.getOutputStream());
+ out.writeObject(request);
+ out.close();
+
+ try {
+ return new ObjectInputStream(c.getInputStream()).readObject();
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ throw new IOException("unexpected ClassNotFoundException");
+ }
+ }
+
+ public static abstract class Request implements Serializable {
+ public abstract Object process(JSScope root) throws JSExn;
+ }
+
+ public static class KeyRequest extends Request {
+ private String path, matcher;
+ public KeyRequest() {}
+ public KeyRequest(String path, String matcher) {
+ this.path = path; this.matcher = matcher;
+ }
+
+ /** Returns a List. */
+ public Object process(JSScope root) throws JSExn {
+ String p = path == null ? "" : path.replaceAll("\\.+", "\\.");
+ if (p.length() > 0 && p.charAt(0) == '.') p = p.substring(1);
+ if (p.length() > 0 && p.charAt(p.length() - 1) == '.') p = p.substring(0, p.length() - 1);
+ System.out.println("searching path '"+p+"' for pattern '"+matcher+"'");
+
+ Object o = p.equals("") ? root : root.get(p);
+ if (o == null || o instanceof JSDate ||
+ o instanceof JSArray ||
+ !(o instanceof JS)) {
+ System.out.println("hit bad object: "+o+", class="+
+ (o == null ? null : o.getClass().getName()));
+ throw new JSExn("path /" + p + " does not exist");
+ } else {
+ Pattern pat = Pattern.compile(matcher);
+ List keys = new ArrayList();
+
+ Iterator i = ((JS)o).keys().iterator(); while(i.hasNext()) {
+ String k = i.next().toString();
+ if (pat.matcher(k).matches()) keys.add(k);
+ }
+
+ return keys;
+ }
+ }
+ }
+
+ public static class ExecRequest extends Request {
+ private JS exec;
+ public ExecRequest() {}
+ public ExecRequest(JS exec) { this.exec = exec; }
+ public ExecRequest(String source) throws IOException, JSExn {
+ this(new StringReader(source));
+ }
+ public ExecRequest(Reader source) throws IOException, JSExn {
+ exec = JS.fromReader("xsh", 0, source);
+ }
+
+ /** Returns the result of <tt>JS.eval()</tt>. */
+ public Object process(JSScope root) throws JSExn {
+ return JS.eval(JS.cloneWithNewParentScope(exec, root));
+ }
+ }
+}