new xt shell based on JS transparency layer
[org.ibex.xt-crawshaw.git] / src / java / org / ibex / xt / shell / Command.java
index 7edc5ac..48fb329 100644 (file)
@@ -1,19 +1,24 @@
 package org.ibex.xt.shell;
 
-import java.io.StringReader;
 import java.io.Writer;
 import java.io.IOException;
 
 import java.util.*;
+import java.util.regex.*;
 
 import org.ibex.js.*;
+import org.ibex.util.*;
 
 public abstract class Command {
+    protected Shell shell;
+
+    protected Command(Shell s) { shell = s; }
+
     /** Returns the command name. */
     public abstract String name();
 
-    /** Returns single-line of usage information, eg. <tt>pattern]</tt> */
-    public abstract String usage();
+    /** Returns a single-line of parameter information, eg. <tt>[pattern]</tt> */
+    public abstract String params();
 
     /** Returns single-line description of command. */
     public abstract String summary();
@@ -22,38 +27,72 @@ public abstract class Command {
     public abstract String help();
 
     /** Writes result of execution, even if result is an error. */
-    public abstract void execute(Writer w, String[] args, Env env) throws IOException;
+    public abstract void execute(Writer w, String[] args) throws IOException;
+
+
+    /** Returns single-line of usage information, eg. <tt>usage: ls [pattern]</tt> */
+    public void usage(Writer w) throws IOException {
+        w.write("usage: ");
+        w.write(name());
+        w.write(" ");
+        w.write(params());
+        w.write("\n");
+    }
+
+    public List fromShellPath(String s) throws Shell.NoSuchPathException {
+        return fromShellPath(s, null);
+    }
+
+    /** Converts a shell path "/foo/etc/../bar" to its component form,
+     *  { "foo", "bar" } and loads it into the returned list.
+     *  Handles relative positioning to shell.getPath(). */
+    public List fromShellPath(String s, List l) throws Shell.NoSuchPathException {
+        if (s == null) return null;
+        if (l == null) l = new ArrayList();
+
+        s = s.trim().replace("/+", "/");
+        if (s.charAt(0) != '/') l.addAll(Arrays.asList(shell.getPath()));
+
+        StringTokenizer st = new StringTokenizer(s, "/");
+        while (st.hasMoreTokens()) {
+            String part = st.nextToken();
+            if (part == null || part.equals("") || part.equals(".")) continue;
+            else if (part.equals("..")) { if (l.size() > 0) l.remove(l.size() - 1); }
+            else l.add(part);
+        }
+
+        return l;
+    }
 
     public static class Help extends Command {
+        public Help(Shell s) { super(s); }
         public String name() { return "help"; }
-        public String usage() { return "[command name]"; }
+        public String params() { return "[command name]"; }
         public String summary() { return "Lists available commands."; }
         public String help() { return ""; }
 
-        public void execute(Writer w, String[] c, Env env) throws IOException {
+        public void execute(Writer w, String[] c) throws IOException {
             if (c.length > 1) {
-                Command cmd = env.command(c[1]);
+                Command cmd = shell.getCommand(c[1]);
                 if (c == null) {
                     w.write("help: ");
                     w.write(c[1]);
                     w.write(": command not found\n");
                 } else {
-                    w.write("usage: ");
-                    w.write(cmd.name());
-                    w.write(" ");
-                    w.write(cmd.usage());
-                    w.write("\n\n");
+                    cmd.usage(w);
+                    w.write("\n");
                     w.write(cmd.help());
                     w.write("\n");
                 }
             } else {
                 int len = 3;
-                for (int i=0; i < env.commands.length; i++)
-                    len = Math.max(env.commands[i].name().length(), len);
+                Command[] cmds = shell.getCommands();
+                for (int i=0; i < cmds.length; i++)
+                    len = Math.max(cmds[i].name().length(), len);
 
                 w.write("Available commands:\n");
-                for (int i=0; i < env.commands.length; i++) {
-                    Command cmd = env.commands[i];
+                for (int i=0; i < cmds.length; i++) {
+                    Command cmd = cmds[i];
                     w.write("  ");
                     w.write(cmd.name());
                     for (int j=len - cmd.name().length(); j >= 0; j--) w.write(" ");
@@ -67,47 +106,81 @@ public abstract class Command {
     }
 
     public static class Ls extends Command {
+        public Ls(Shell s) { super(s); }
         public String name() { return "ls"; }
-        public String usage() { return "[path]"; }
+        public String params() { return "[path]"; }
         public String summary() { return "List object entries."; }
         public String help() { return
             "Lists the keys in an object. Modelled after the UNIX ls command.";
         }
 
-        public void execute(Writer w, String[] c, Env env) throws IOException {
-            if (c.length > 2) { w.write(usage()); return; }
-            String p = env.path(c.length == 1 ? "*" : c[1]);
-
-            Request.Response ret = env.send(new Request.Key(p));
-            if (!(ret instanceof Request.Key.Res)) {
-                w.write("error: ");
-                w.write(ret.error().getMessage());
-                w.write("\n");
-            } else {
-                List l = ((Request.Key.Res)ret).keys();
-                Iterator i = l.iterator(); while (i.hasNext()) {
-                    w.write(i.next().toString());
+        public void execute(Writer w, String[] c) throws IOException {
+            if (c.length > 2) { usage(w); return; }
+            String p = c.length == 1 ? "*" : c[1];
+            if (p.endsWith("/")) p += "*";
+
+            try {
+                List path = fromShellPath(p);
+                Object key = path.remove(path.size() - 1);
+                Object po = shell.getFromPath(path.toArray());
+                if (po == null || !(po instanceof JS))
+                    throw new Shell.NoSuchPathException();
+                JS cur = (JS)po;
+
+                if (key instanceof String &&
+                        ((String)key).indexOf('*') >= 0) {
+                    String last = (String)key;
+                    last = last.replaceAll("\\.", "\\.");
+                    last = last.replaceAll("\\*", ".*");
+                    Pattern pat = Pattern.compile(last);
+                    Iterator i = cur.keys().iterator(); while (i.hasNext()) {
+                        Object o = i.next();
+                        if (o == null || !(o instanceof String)) continue;
+                        String s = (String)o;
+                        if (!pat.matcher(s).matches()) continue;
+                        w.write(s);
+                        w.write("\n");
+                    }
+                } else if (cur.containsKey(key)) {
+                    w.write(key.toString());
                     w.write("\n");
                 }
+            } catch (Shell.NoSuchPathException e) {
+                w.write("error: no such path: ");
+                w.write(p);
+                w.write("\n");
+            } catch (JSExn e) {
+                w.write("error: no such path: ");
+                w.write(p);
+                w.write(" (");
+                w.write(e.getMessage());
+                w.write(")\n");
             }
         }
     }
 
     public static class Pwd extends Command {
+        public Pwd(Shell s) { super(s); }
         public String name() { return "pwd"; }
-        public String usage() { return ""; }
+        public String params() { 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, Env env) throws IOException {
-            if (c.length != 1) { w.write(usage()); return; }
-            w.write(env.path.equals("") ? "." : env.path);
+        public void execute(Writer w, String[] c) throws IOException {
+            if (c.length != 1) { usage(w); return; }
+            Object[] path = shell.getPath();
+            for (int i=0; i < path.length; i++) {
+                w.write("/");
+                w.write(path[i].toString());
+            }
+            if (path.length == 0) w.write("/");
             w.write("\n");
         }
     }
 
     public static class Cd extends Command {
+        public Cd(Shell s) { super(s); }
         public String name() { return "cd"; }
-        public String usage() { return "[path]"; }
+        public String params() { return "[path]"; }
         public String summary() { return "Change current object."; }
         public String help() { return
             "Chnages the current object that all other commands use "+
@@ -116,97 +189,28 @@ public abstract class Command {
             "or an absolute path (e.g. cd .prevalent.myob).\n\n" +
             "To go up one level, cd .. can be used.";
         }
-        public void execute(Writer w, String[] c, Env env) throws IOException {
-            if (c.length > 2) w.write(usage());
-            else if (c.length == 1 || c[1].equals("") || c[1].equals(".")) env.path = "";
-            else if (c[1].equals("..")) {
-                String n = env.path;
-                n = n.substring(0, n.lastIndexOf('.'));
-                env.path = n;
-            } else {
-                String n = env.path(c[1]);
-
-                Request.Response ret = env.send(new Request.IsKey(n));
-                if (!(ret instanceof Request.IsKey.Res)) {
-                    w.write("error: ");
-                    w.write(ret.error().getMessage());
-                    w.write("\n");
-                } else {
-                    if (((Request.IsKey.Res)ret).exists()) {
-                        w.write("error: ");
-                        w.write(c[1]);
-                        w.write(": no such path\n");
-                    } else {
-                        env.path = n;
-                    }
-                }
+        public void execute(Writer w, String[] c) throws IOException {
+            if (c.length > 2) { usage(w); return; }
+            String path = c.length == 1 ? "/" : c[1];
+
+            try { shell.setPath(fromShellPath(path).toArray()); }
+            catch (Shell.NoSuchPathException e) {
+                w.write("error: no such path: ");
+                w.write(path);
+                w.write("\n");
             }
         }
     }
 
     public static class Rm extends Command {
+        public Rm(Shell s) { super(s); }
         public String name() { return "rm"; }
-        public String usage() { return "[options] [path]"; }
+        public String params() { return "[options] [path]"; }
         public String summary() { return "Removes objects."; }
         public String help() { return "Removes objects."; } // FIXME
-        public void execute(Writer w, String[] c, Env env) throws IOException {
-            if (c.length == 1) { w.write(usage()); }
-
-            Request.Key[] r = new Request.Key[c.length - 1];
-            for (int i=0; i < r.length; i++) r[i] = new Request.RemoveKey(env.path(c[i + 1]));
+        public void execute(Writer w, String[] c) throws IOException {
+            if (c.length == 1) { usage(w); return; }
 
-            Request.Response ret = env.send(new Request.Composite(r, false));
-            if (!(ret instanceof Request.Composite.Res)) {
-                w.write("error: ");
-                w.write(ret.error().getMessage());
-                w.write("\n");
-            } else {
-                Request.Response[] res = ((Request.Composite.Res)ret).responses();
-                for (int i=0; i < res.length; i++) {
-                    if (res[i] instanceof Request.RemoveKey.Res) {
-                        boolean rm = ((Request.RemoveKey.Res)res[i]).removed();
-                        if (!rm) {
-                            w.write("error: cannot remove '");
-                            w.write(c[i + 1]);
-                            w.write("': no such key\n");
-                        }
-                    } else {
-                        w.write("error: cannot remove '");
-                        w.write(c[i + 1]);
-                        w.write("': ");
-                        w.write(res[i].error().getMessage());
-                        w.write("\n");
-                    }
-                }
-            }
         }
     }
-
-    public static class Set extends Command {
-        public String name() { return "set"; }
-        public String usage() { return "[name] [object]"; }
-        public String summary() { return "Sets ."; }
-        public String help() { return "Removes objects."; } // FIXME
-
-        public void execute(Writer w, String[] c, Env env) throws IOException {
-            if (c.length < 3) { w.write(usage()); }
-
-            String m = c[1];
-            String s = "return (";
-            for (int i=2; i < c.length; i++) s += c[i];
-            s += ");";
-
-            JS js;
-            try { js = JS.fromReader("input", 0, new StringReader(s)); }
-            catch (IOException e) {
-                w.write("error: ");
-                w.write(e.getMessage());
-                w.write("\n");
-                return;
-            }
-
-            Request.Response ret = env.send(new Request.SetKey(env.path, m, js));
-        }
-    }
-
 }