1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
7 import org.ibex.util.*;
12 import javax.servlet.*;
13 import javax.servlet.http.*;
15 public class Template extends Node.Stream.Filter implements Node.Stream.Functor {
17 static Template newTemplate(Servlet.ServletScope servletscope, Scope scope, String str) {
19 File f = new File(str);
20 if (!f.exists()) f = new File(str + ".xt");
21 if (!f.exists()) f = new File(str + ".xml");
22 return new Template(servletscope, scope, new InputStreamReader(new FileInputStream(f)));
23 } catch (Exception e) { throw new RuntimeException(e); }
26 static Scope copyNodeToScope(Node n, Scope scope) {
28 for(int i=0; i<n.numattrs; i++) {
29 scope.declare(n.attrs[i*2]);
30 scope.put(JSU.S(n.attrs[i*2]), JSU.S(n.attrs[i*2+1]));
33 } catch (Exception e) { throw new RuntimeException(e); }
36 private class JSRewriter extends Node.Stream {
37 private Node.Stream in;
39 public JSRewriter(Node.Stream in, Scope scope) { this.in = in; this.scope = scope; }
40 protected boolean _read(Node n) { if (!in.read(n)) return false; transform(n, scope); return true; }
43 public static Node transform(Node n, Scope scope) {
45 if (n.cdata != null) n.cdata = eval(n.cdata, scope).toString();
46 else for(int i=1; i<n.numattrs*2; i+=2) n.attrs[i] = eval(n.attrs[i], scope).toString();
48 } catch (JSExn e) { throw new RuntimeException(e); }
51 private static Object eval(String s, Scope scope) throws JSExn {
52 if (s == null) return null;
53 StringBuffer ret = new StringBuffer();
54 for(boolean first = true; s.indexOf("${") != -1; first = false) {
55 ret.append(s.substring(0, s.indexOf("${")));
56 String s2 = s.substring(s.indexOf("${")+2);
57 JS app = exec("return (" + s2.substring(0, s2.indexOf('}')) + ");\n", scope);
58 s = s.substring(s.indexOf('}') + 1);
59 //if (first && s.trim().length() == 0) return app;
60 if (!(app == null || app instanceof JSPrimitive))
61 throw new RuntimeException("javascripts within ${...} can only return strings, numbers, and booleans; not a " +
62 app.getClass().getName());
63 ret.append(app == null ? "null" : JSU.toString(app));
66 return ret.toString();
69 public static JS exec(String s, Scope scope) {
71 return JSU.cloneWithNewGlobalScope(JSU.fromReader("input", 0, new StringReader(s)), scope).call(null,null);
72 } catch (Exception e) {
74 throw new RuntimeException(e);
79 private Servlet.ServletScope servletscope;
80 private Node.Stream children;
81 public Node.Stream wrap(Node.Stream children) { this.children = children; return this; }
82 public Template(Servlet.ServletScope servletscope, JS scope, Reader template) {
83 super(new Node.Stream.FromXML(template));
84 this.scope = new Scope(scope);
85 this.servletscope = servletscope;
87 public boolean _read(Node n) { boolean ret = __read(n); if (ret) transform(n, scope); return ret; }
88 public boolean __read(final Node n) {
89 if (!upstreamRead(n)) return false;
90 if (n.cdata != null) return true;
92 if (uri == null) uri = "http://www.w3.org/1999/xhtml"; // FIXME FIXME FIXME!!!!
93 final String name = n.name;
94 if (uri.indexOf(':') == -1)
95 throw new RuntimeException("uri does not contain a colon: " + uri + " (tag name " + name + ")");
96 final String method = uri.substring(0, uri.indexOf(':'));
97 final String rest = uri.substring(uri.indexOf(':')+1);
98 if (uri.equals("http://www.w3.org/1999/xhtml")) { return true;
99 } else if (method.equals("webinf")) {
100 return graft(newTemplate(servletscope, copyNodeToScope(transform(n, scope), new Scope(servletscope)),
101 servletscope.getRealPath("/") + "/WEB-INF/" + rest + name), n).upstreamRead(n);
102 } else if (uri.equals("http://xt.ibex.org/form")) {
103 return graft(new InputTag(n.name), n).upstreamRead(n);
104 } else if (uri.equals("http://xt.ibex.org/")) {
106 case "form": return graft(new FormTag(n.attr("class")), n).upstreamRead(n);
107 case "input": return graft(new InputTag(n.attr("field")), n).upstreamRead(n);
110 return graft((Node.Stream.Functor)("true".equals(n.attr("if"))?new DropTag():new DropAll()), n).upstreamRead(n);
111 case "js": return graft(new JsTag(scope), n).upstreamRead(n);
112 case "foreach": return graft(new ForEach(n, scope), n).upstreamRead(n);
114 if (children == null) return true;
115 graft(new Node.Stream.ConstantFunctor(children), n);
117 return upstreamRead(n);
120 } else if (method.equals("java")) {
121 try { return graft((Node.Stream.Functor)Class.forName(rest).newInstance(), n).upstreamRead(n); }
122 catch (Exception e) { throw new RuntimeException(e); }
124 throw new RuntimeException("Unknown namespace URI " + uri);
127 private class ForEach extends Node.Stream.Filter implements Node.Stream.Functor {
128 private Node[] nodes = null;
129 private Vec array = new Vec();
131 public ForEach(Node n, Scope s) {
132 super(Node.Stream.NULL);
134 JSArray a = ((JSArray)exec("return (" + n.attr("in").toString() + ");", this.scope = s));
136 JS o = a.call(JSU.S("pop"), new JS[] { });
137 if (o == null) break;
141 throw new RuntimeException(e);
144 public Node.Stream wrap(Node.Stream kids) {
145 Vec nodes = new Vec();
146 Node n2 = new Node();
147 while(kids.read(n2)) nodes.addElement(new Node(n2));
148 nodes.copyInto(this.nodes = new Node[nodes.size()]);
151 protected boolean _read(Node n) {
152 if (upstreamRead(n)) return true;
153 if (array.size() == 0) return false;
154 Scope scope2 = new Scope(scope);
155 try { scope2.declare("x"); scope2.put(JSU.S("x"), (JS)array.pop()); } catch (JSExn e) { throw new RuntimeException(e); }
156 return graft(new ConstantFunctor(new JSRewriter(new Node.Stream() {
158 protected boolean _read(Node n) {
159 if (i>=nodes.length) return false;
160 n.copyFrom(nodes[i++]);
162 } }, scope2)), n).upstreamRead(n);
166 private class InputTag implements Node.Stream.Functor {
167 final String fieldName;
168 public InputTag(String fieldName) { this.fieldName = fieldName; }
169 public Node.Stream wrap(Node.Stream kids) {
171 return new Node.Stream.FromXML(new StringReader(servletscope.currentForm.emit(fieldName)));
172 } catch (Exception e) { Log.warn(this, e); return null; }
176 private class FormTag implements Node.Stream.Functor {
177 final String classname;
178 public FormTag(String classname) { this.classname = classname; }
179 public Node.Stream wrap(final Node.Stream kids) {
181 servletscope.currentForm = (Form)Class.forName(classname).newInstance();
182 Log.warn("emit", servletscope.currentForm.emit());
183 return new Node.Stream() {
184 boolean done = false;
185 public boolean _read(Node n) {
186 if (done) return kids._read(n);
190 n.uri = "http://www.w3.org/1999/xhtml";
192 n.attrs = new String[] { "action", "/servlet/"+classname };
196 } catch (Exception e) {
202 private class DropTag implements Node.Stream.Functor {
203 public Node.Stream wrap(Node.Stream kids) {
207 private class DropAll implements Node.Stream.Functor {
208 public Node.Stream wrap(Node.Stream kids) {
209 return new Node.Stream() { public boolean _read(Node n) { return false; } };
212 private class JsTag implements Node.Stream.Functor {
214 public JsTag(Scope scope) { this.scope = scope; }
215 public Node.Stream wrap(final Node.Stream s) {
216 return new Node.Stream() {
217 protected boolean _read(Node n) {
218 boolean ret = s.read(n);
219 if (ret && n.cdata != null) {
220 System.err.println("exec("+n.cdata+")");
221 exec(n.cdata, scope);