0f228abb75656e3430a0f18804af158f5edcd0c4
[org.ibex.xt-crawshaw.git] / src / java / org / ibex / xt / Shell.java
1 package org.ibex.xt;
2
3 import java.io.*;
4 import java.net.*;
5 import java.util.*;
6 import java.util.regex.*;
7 import javax.servlet.*;
8 import javax.servlet.http.*;
9
10 import org.ibex.util.*;
11 import org.ibex.util.Collections;
12 import org.ibex.js.*;
13
14 import org.prevayler.*;
15
16 public class Shell {
17
18     public static void main(String[] args) throws Exception {
19         if (args.length == 0 || args.length > 2||  !args[0].startsWith("http://")) {
20             printUsage(); return;
21         }
22         Shell shell = new Shell(new URL(args[0]));
23
24         if (args.length == 2) {
25             System.out.println(shell.execute(args[1]));
26         } else {
27             shell.listen(new InputStreamReader(System.in), new OutputStreamWriter(System.out));
28         }
29     }
30
31     private static void printUsage() {
32         System.out.println("Usage: xish url [command]");
33     }
34
35     /** URL of server. */
36     private URL server;
37
38     /** Current JS path using '.' as a seperator. */
39     private String pwd = ".";
40
41     /** Create a new Shell using the given url for the server. */
42     public Shell(URL url) { server = url; }
43
44     public void listen(Reader r, Writer w) throws IOException {
45         LineNumberReader in = new LineNumberReader(r);
46         PrintWriter out = new PrintWriter(w);
47
48         out.println("Ibex xt shell. Type help or exit.");
49         out.print("xt: ");
50         out.flush();
51
52         String line;
53         String buffer = "";
54         while ((line = in.readLine()) != null) {
55             if (line.length() > 0) {
56                 if (line.startsWith("exit")) return;
57                 if (line.charAt(line.length() - 1) == '\\') {
58                     buffer += line.substring(0, line.length() - 1);
59                     out.print('>');
60                     out.flush(); continue;
61                 }
62
63                 buffer += line;
64             }
65
66             if (buffer.length() > 0) {
67                 execute(buffer, out);
68                 buffer = "";
69             } else {
70                 out.println("");
71             }
72             out.print("xt: ");
73             out.flush();
74         }
75     }
76
77     public String execute(String cmd) throws IOException {
78         StringWriter s = new StringWriter();
79         execute(cmd, s);
80         return s.toString();
81     }
82
83     public void execute(String cmd, Writer w) throws IOException {
84         if (cmd == null || cmd.length() == 0) return;
85         String[] c = cmd.split(" ");
86
87         //#switch(c[0])
88         case "ls":
89             if (c.length > 2) { w.write("usage: ls [path]\n"); return; }
90             String path = pwd;
91             String matcher = ".*";
92             if (c.length == 2) {
93                 int pos = c[1].lastIndexOf('/');
94                 path += c[1].substring(0, pos);
95                 path.replaceAll("/", "\\.");
96                 if (pos + 1 < c[1].length()) {
97                     matcher = c[1].substring(pos + 1);
98                     matcher.replaceAll("\\.", "\\.");
99                     matcher.replaceAll("\\*", ".*");
100                 }
101             }
102
103             Object ret = send(new KeyRequest(path, matcher));
104             if (ret == null) {
105                 error(w, "returned object is null"); return;
106             } else if (ret instanceof JSExn) {
107                 error(w, (JSExn)ret); return;
108             } else if (ret instanceof List) {
109                 List l = (List)ret; Collections.sort(l);
110                 Iterator i = l.iterator(); while (i.hasNext()) {
111                     w.write("  ");
112                     w.write(i.next().toString());
113                     w.write("\n");
114                 }
115                 return;
116             } else {
117                 error(w, "returned object of unknown type: "+ret.getClass().getName());return;
118             }
119         case "pwd":
120             w.write(pwd.replace('.', '/'));
121             w.write("\n");
122         case "cd":
123             throw new Error("not yet implemented");
124         case "help":
125             w.write("Available commands:\n");
126             w.write("  ls    - list entries in current object\n");
127             w.write("  pwd   - path of current object\n");
128             w.write("  help  - this message\n");
129             w.write("  exit  - close xt shell\n");
130         //#end
131     }
132
133     public void error(Writer w, String s) throws IOException {
134         w.write("Unexpected error: ");
135         w.write(s);
136         w.write("\n");
137     }
138
139     public void error(Writer w, JSExn e) throws IOException {
140         w.write("\nerror: ");
141         w.write(e.getMessage());
142         w.write('\n');
143     }
144
145     public Object send(Request request) throws IOException {
146         URLConnection c = server.openConnection();
147         ((HttpURLConnection)c).setRequestMethod("POST");
148         c.setDoOutput(true);
149         c.connect();
150
151         ObjectOutputStream out = new ObjectOutputStream(c.getOutputStream());
152         out.writeObject(request);
153         out.close();
154
155         try {
156             return new ObjectInputStream(c.getInputStream()).readObject();
157         } catch (ClassNotFoundException e) {
158             e.printStackTrace();
159             throw new IOException("unexpected ClassNotFoundException");
160         }
161     }
162
163     public static abstract class Request implements Serializable {
164         public abstract Object process(JSScope root) throws JSExn;
165     }
166
167     public static class KeyRequest extends Request {
168         private String path, matcher;
169         public KeyRequest() {}
170         public KeyRequest(String path, String matcher) {
171             this.path = path; this.matcher = matcher;
172         }
173
174         /** Returns a List. */
175         public Object process(JSScope root) throws JSExn {
176             String p = path == null ? "" : path.replaceAll("\\.+", "\\.");
177             if (p.length() > 0 && p.charAt(0) == '.') p = p.substring(1);
178             if (p.length() > 0 && p.charAt(p.length() - 1) == '.') p = p.substring(0, p.length() - 1);
179             System.out.println("searching path '"+p+"' for pattern '"+matcher+"'");
180
181             Object o = p.equals("") ? root : root.get(p);
182             if (o == null || o instanceof JSDate ||
183                              o instanceof JSArray ||
184                              !(o instanceof JS)) {
185                 System.out.println("hit bad object: "+o+", class="+
186                     (o == null ? null : o.getClass().getName()));
187                 throw new JSExn("path /" + p + " does not exist");
188             } else {
189                 Pattern pat = Pattern.compile(matcher);
190                 List keys = new ArrayList();
191
192                 Iterator i = ((JS)o).keys().iterator(); while(i.hasNext()) {
193                     String k = i.next().toString();
194                     if (pat.matcher(k).matches()) keys.add(k);
195                 }
196
197                 return keys;
198             }
199         }
200     }
201
202     public static class ExecRequest extends Request {
203         private JS exec;
204         public ExecRequest() {}
205         public ExecRequest(JS exec) { this.exec = exec; }
206         public ExecRequest(String source) throws IOException, JSExn {
207             this(new StringReader(source));
208         }
209         public ExecRequest(Reader source) throws IOException, JSExn {
210             exec = JS.fromReader("xsh", 0, source);
211         }
212
213         /** Returns the result of <tt>JS.eval()</tt>. */
214         public Object process(JSScope root) throws JSExn {
215             return JS.eval(JS.cloneWithNewParentScope(exec, root));
216         }
217     }
218 }