+package org.ibex.xt;
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.io.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+public class Template extends Node.Stream.Filter implements Node.Stream.Functor {
+
+ static Template newTemplate(Servlet.ServletScope servletscope, JSScope scope, String str) {
+ try {
+ File f = new File(str);
+ if (!f.exists()) f = new File(str + ".xt");
+ if (!f.exists()) f = new File(str + ".xml");
+ return new Template(servletscope, scope, new InputStreamReader(new FileInputStream(f)));
+ } catch (Exception e) { throw new RuntimeException(e); }
+ }
+
+ static JSScope copyNodeToScope(Node n, JSScope scope) {
+ try {
+ for(int i=0; i<n.numattrs; i++) {
+ scope.declare(n.attrs[i*2]);
+ scope.put(n.attrs[i*2], n.attrs[i*2+1]);
+ }
+ return scope;
+ } catch (Exception e) { throw new RuntimeException(e); }
+ }
+
+ private class JSRewriter extends Node.Stream {
+ private Node.Stream in;
+ private JSScope scope;
+ public JSRewriter(Node.Stream in, JSScope scope) { this.in = in; this.scope = scope; }
+ protected boolean _read(Node n) { if (!in.read(n)) return false; transform(n, scope); return true; }
+ }
+
+ public static Node transform(Node n, JSScope scope) {
+ if (n.cdata != null) n.cdata = eval(n.cdata, scope).toString();
+ else for(int i=1; i<n.numattrs*2; i+=2) n.attrs[i] = eval(n.attrs[i], scope).toString();
+ return n;
+ }
+
+ private static Object eval(String s, JSScope scope) {
+ if (s == null) return null;
+ StringBuffer ret = new StringBuffer();
+ for(boolean first = true; s.indexOf("${") != -1; first = false) {
+ ret.append(s.substring(0, s.indexOf("${")));
+ String s2 = s.substring(s.indexOf("${")+2);
+ Object app = exec("return (" + s2.substring(0, s2.indexOf('}')) + ");\n", scope);
+ s = s.substring(s.indexOf('}') + 1);
+ //if (first && s.trim().length() == 0) return app;
+ if (!(app == null || app instanceof String || app instanceof Number || app instanceof Boolean))
+ throw new RuntimeException("javascripts within ${...} can only return strings, numbers, and booleans; not a " +
+ app.getClass().getName());
+ ret.append(app == null ? "null" : app.toString());
+ }
+ ret.append(s);
+ return ret.toString();
+ }
+
+ public static Object exec(String s, JSScope scope) {
+ try {
+ return JS.eval(JS.cloneWithNewParentScope(JS.fromReader("input", 0, new StringReader(s)), scope));
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+ }
+
+ private JSScope scope;
+ private Servlet.ServletScope servletscope;
+ private Node.Stream children;
+ public Node.Stream wrap(Node.Stream children) { this.children = children; return this; }
+ public Template(Servlet.ServletScope servletscope, JSScope scope, Reader template) {
+ super(new Node.Stream.FromXML(template));
+ this.scope = scope;
+ this.servletscope = servletscope;
+ }
+ public boolean _read(Node n) { boolean ret = __read(n); if (ret) transform(n, scope); return ret; }
+ public boolean __read(final Node n) {
+ if (!upstreamRead(n)) return false;
+ if (n.cdata != null) return true;
+ final String uri = n.uri;
+ final String name = n.name;
+ if (uri.indexOf(':') == -1)
+ throw new RuntimeException("uri does not contain a colon: " + uri + " (tag name " + name + ")");
+ final String method = uri.substring(0, uri.indexOf(':'));
+ final String rest = uri.substring(uri.indexOf(':')+1);
+ if (uri.equals("http://www.w3.org/1999/xhtml")) { return true;
+ } else if (method.equals("webinf")) {
+ return graft(newTemplate(servletscope, copyNodeToScope(transform(n, scope), new JSScope(servletscope)),
+ servletscope.getRealPath("/") + "/WEB-INF/" + rest + name), n).upstreamRead(n);
+ } else if (uri.equals("http://xt.ibex.org/")) {
+ //#switch(name)
+ case "if":
+ transform(n, scope);
+ return graft("true".equals(n.attr("if")) ? new DropTag() : new DropAll(), n).upstreamRead(n);
+ case "js": return graft(new JsTag(scope), n).upstreamRead(n);
+ case "foreach": return graft(new ForEach(n, scope), n).upstreamRead(n);
+ case "children":
+ if (children == null) return true;
+ graft(new Node.Stream.ConstantFunctor(children), n);
+ children = null;
+ return upstreamRead(n);
+ //#end
+ return true;
+ } else if (method.equals("java")) {
+ try { return graft((Node.Stream.Functor)Class.forName(rest).newInstance(), n).upstreamRead(n); }
+ catch (Exception e) { throw new RuntimeException(e); }
+ }
+ throw new RuntimeException("Unknown namespace URI " + uri);
+ }
+
+ private class ForEach extends Node.Stream.Filter implements Node.Stream.Functor {
+ private Node[] nodes = null;
+ private Vec array = new Vec();
+ private JSScope scope;
+ public ForEach(Node n, JSScope s) {
+ super(Node.Stream.NULL);
+ Vec v = ((JSArray)exec("return (" + n.attr("in").toString() + ");", this.scope = s)).toVec();
+ while(true) { Object o = v.pop(); if (o == null) break; array.push(o); }
+ }
+ public Node.Stream wrap(Node.Stream kids) {
+ Vec nodes = new Vec();
+ Node n2 = new Node();
+ while(kids.read(n2)) nodes.addElement(new Node(n2));
+ nodes.copyInto(this.nodes = new Node[nodes.size()]);
+ return this;
+ }
+ protected boolean _read(Node n) {
+ if (upstreamRead(n)) return true;
+ if (array.size() == 0) return false;
+ JSScope scope2 = new JSScope(scope);
+ try { scope2.declare("x"); scope2.put("x", array.pop()); } catch (JSExn e) { throw new RuntimeException(e); }
+ return graft(new ConstantFunctor(new JSRewriter(new Node.Stream() {
+ private int i = 0;
+ protected boolean _read(Node n) {
+ if (i>=nodes.length) return false;
+ n.copyFrom(nodes[i++]);
+ return true;
+ } }, scope2)), n).upstreamRead(n);
+ }
+ }
+
+ private class DropTag implements Node.Stream.Functor {
+ public Node.Stream wrap(Node.Stream kids) {
+ return kids;
+ } }
+
+ private class DropAll implements Node.Stream.Functor {
+ public Node.Stream wrap(Node.Stream kids) {
+ return new Node.Stream() { public boolean _read(Node n) { return false; } };
+ } }
+
+ private class JsTag implements Node.Stream.Functor {
+ JSScope scope;
+ public JsTag(JSScope scope) { this.scope = scope; }
+ public Node.Stream wrap(final Node.Stream s) {
+ return new Node.Stream() {
+ protected boolean _read(Node n) {
+ boolean ret = s.read(n);
+ if (ret && n.cdata != null) {
+ System.err.println("exec("+n.cdata+")");
+ exec(n.cdata, scope);
+ return _read(n);
+ }
+ return ret;
+ }
+ };
+ }
+ }
+}