improve ls output
[org.ibex.xt-crawshaw.git] / src / java / org / ibex / xt / shell / Request.java
1 package org.ibex.xt.shell;
2
3 import java.io.*;
4 import java.util.*;
5 import java.util.regex.*;
6
7 import org.ibex.js.*;
8
9 public abstract class Request implements Serializable {
10
11     public abstract Response process(JSScope root) throws JSExn;
12
13     public static class Response implements Serializable {
14         protected Exception ex;
15         public Response() { ex = null; }
16         public Response(Exception e) { ex = e; }
17         public Exception error() { return ex; }
18     }
19
20     public static class Key extends Request {
21         protected String path, matcher;
22         public Key() {}
23
24         /** Expects a shell-path that uses '.' as a seperator and * for wildcard. */
25         public Key(String c) {
26             int pos = c.lastIndexOf('.');
27             path = c.substring(0, pos);
28             matcher = c.substring(pos + 1).replaceAll("\\*+", ".*");
29         }
30
31         public Key(String path, String matcher) {
32             this.path = path; this.matcher = matcher;
33         }
34
35         /** Returns the object referenced by path. */
36         protected Object path(JSScope root) throws JSExn {
37             String p = path == null ? "" : path.replaceAll("\\.+", "\\.");
38             if (p.length() > 0 && p.charAt(0) == '.')
39                 p = p.substring(1);
40             if (p.length() > 0 && p.charAt(p.length() - 1) == '.')
41                 p = p.substring(0, p.length() - 1);
42
43             return p.equals("") ? root : root.get(p);
44         }
45
46         /** Returns the keys in <tt>js</tt> that match <tt>matcher</tt>. */
47         protected List matches(JS js) throws JSExn {
48             String m = matcher;
49
50             Pattern pat = Pattern.compile(m);
51             List keys = new ArrayList();
52
53             Iterator i = js.keys().iterator(); while(i.hasNext()) {
54                 String k = i.next().toString();
55                 if (pat.matcher(k).matches()) keys.add(k);
56             }
57
58             return keys;
59         }
60
61         /** Returns <tt>o</tt> cast as a JS if it is such and has keys,
62          *  otherwise returns null. */
63         protected JS keyed(Object o) {
64             // FIXME: replace this with a canHaveKeys() function in org.ibex.js.JS
65             return o == null || !(o instanceof JS) ||
66                        o instanceof JSDate || o instanceof Directory ||
67                        o instanceof Grammar || o instanceof JSMath ||
68                        o instanceof JSReflection || o instanceof JSRegexp ?
69                    null : (JS)o;
70         }
71
72         public Response process(JSScope root) throws JSExn {
73             JS js = keyed(path(root));
74
75             // if matcher is exact and a keyed object, return its keys
76             JS o = keyed(js.get(matcher));
77             if (o != null) { js = o; matcher = ".*"; }
78
79             return js == null ? new Res() : new Res(matches(js));
80         }
81
82         public static class Res extends Response {
83             private List keys;
84             public Res() { keys = null; }
85             public Res(List l) { keys = l; }
86             public List keys() { return keys; }
87             public boolean isPath() { return keys != null; }
88         }
89     }
90
91     public static class IsKey extends Key {
92         public IsKey() {}
93         public IsKey(String c) { super(c); }
94         public Response process(JSScope root) throws JSExn {
95             JS js = keyed(path(root));
96             return js == null ? new Res(false) : new Res(js.get(matcher) != null);
97         }
98
99         public static class Res extends Response {
100             private boolean exists;
101             public Res() {}
102             public Res(boolean e) { exists = e; }
103             public boolean exists() { return exists; }
104         }
105     }
106
107     public static class RemoveKey extends Key {
108         public RemoveKey(String c) { super(c); }
109         public RemoveKey(String p, String m) { super(p, m); }
110         public Response process(JSScope root) throws JSExn {
111             JS js = keyed(path(root));
112             if (js == null) throw new JSExn("no such path");
113             boolean rm = js.containsKey(matcher);
114             if (rm) js.remove(matcher);
115             return new Res(rm);
116         }
117
118         public static class Res extends Response {
119             private boolean removed;
120             public Res() { }
121             public Res(boolean rm) { removed = rm; }
122             public boolean removed() { return removed; }
123         }
124     }
125
126     public static class SetKey extends Key {
127         protected JS value;
128         public SetKey() {}
129         public SetKey(String p, String m, JS val) { super(p, m); this.value = val; }
130
131         public Response process(JSScope root) throws JSExn {
132             JS js = keyed(path(root));
133             if (js == null) throw new JSExn("no such path");
134
135             js.put(matcher, JS.eval(JS.cloneWithNewParentScope(
136                                     value, new JSResolve(root, js))));
137             return new Res(); // TODO: return put() value when it has one
138         }
139
140         public class Res extends Response {
141         }
142
143         public class JSResolve extends JSScope {
144             private JS path;
145             public JSResolve(JSScope parent, JS path) { super(parent); this.path = path; }
146             public Object get(Object key) throws JSExn {
147                 if (key != null && key instanceof String) {
148                     String k = (String)key;
149                     if (k.startsWith(".")) k = k.substring(1);
150                     else return path.get(k);
151                 }
152                 return super.get(key);
153             }
154             public void put(Object key, Object val) throws JSExn {
155                 if (key != null && key instanceof String) {
156                     String k = (String)key;
157                     if (k.startsWith(".")) k = k.substring(1);
158                     else { path.put(key, val); return; }
159                 }
160                 super.put(key, val);
161             }
162         }
163     }
164
165     /** Runs a series of requests. */
166     public static class Composite extends Request {
167         private boolean breakOnError = false;
168         private Request[] requests;
169         public Composite() {}
170         public Composite(Request[] r, boolean breakOnError) {
171             requests = r;
172             this.breakOnError = breakOnError;
173         }
174         public Composite(List r, boolean breakOnError) {
175             Request[] req = new Request[r.size()];
176             r.toArray(req);
177             requests = req;
178             this.breakOnError = breakOnError;
179         }
180
181         public Response process(JSScope root) {
182             Response[] res = new Response[requests.length];
183
184             for (int i=0; i < requests.length; i++) {
185                 try { res[i] = requests[i].process(root); }
186                 catch (JSExn e) {
187                     res[i] = new Response(e);
188
189                     if (breakOnError) {
190                         Response[] newres = new Response[i + 1];
191                         System.arraycopy(res, 0, newres, 0,  newres.length);
192                         res = newres;
193                         break;
194                     }
195                 }
196             }
197
198             return new Res(res);
199         }
200
201         public static class Res extends Response {
202             private Response[] responses;
203             public Res() {}
204             public Res(Response[] r) { responses = r; }
205             public Response get(int i) { return responses[i]; }
206             public int size() { return responses.length; }
207             public Response[] responses() { return responses; }
208         }
209     }
210 }