introduce a shell command object
[org.ibex.xt-crawshaw.git] / src / java / org / ibex / xt / Shell.java
index 0f228ab..1596630 100644 (file)
@@ -4,15 +4,11 @@ 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 {
@@ -22,7 +18,7 @@ public class Shell {
         Shell shell = new Shell(new URL(args[0]));
 
         if (args.length == 2) {
-            System.out.println(shell.execute(args[1]));
+            // FIXME
         } else {
             shell.listen(new InputStreamReader(System.in), new OutputStreamWriter(System.out));
         }
@@ -32,11 +28,17 @@ public class Shell {
         System.out.println("Usage: xish url [command]");
     }
 
+    protected Command[] commands = new Command[] {
+        new LsCommand(),
+        new PwdCommand(),
+        new HelpCommand()
+    };
+
     /** URL of server. */
-    private URL server;
+    protected URL server;
 
     /** Current JS path using '.' as a seperator. */
-    private String pwd = ".";
+    protected String pwd = ".";
 
     /** Create a new Shell using the given url for the server. */
     public Shell(URL url) { server = url; }
@@ -45,7 +47,7 @@ public class Shell {
         LineNumberReader in = new LineNumberReader(r);
         PrintWriter out = new PrintWriter(w);
 
-        out.println("Ibex xt shell. Type help or exit.");
+        out.println("ibex xt shell: type help or exit");
         out.print("xt: ");
         out.flush();
 
@@ -64,99 +66,168 @@ public class Shell {
             }
 
             if (buffer.length() > 0) {
-                execute(buffer, out);
+                String[] c = buffer.split(" ");
+                int i=0; while (i < commands.length) {
+                    if (commands[i].name().equals(c[0])) {
+                        commands[i].execute(out, c);
+                        out.write('\n'); break;
+                    }
+                    i++;
+                }
+                if (i == commands.length) {
+                    out.write(c[0]);
+                    w.write(": command not found\n");
+                }
                 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();
+    private String cookie = null;
+    public Object send(Request request) throws IOException {
+        URLConnection c = server.openConnection();
+        ((HttpURLConnection)c).setRequestMethod("POST");
+        c.setDoOutput(true);
+        if (cookie != null) c.setRequestProperty("Cookie", cookie);
+
+        c.connect();
+
+        ObjectOutputStream out = new ObjectOutputStream(c.getOutputStream());
+        out.writeObject(request);
+        out.close();
+
+        String cook = c.getHeaderField("Set-Cookie");
+        if (cook != null && !cook.equals("")) cookie = cook.substring(0, cook.indexOf(';'));
+
+        try {
+            return new ObjectInputStream(c.getInputStream()).readObject();
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+            throw new IOException("unexpected ClassNotFoundException");
+        }
+    }
+
+    public abstract class Command {
+        /** Returns the command name. */
+        public abstract String name();
+
+        /** Returns single-line of usage information, eg. <tt>pattern]</tt> */
+        public abstract String usage();
+
+        /** Returns single-line description of command. */
+        public abstract String summary();
+
+        /** Returns multi-line description. */
+        public abstract String help();
+
+        /** Writes result of execution, even if result is an error. */
+        public abstract void execute(Writer w, String[] args) throws IOException;
+    }
+
+    /** Returns the command matching the given name. */
+    protected Command command(String name) {
+        for (int i=0; i < commands.length; i++)
+            if (commands[i].name().equals(name)) return commands[i];
+        return null;
+    }
+
+    public class HelpCommand extends Command {
+        public String name() { return "help"; }
+        public String usage() { return "[command name]"; }
+        public String summary() { return "Lists available commands."; }
+        public String help() { return ""; }
+
+        public void execute(Writer w, String[] c) throws IOException {
+            if (c.length > 1) {
+                Command cmd = command(c[1]);
+                if (c == null) {
+                    w.write("help: ");
+                    w.write(c[1]);
+                    w.write(": command not found");
+                } else {
+                    w.write("usage: ");
+                    w.write(cmd.name());
+                    w.write(" ");
+                    w.write(cmd.usage());
+                    w.write("\n\n");
+                    w.write(cmd.help());
+                    w.write("\n");
+                }
+            } else {
+                int len = 3;
+                for (int i=0; i < commands.length; i++)
+                    len = Math.max(commands[i].name().length(), len);
+
+                w.write("Available commands:\n");
+                for (int i=0; i < commands.length; i++) {
+                    Command cmd = commands[i];
+                    w.write("  ");
+                    w.write(cmd.name());
+                    for (int j=len - cmd.name().length(); j >= 0; j--) w.write(" ");
+                    w.write(" - ");
+                    w.write(cmd.summary());
+                    w.write("\n");
+                }
+                w.write("\nFor usage details, type help [command name].");
+            }
+        }
     }
 
-    public void execute(String cmd, Writer w) throws IOException {
-        if (cmd == null || cmd.length() == 0) return;
-        String[] c = cmd.split(" ");
+    public class LsCommand extends Command {
+        public String name() { return "ls"; }
+        public String usage() { return "[path]"; }
+        public String summary() { return "List object entries."; }
+        public String help() { return
+            "The ls command, modelled after the UNIX ls command lists the " +
+            "keys in an object. ";
+        }
+
+        public void execute(Writer w, String[] c) throws IOException {
+            if (c.length > 2) { w.write(usage()); return; }
 
-        //#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('/');
+                int pos = c[1].lastIndexOf('/') + 1;
                 path += c[1].substring(0, pos);
-                path.replaceAll("/", "\\.");
-                if (pos + 1 < c[1].length()) {
-                    matcher = c[1].substring(pos + 1);
-                    matcher.replaceAll("\\.", "\\.");
-                    matcher.replaceAll("\\*", ".*");
+                path = path.replaceAll("/", "\\.");
+                if (pos < c[1].length()) {
+                    matcher = c[1].substring(pos);
+                    matcher = matcher.replaceAll("\\.", "\\.");
+                    matcher = matcher.replaceAll("\\*", ".*");
                 }
             }
 
             Object ret = send(new KeyRequest(path, matcher));
             if (ret == null) {
-                error(w, "returned object is null"); return;
+                w.write("error: (unexpected) returned object is null");
             } else if (ret instanceof JSExn) {
-                error(w, (JSExn)ret); return;
+                w.write("error: ");
+                w.write(((JSExn)ret).getMessage());
             } else if (ret instanceof List) {
                 List l = (List)ret; Collections.sort(l);
+                w.write("total items: ");
+                w.write(l.size()+"");
                 Iterator i = l.iterator(); while (i.hasNext()) {
-                    w.write("  ");
+                    w.write("\n  ");
                     w.write(i.next().toString());
-                    w.write("\n");
                 }
-                return;
             } else {
-                error(w, "returned object of unknown type: "+ret.getClass().getName());return;
+                w.write("error: (unexpected) returned object is of unknown type: ");
+                w.write(ret.getClass().getName());
             }
-        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 class PwdCommand extends Command {
+        public String name() { return "pwd"; }
+        public String usage() { return ""; }
+        public String summary() { return "Path to current object."; }
+        public String help() { return "Print the path to the current object."; }
+        public void execute(Writer w, String[] c) throws IOException {
+            w.write(c.length == 1 ? pwd.replace('.', '/') : usage());
         }
     }