extra comments
[org.ibex.xt-crawshaw.git] / src / java / org / ibex / xt / shell / Command.java
index 48fb329..a55ded4 100644 (file)
@@ -1,5 +1,6 @@
 package org.ibex.xt.shell;
 
+import java.io.StringReader;
 import java.io.Writer;
 import java.io.IOException;
 
@@ -9,6 +10,11 @@ import java.util.regex.*;
 import org.ibex.js.*;
 import org.ibex.util.*;
 
+/** Provides implementations for the shell commands. General contract
+ *  is commands named after standard unix tools (eg. rm, mkdir) are
+ *  as safe as possible. Extra functions such as replace have no such
+ *  saftey features.
+ */
 public abstract class Command {
     protected Shell shell;
 
@@ -39,14 +45,14 @@ public abstract class Command {
         w.write("\n");
     }
 
-    public List fromShellPath(String s) throws Shell.NoSuchPathException {
+    public List fromShellPath(String s) throws Shell.BadPathException {
         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 {
+    public List fromShellPath(String s, List l) throws Shell.BadPathException {
         if (s == null) return null;
         if (l == null) l = new ArrayList();
 
@@ -124,7 +130,7 @@ public abstract class Command {
                 Object key = path.remove(path.size() - 1);
                 Object po = shell.getFromPath(path.toArray());
                 if (po == null || !(po instanceof JS))
-                    throw new Shell.NoSuchPathException();
+                    throw new Shell.BadPathException();
                 JS cur = (JS)po;
 
                 if (key instanceof String &&
@@ -145,7 +151,7 @@ public abstract class Command {
                     w.write(key.toString());
                     w.write("\n");
                 }
-            } catch (Shell.NoSuchPathException e) {
+            } catch (Shell.BadPathException e) {
                 w.write("error: no such path: ");
                 w.write(p);
                 w.write("\n");
@@ -159,6 +165,192 @@ public abstract class Command {
         }
     }
 
+    public static class Replace extends Command {
+        public Replace(Shell s) { super(s); }
+        public String name() { return "replace"; }
+        public String params() { return "[key] [value]"; }
+        public String summary() { return "Sets a key to a specific value."; }
+        public String help() { return
+            "Sets a key to a specific value. This function accepts " +
+            "an xt shell path for the key name (eg. /foo/bar), and a " +
+            "script object for the value (eg. {}, [], \"foo\").\n\n" +
+
+            "If the key does not already exist, it is created, but an " +
+            "error is thrown if the parent object of the key does not " +
+            "exist.\n\n" +
+
+            "WARNING: This function is dangerous. It gives you a " +
+            "quick and easy way to replace your entire data set with "+
+            "an empty object.";
+        }
+        public void execute(Writer w, String[] c) throws IOException {
+            if (c.length < 3) { usage(w); return; }
+            try {
+                List path = fromShellPath(c[1]);
+                Object key = path.remove(path.size() - 1);
+                Object po = shell.getFromPath(path.toArray());
+
+                if (po == null || !(po instanceof JS))
+                    throw new Shell.BadPathException();
+                JS parent = (JS)po;
+
+                String func = "prevalent.";
+                for (int i=0; i < path.size(); i++)
+                    func += path.get(i) + ".";
+                func += key + " = ";
+                for (int i=2; i < c.length; i++)
+                    func += c[i];
+                func += ";\n";
+
+                shell.transaction(JS.fromReader(
+                    "replace-transaction", 0, new StringReader(func)));
+
+            } catch (JSExn e) {
+                w.write("error: cannot replace '");
+                w.write(c[1]);
+                w.write("': ");
+                w.write(e.getMessage());
+                w.write("\n");
+            } catch (Shell.BadPathException e) {
+                w.write("error: cannot replace '");
+                w.write(c[1]);
+                w.write("': ");
+                w.write(e.getMessage() == null ? "no such path" : e.getMessage());
+                w.write("\n");
+            }
+        }
+    }
+
+    public static class Mkdir extends Command {
+        public Mkdir(Shell s) { super(s); }
+        public String name() { return "mkdir"; }
+        public String params() { return "[path]"; }
+        public String summary() { return "Creates a new object ready to handle keys."; }
+        public String help() { return
+            "Creates a new object ready to handle keys. This function " +
+            "is similar to calling replace [path] {}, only it will not " +
+            "overwrite an existing key.";
+        }
+        public void execute(Writer w, String[] c) throws IOException {
+            if (c.length != 2) { usage(w); return; }
+            try {
+                List path = fromShellPath(c[1]);
+                Object key = path.remove(path.size() - 1);
+                Object po = shell.getFromPath(path.toArray());
+
+                if (po == null || !(po instanceof JS))
+                    throw new Shell.BadPathException();
+
+                JS parent = (JS)po;
+                if (parent.containsKey(key))
+                    throw new Shell.BadPathException("already exists");
+
+                String func = "prevalent.";
+                for (int i=0; i < path.size(); i++)
+                    func += path.get(i) + ".";
+                func += key + " = {};\n";
+
+                shell.transaction(JS.fromReader(
+                    "mkdir-transaction", 0, new StringReader(func)));
+
+            } catch (JSExn e) {
+                w.write("error: cannot create '");
+                w.write(c[1]);
+                w.write("': ");
+                w.write(e.getMessage());
+                w.write("\n");
+            } catch (Shell.BadPathException e) {
+                w.write("error: cannot create '");
+                w.write(c[1]);
+                w.write("': ");
+                w.write(e.getMessage() == null ? "no such path" : e.getMessage());
+                w.write("\n");
+            }
+        }
+    }
+
+    public static class Rm extends Command {
+        public Rm(Shell s) { super(s); }
+        public String name() { return "rm"; }
+        public String params() { return "[options] [path]"; }
+        public String summary() { return "Removes objects."; }
+        public String help() { return
+            "Removes objects. If any one of the specified paths does " +
+            "not exist, the entire remove process is cancelled."; }
+        public void execute(Writer w, String[] c) throws IOException {
+            if (c.length == 1) { usage(w); return; }
+
+            boolean force = false; // FIXME provide ability to set
+
+            StringBuffer func = new StringBuffer();
+
+            for (int ic=1; ic < c.length; ic++) {
+                String p = c.length == 1 ? "*" : c[ic];
+                if (p.endsWith("/")) p += "*";
+
+                try {
+                    // get the base of the path
+                    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.BadPathException();
+                    JS cur = (JS)po;
+
+                    if (cur.containsKey(key)) {
+                        Object o = cur.get(key);
+                        if (!force && o != null && o instanceof JS && ((JS)o).keys().size() > 0)
+                            throw new Shell.BadPathException("key is not empty");
+                        func.append("prevalent.");
+                        for(int i=0; i < path.size(); i++) {
+                            func.append(path.get(i)); func.append('.'); }
+                        func.append("Delete(\"");
+                        func.append(key.toString());
+                        func.append("\");\n");
+                    } else if (key instanceof String && ((String)key).indexOf('*') >= 0) {
+                        String last = (String)key;
+                        last = last.replaceAll("\\.", "\\.");
+                        last = last.replaceAll("\\*", ".*");
+                        Pattern pat = Pattern.compile(last);
+                        Collection curkeys = cur.keys();
+                        if (curkeys.size() == 0) throw new Shell.BadPathException();
+                        Iterator it = curkeys.iterator(); while (it.hasNext()) {
+                            Object o = it.next();
+                            if (o == null || !(o instanceof String)) continue;
+                            String s = (String)o;
+                            if (!pat.matcher(s).matches()) continue;
+
+                            func.append("prevalent.");
+                            for(int i=0; i < path.size(); i++) {
+                                func.append(path.get(i)); func.append('.'); }
+                            func.append("Delete(\"");
+                            func.append(s);
+                            func.append("\");\n");
+                        }
+                    } else throw new Shell.BadPathException();
+
+                } catch (JSExn e) {
+                    w.write("error: cannot remove '");
+                    w.write(p);
+                    w.write("': ");
+                    w.write(e.getMessage());
+                    w.write("\n");
+                    return;
+                } catch (Shell.BadPathException e) {
+                    w.write("error: cannot remove '");
+                    w.write(p);
+                    w.write("': ");
+                    w.write(e.getMessage() != null ? e.getMessage() : "no such path");
+                    w.write("\n");
+                    return;
+                }
+            }
+
+            shell.transaction(JS.fromReader(
+                "rm-transaction", 0, new StringReader(func.toString())));
+        }
+    }
+
     public static class Pwd extends Command {
         public Pwd(Shell s) { super(s); }
         public String name() { return "pwd"; }
@@ -194,7 +386,7 @@ public abstract class Command {
             String path = c.length == 1 ? "/" : c[1];
 
             try { shell.setPath(fromShellPath(path).toArray()); }
-            catch (Shell.NoSuchPathException e) {
+            catch (Shell.BadPathException e) {
                 w.write("error: no such path: ");
                 w.write(path);
                 w.write("\n");
@@ -202,15 +394,4 @@ public abstract class Command {
         }
     }
 
-    public static class Rm extends Command {
-        public Rm(Shell s) { super(s); }
-        public String name() { return "rm"; }
-        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) throws IOException {
-            if (c.length == 1) { usage(w); return; }
-
-        }
-    }
 }