initial support for XML.Document
authorcrawshaw <crawshaw@ibex.org>
Tue, 16 Nov 2004 12:01:08 +0000 (12:01 +0000)
committercrawshaw <crawshaw@ibex.org>
Tue, 16 Nov 2004 12:01:08 +0000 (12:01 +0000)
darcs-hash:20041116120108-2eb37-e4527797d2bd4a0a62891d5853c0108595ccdab5.gz

src/ibex/xt/JSElement.java [new file with mode: 0644]
src/ibex/xt/Prevalence.java [moved from src/org/ibex/xt/Prevalence.java with 94% similarity]
src/ibex/xt/Servlet.java [moved from src/org/ibex/xt/Servlet.java with 64% similarity]
src/ibex/xt/Template.java [new file with mode: 0644]
src/org/ibex/xt/Node.java [deleted file]
src/org/ibex/xt/Template.java [deleted file]

diff --git a/src/ibex/xt/JSElement.java b/src/ibex/xt/JSElement.java
new file mode 100644 (file)
index 0000000..1cd6879
--- /dev/null
@@ -0,0 +1,146 @@
+package ibex.xt;
+
+import ibex.util.XML;
+import org.ibex.js.JS;
+import org.ibex.js.JSScope;
+
+import ibex.collection.*;
+import java.util.*;
+
+import java.io.StringReader;
+import java.io.Writer;
+import java.io.IOException;
+
+public class JSElement extends JSScope implements XML.Element {
+    protected XML.Element wrapped;
+
+    /** Creates an Element around <tt>wrapped</tt>, replacing
+     *  references to it in its parent and children with this object. */
+    public JSElement(XML.Element wrapped) {
+        super(findScope(wrapped));
+        this.wrapped = wrapped;
+
+        // remap parent and children
+        if (wrapped.getParent() != null) {
+            List c = wrapped.getParent().getChildren();
+            c.remove(wrapped); c.add(this);
+        }
+        List c = wrapped.getChildren();
+        for (int i=c.size(); i >= 0; i--) ((XML.Block)c.get(i)).setParent(this);
+    }
+
+    public void toXML(Writer w) throws IOException {
+        // grab all related attributes
+        try {
+            XML.Attributes a = getAttributes();
+            for(int i=0; i < a.attrSize(); i++) {
+                if (!"http://xt.ibex.org/".equals(a.getUri(i))) continue;
+                declare(a.getKey(i));
+                put(a.getKey(i), eval(a.getVal(i)));
+            }
+        } catch (Exception e) { throw new RuntimeException(e); }
+    }
+
+    private Object eval(String s) {
+        if (s == null) return null;
+        StringBuffer ret = new StringBuffer();
+        while (s.indexOf("${") != -1) {
+            ret.append(s.substring(0, s.indexOf("${")));
+            String s2 = s.substring(s.indexOf("${")+2);
+            Object app = exec("return (" + s2.substring(0, s2.indexOf('}')) + ");\n");
+            s = s.substring(s.indexOf('}') + 1);
+
+            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 Object exec(String s) {
+        try {
+            return JS.eval(JS.cloneWithNewParentScope(
+                           JS.fromReader("input", 0, new StringReader(s)), this));
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+    // Pass Through ///////////////////////////////////////////////////////////
+
+    public void setParent(XML.Element p)  { wrapped.setParent(p); }
+    public XML.Element getParent()        { return wrapped.getParent(); }
+    public XML.Attributes getAttributes() { return wrapped.getAttributes(); }
+    public XML.Prefixes getPrefixes()     { return wrapped.getPrefixes(); }
+    public List getChildren()             { return wrapped.getChildren(); }
+    public String getQName()              { return wrapped.getQName(); }
+    public String getLocalName()          { return wrapped.getLocalName(); }
+    public String getPrefix()             { return wrapped.getPrefix(); }
+    public String getUri()                { return wrapped.getUri(); }
+
+    /** Works up the Element object model until an instance of a JSScope is found. */
+    private static JSScope findScope(XML.Element e) {
+        while (e != null && !(e instanceof JSScope)) e = e.getParent();
+        return (JSScope)e;
+    }
+
+    /** A JSElement with the element attributes merged with a second
+     *  element.
+     *
+     *  All functions of the XML.Element interface are mapped onto the
+     *  primary element, except <tt>getAttributes()</tt>. This function
+     *  returns a MergedAttr instance with the <b>secondary</b> element
+     *  acting as the primary attribute source.
+     */
+    public static class Merge extends JSElement {
+        private final XML.Attributes a;
+        public Merge(XML.Element wrapped, XML.Element merge) {
+            super(wrapped);
+            a = new MergeAttr(merge.getAttributes(), wrapped.getAttributes());
+        }
+        public XML.Attributes getAttributes() { return a; }
+    }
+
+    /** Creates a single view onto two sets of Attributes, first
+     *  checking the <tt>primary</tt> array for an entry, or
+     *  otherwise returning any matching entry in the
+     *  <tt>secondary</tt> Attributes object.
+     *
+     *  FIXME: toXML() produces invalid XML if qname in both a and b.
+     */
+    public static final class MergeAttr implements XML.Attributes {
+        private final XML.Attributes a, b;
+        public MergeAttr(XML.Attributes primary, XML.Attributes secondary) {
+            a = primary; b = secondary;
+        }
+        public int getIndex(String qname) {
+            int i = a.getIndex(qname); if (i >= 0) return i;
+                i = b.getIndex(qname); if (i >= 0) return i + a.attrSize();
+            return -1;
+        }
+        public int getIndex(String uri, String key) {
+            int i = a.getIndex(uri, key); if (i >= 0) return i;
+                i = b.getIndex(uri, key); if (i >= 0) return i + b.attrSize();
+            return -1;
+        }
+        public String getKey(int i) {
+            return i >= a.attrSize() ? b.getKey(i-a.attrSize()) : a.getKey(i); }
+        public String getVal(int i) {
+            return i >= a.attrSize() ? b.getVal(i-a.attrSize()) : a.getVal(i); }
+        public String getUri(int i) {
+            return i >= a.attrSize() ? b.getUri(i-a.attrSize()) : a.getUri(i); }
+        public String getPrefix(int i) {
+            return i >= a.attrSize() ? b.getUri(i-a.attrSize()) : a.getUri(i); }
+        public String getQName(int i) {
+            return i >= a.attrSize() ? b.getUri(i-a.attrSize()) : a.getUri(i); }
+        public int attrSize() { return a.attrSize() + b.attrSize(); }
+    }
+}
similarity index 94%
rename from src/org/ibex/xt/Prevalence.java
rename to src/ibex/xt/Prevalence.java
index b237619..4904bbd 100644 (file)
@@ -1,13 +1,14 @@
-package org.ibex.xt;
+package ibex.xt;
+
 import org.ibex.js.*;
-import org.ibex.util.*;
-import org.ibex.io.*;
+import ibex.util.*;
+//import org.ibex.io.*;
 import java.io.*;
 import java.net.*;
 import java.util.*;
 import javax.servlet.*;
 import javax.servlet.http.*;
-import com.thoughtworks.xstream.*;
+//import com.thoughtworks.xstream.*;
 import org.prevayler.*;
 import org.prevayler.implementation.snapshot.*;
 
@@ -46,6 +47,7 @@ public class Prevalence {
                     String base = cx.getRealPath("/") + "WEB-INF" + File.separatorChar + "prevalent";
                     System.err.println("prevayling to " + base);
                     pf.configurePrevalenceBase(base);
+                    /*
                     XStreamSnapshotManager manager = new XStreamSnapshotManager(new JS(), base, null) {
                             protected XStream createXStream() {
                                 XStream xstream = new XStream();
@@ -56,7 +58,8 @@ public class Prevalence {
                         };
                     System.err.println("configuring with " + manager);
                     pf.configureSnapshotManager(manager);
-                    //pf.configureSnapshotManager(new SnapshotManager(new JS(), base));
+                    */
+                    pf.configureSnapshotManager(new SnapshotManager(new JS(), base));
                     //pf.configureClassLoader(JSTransaction.class.getClassLoader());
                     prevayler = pf.create();
                     prevaylers.put(cx, prevayler);
similarity index 64%
rename from src/org/ibex/xt/Servlet.java
rename to src/ibex/xt/Servlet.java
index 1d18563..94d3e29 100644 (file)
@@ -1,19 +1,22 @@
-package org.ibex.xt;
-import org.ibex.js.*;
-import org.ibex.util.*;
-import org.ibex.io.*;
+package ibex.xt;
+
+import ibex.util.XML;
+import org.ibex.js.JS;
+import org.ibex.js.JSArray;
+import org.ibex.js.JSDate;
+import org.ibex.js.JSExn;
+
 import java.io.*;
 import java.net.*;
 import java.util.*;
 import javax.servlet.*;
 import javax.servlet.http.*;
-import com.thoughtworks.xstream.*;
+
 import org.prevayler.*;
 import org.prevayler.implementation.snapshot.*;
 
 public class Servlet extends HttpServlet {
 
-    private ServletScope servletscope = null;
     private String path;
     private Prevayler prevayler;
     private JS prevalent;
@@ -35,28 +38,33 @@ public class Servlet extends HttpServlet {
     }
 
     public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { doGet(request, response); }
-    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
-        servletscope = new ServletScope(request, response, cx);
-        path = cx.getRealPath(((HttpServletRequest)request).getServletPath());
-        Reader xmlreader = new InputStreamReader(new FileInputStream(path));
-        new Template(servletscope, new JSScope(servletscope), xmlreader).wrap(null).toXML(response.getWriter());
+    public void doGet(HttpServletRequest rq, HttpServletResponse rs) throws IOException {
+        String path = cx.getRealPath(rq.getServletPath());
+        Servlet.Scope scope = new Servlet.Scope(cx, rq, rs, prevayler);
+        try { Template.wrap(Template.parse(path, scope), scope).toXML(rs.getWriter()); }
+        catch (Exception e) { e.printStackTrace(); System.out.println("e = "+e); }
     }
 
-    public class ServletScope extends JSScope {
-        HttpServletRequest request;
-        HttpServletResponse response;
-        ServletContext cx;
-        public String getRealPath(String s) { return cx.getRealPath(s); }
-        public ServletScope(ServletRequest request, ServletResponse response, ServletContext cx) {
-            super(null);
-            this.request = (HttpServletRequest)request;
-            this.response = (HttpServletResponse)response;
-            this.cx = cx;
+    public static class Scope extends Template.Scope {
+        private final ServletContext cx;
+        private final HttpServletRequest request;
+        private final HttpServletResponse response;
+        private final Prevayler prevayler;
+
+        public Scope(ServletContext cx, HttpServletRequest rq, HttpServletResponse rs, Prevayler p) {
+            super(null); this.cx = cx; request = rq; response = rs; prevayler = p;
+        }
+
+        public String getLocalPath() { return cx.getRealPath("/") + "/WEB-INF/"; }
+        public void transaction(JS t) {
+            try { prevayler.execute(new Prevalence.JSTransaction(t)); }
+            catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); }
         }
+
         private JS params = new JS() {
-                public Object get(Object key) { return request.getParameter(JS.toString(key)); }
-                public Enumeration keys() { return request.getParameterNames(); }
-            };
+            public Object get(Object key) { return request.getParameter(JS.toString(key)); }
+            public Enumeration keys() { return request.getParameterNames(); }
+        };
         private JS cookies = new JS() {
                 /*
                 public Object get(Object key) { return request.getCookie(JS.toString(key)); }
@@ -64,19 +72,21 @@ public class Servlet extends HttpServlet {
                 */
             };
         private JS sessionAttributes = new JS() {
-                public Object get(Object key) { return request.getSession(true).getAttribute(JS.toString(key)); }
-                public void put(Object key, Object val) {
-                    if (val == null) request.getSession(true).removeAttribute(JS.toString(key));
-                    else request.setAttribute(JS.toString(key), val); }
-                public Enumeration keys() { return request.getSession(true).getAttributeNames(); }
-            };
+            public Object get(Object key) {
+                return request.getSession(true).getAttribute(JS.toString(key)); }
+            public void put(Object key, Object val) {
+                if (val == null) request.getSession(true).removeAttribute(JS.toString(key));
+                else request.setAttribute(JS.toString(key), val); }
+            public Enumeration keys() { return request.getSession(true).getAttributeNames(); }
+        };
         private JS requestHeader = new JS() {
-                public Object get(Object key) { return request.getHeader(JS.toString(key)); }
-                public Enumeration keys() { return request.getHeaderNames(); }
-            };
+            public Object get(Object key) { return request.getHeader(JS.toString(key)); }
+            public Enumeration keys() { return request.getHeaderNames(); }
+        };
         private JS responseHeader = new JS() {
-                public void put(Object key, Object val) { response.setHeader(JS.toString(key), JS.toString(val)); }
-            };
+            public void put(Object key, Object val) {
+                response.setHeader(JS.toString(key), JS.toString(val)); }
+        };
 
 
         /** lets us put multi-level get/put/call keys all in the same method */
@@ -84,14 +94,14 @@ public class Servlet extends HttpServlet {
             Object key;
             Sub(Object key) { this.key = key; }
             public void put(Object key, Object val) throws JSExn {
-                ServletScope.this.put(JS.toString(this.key) + "." + JS.toString(key), val); }
+                Scope.this.put(JS.toString(this.key) + "." + JS.toString(key), val); }
             public Object get(Object key) throws JSExn {
-                return ServletScope.this.get(JS.toString(this.key) + "." + JS.toString(key)); }
+                return Scope.this.get(JS.toString(this.key) + "." + JS.toString(key)); }
             public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
-                return ServletScope.this.callMethod(this.key, a0, a1, a2, rest, nargs);
+                return Scope.this.callMethod(this.key, a0, a1, a2, rest, nargs);
             }
             public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
-                return ServletScope.this.callMethod(JS.toString(this.key) + "."
+                return Scope.this.callMethod(JS.toString(this.key) + "."
                                                          + JS.toString(method), a0, a1, a2, rest, nargs);
             }
         }
@@ -124,7 +134,7 @@ public class Servlet extends HttpServlet {
             case "request.remote":        return getSub("request.remote");
             case "request.remote.ip":     return request.getRemoteAddr();
             case "request.remote.host":   return request.getRemoteHost();
-            case "request.ssl":           return request.isSecure();
+            case "request.ssl":           return new Boolean(request.isSecure());
             case "request.path":          return request.getPathInfo();
             case "response":              return getSub("response");
             case "response.header":       return responseHeader;
@@ -134,20 +144,19 @@ public class Servlet extends HttpServlet {
             case "session.accessed":      return new JSDate(request.getSession(true).getLastAccessedTime());
             case "session.invalidate":    return METHOD;
             case "page":                  return getSub("page");
-            case "page.lastmodified":     return new JSDate(new File(path).lastModified());
+            //case "page.lastmodified":     return new JSDate(new File(path).lastModified()); FIXME
             case "context":               return getSub("context");
             case "context.list":          return METHOD;
             case "params":                return params;
             case "cookie":                return cookies;
+            case "xt.date":               return new JSDate(); // TODO: discuss
             //#end
             return null;
         }
         public void put(Object key, Object val) throws JSExn {
             try {
             //#switch(JS.toString(key))
-            case "transaction":
-                try { prevayler.execute(new Prevalence.JSTransaction((JS)val));
-                } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); }
+            case "transaction":           transaction((JS)val);
             case "response.code":         response.setStatus(JS.toInt(val));
             case "response.redirect":     response.sendRedirect(JS.toString(val));
             case "response.contentType":  response.setContentType(JS.toString(val));
diff --git a/src/ibex/xt/Template.java b/src/ibex/xt/Template.java
new file mode 100644 (file)
index 0000000..076251b
--- /dev/null
@@ -0,0 +1,213 @@
+package ibex.xt;
+
+import ibex.util.XML;
+import ibex.util.Vec; //FIXME remove
+
+import org.ibex.js.JS;
+import org.ibex.js.JSScope;
+import org.ibex.js.JSArray;
+import org.ibex.js.JSExn;
+
+import ibex.collection.*;
+import java.util.*;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.io.IOException;
+
+
+public class Template extends JSElement {
+    public static Template parse(String path, Template.Scope s) throws IOException, XML.Exn {
+        Reader xmlreader = new BufferedReader(new InputStreamReader(new FileInputStream(path)));
+        XML.Document doc = new XML.Document();
+        doc.parse(xmlreader);
+        return new Template(doc.getRoot(), s);
+    }
+
+    public static XML.Element wrap(XML.Element e, Template.Scope s) throws IOException, XML.Exn {
+        final String uri = e.getUri();
+
+        if (uri.equals("http://xt.ibex.org/")) {
+            //#switch(e.getLocalName())
+            case "if":          e = new Template.If(e); break;
+            case "js":          e = new Template.JSTag(e); break;
+            case "foreach":     e = new Template.ForEach(e); break;
+            case "children":    e = new Template.Children(e); break;
+            case "transaction": e = new Template.Transaction(e, s); break;
+            //#end
+
+        } else if (uri.startsWith("http://xt.ibex.org/")) {
+            //#switch(uri.substring(19))
+            case "io": System.out.println("ibex.xt.io not yet implemented"); // TODO
+            //#end
+            throw new RuntimeException("Unknown XT library: "+uri);
+
+        } else if (uri.startsWith("local:")) {
+            Template t = parse(s.getLocalPath() + uri.substring(6), s);
+
+            List c = e.getChildren();
+            if (c.size() > 0) {
+                // move all children from e to placeholder
+                XML.Element placeholder = findPlaceholder(t);
+                if (placeholder == null) throw new RuntimeException(
+                    "<"+e.getQName()+"> attempted to include children into a " +
+                    "template which does not contain an <xt:children /> tag.");
+
+                placeholder.getChildren().addAll(e.getChildren());
+                e.getChildren().clear();
+            }
+
+            // merge original attributes with replacement template
+            e = new JSElement.Merge(t, e);
+
+        } else if (uri.startsWith("java:")) {
+            e = new Java(e);
+        }
+
+        // wrap children
+        List c = e.getChildren();
+        for (int i=0; i < c.size(); i++)
+            if (c.get(i) instanceof XML.Element) wrap((XML.Element)c.get(i), s);
+
+        return e;
+    }
+
+    /** Returns the first Template.Children child found. */
+    private static Template.Children findPlaceholder(XML.Element e) {
+        if (e instanceof Template.Children) return (Template.Children)e;
+        List c = e.getChildren();
+        for (int i=0; i < c.size(); i++) {
+            if (!(c.get(i) instanceof XML.Element)) continue;
+            Template.Children ret = findPlaceholder((XML.Element)c.get(i));
+            if (ret != null) return ret;
+        }
+        return null;
+    }
+
+    private Template.Scope tscope;
+
+    public Template(XML.Element w, Template.Scope t) { super(w); tscope = t; }
+
+    public JSScope getParentScope() { return tscope; }
+
+
+    public static final class If extends JSElement {
+        public If(XML.Element e) { super(e); }
+
+        public void toXML(Writer w) throws IOException {
+            super.toXML(w);
+
+            try {
+                Object varIf = get("if"); if (varIf != null) undeclare("if");
+                if (varIf != null && !Boolean.getBoolean((String)varIf)) return;
+            } catch (JSExn e) { throw new RuntimeException(e); }
+
+            List c = getChildren();
+            for (int i=0; i < c.size(); i++) ((XML.Block)c.get(i)).toXML(w);
+        }
+    }
+
+    public static final class JSTag extends JSElement {
+        public JSTag(XML.Element e) {
+            super(e);
+            List c = getChildren();
+            for (int i=0; i < c.size(); i++)
+                if (c.get(i) instanceof XML.Element) throw new RuntimeException(
+                    "<"+getPrefix()+":js> tags may not have child elements");
+        }
+
+        public void toXML(Writer w) throws IOException {
+            super.toXML(w);
+
+            try {
+                Object varIf = get("if"); if (varIf != null) undeclare("if");
+                if (varIf != null && !Boolean.getBoolean((String)varIf)) return;
+
+                List c = getChildren();
+                StringWriter s = new StringWriter();
+                for (int i=0; i < c.size(); i++) ((XML.Block)c.get(i)).toXML(s);
+                exec(s.toString());
+            } catch (JSExn e) { throw new RuntimeException(e); }
+        }
+    }
+
+    public static final class ForEach extends JSElement {
+        public ForEach(XML.Element e) { super(e); }
+
+        public void toXML(Writer w) throws IOException {
+            super.toXML(w);
+
+            try {
+                Object varIn = get("in"); if (varIn != null) undeclare("in");
+                Object varPut = get("put"); if (varPut != null) undeclare("put");
+                Object varIf = get("if"); if (varIf != null) undeclare("if");
+                if (varIf != null && !Boolean.getBoolean((String)varIf)) return;
+
+                varIn = exec("return (" + varIn + ");");
+                if (varIn == null || (varIn instanceof JSArray)) throw new RuntimeException(
+                    "<"+getPrefix()+":foreach> requires attribute 'in' to specify " +
+                    "the name of a valid js array in the current scope, not in='"+varIn+"'.");
+
+                if (varPut == null) varPut = "x";
+                else if (!(varPut instanceof String) || get(varPut) != null)
+                    throw new RuntimeException(
+                    "<"+getPrefix()+":foreach> 'put' attribute requires the name of "+
+                    "an undeclared variable, not put='"+varPut+"'.");
+                if (get(varPut) != null) throw new RuntimeException(
+                    "<"+getPrefix()+":foreach> has no 'put' attribute defined and the "+
+                    "default variable 'x' already exists in the current scope.");
+
+                List c = getChildren();
+
+                declare((String)varPut);
+                Vec v = ((JSArray)varIn).toVec();
+                for (int j=0; j < v.size(); j++) {
+                    put(varPut, v.elementAt(j));
+                    for (int i=0; i < c.size(); i++) ((XML.Block)c.get(i)).toXML(w);
+                }
+            } catch (JSExn e) { throw new RuntimeException(e); }
+        }
+    }
+
+    public static final class Children extends JSElement {
+        public Children(XML.Element e) { super(e); }
+    }
+
+    public static final class Transaction extends JSElement {
+        private final Template.Scope scope; // FIXME: HACK. unstatisise all tags, or do this to all
+        public Transaction(XML.Element e, Template.Scope s) { super(e); scope = s;} // TODO: check kids
+
+        public void toXML(Writer w) throws IOException {
+            super.toXML(w);
+
+            // FIXME: what about scope import? <xt:use /> children?
+            List c = getChildren();
+            StringWriter sw = new StringWriter();
+            for (int i=0; i < c.size(); i++) ((XML.Block)c.get(i)).toXML(sw);
+            JS t = JS.fromReader("input", 0, new StringReader(sw.toString()));
+            t = JS.cloneWithNewParentScope(t, new JSScope(null));
+            scope.transaction(t);
+        }
+    }
+
+    public static final class Java extends JSElement {
+        // TODO what exactly?
+        public Java(XML.Element w) { super(w); }
+    }
+
+    public abstract static class Scope extends JSScope {
+        public Scope(JSScope j) { super(j); }
+
+        /** Returns the template path for local:/ namespace. */
+        public abstract String getLocalPath();
+
+        /** Registers a new Prevayler transaction. */
+        public abstract void transaction(JS t);
+    }
+
+}
diff --git a/src/org/ibex/xt/Node.java b/src/org/ibex/xt/Node.java
deleted file mode 100644 (file)
index f4fe0a4..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-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.*;
-
-public class Node {
-
-    public String   name = null;
-    public String   cdata = null;
-    public int      numattrs = 0;
-    public String[] attrs = null;
-    public String   uri = null;
-    private int     delta = 0;
-
-    public Node() { }
-    public Node(Node n) { copyFrom(n); }
-    public final void clear() { name = null; cdata = null; numattrs = 0; delta = 0; uri = null; }
-    public final void copyFrom(Node n) {
-        name=n.name; cdata=n.cdata; numattrs=n.numattrs; delta=n.delta; uri=n.uri;
-        if (n.attrs == null) { attrs = null; return; }
-        attrs = new String[n.attrs.length]; System.arraycopy(n.attrs, 0, attrs, 0, attrs.length); }
-    public final String attr(String key) {
-        for(int i=0; i<numattrs; i++) if (key.equals(attrs[i*2])) return attrs[i*2+1]; return null; }
-
-    public static abstract class Stream {
-        public static final Stream NULL = new Stream() { public boolean _read(Node n) { return false; } };
-        public static interface Functor { public Node.Stream wrap(Node.Stream in); }
-        public static class ConstantFunctor implements Functor {
-            private final Node.Stream stream;
-            public ConstantFunctor(Node.Stream stream) { this.stream = stream; }
-            public Node.Stream wrap(Node.Stream in) { return stream; }
-        }
-        public static abstract class Filter extends Stream {
-            Stream upstream;
-            Filter() { }
-            public Filter(Stream upstream) { this.upstream = upstream; }
-            public boolean upstreamRead(Node n) { upstream = upstream.simplify(); return upstream.read(n); }
-            public void wrapUpstream(Functor f) { upstream = f.wrap(upstream); }
-            public Filter graft(Functor f, Node n) {
-                upstream = new Graft((upstream instanceof Peekable) ? (Peekable)upstream : new Peekable(upstream), n, f);
-                return this;
-            }
-        }
-
-        public Stream simplify() { return this; }
-        protected abstract boolean _read(Node n);
-        public final boolean read(Node n) { n.clear(); if (!_read(n)) { n.clear(); return false; } return true; }
-
-        public static class Peekable extends Filter {
-            public Peekable(Stream s) { super(s); }
-            private Node pending = null;
-            public boolean peek(Node n) {
-                if (pending == null) {
-                    Node n2 = new Node();
-                    if (!upstreamRead(n2)) return false;
-                    pending = n2;
-                }
-                n.copyFrom(pending);
-                return true;
-            }
-            public boolean _read(Node n) {
-                if (pending != null) { n.copyFrom(pending); pending = null; return true; }
-                return upstreamRead(n);
-            }
-        }
-
-        private static class Graft extends Filter {
-            private Stream inner2;
-            private Peekable a;
-            int total = 0;
-            int net = 0;
-            boolean simple = false;
-            private Node pending = new Node();
-            public Graft(final Peekable a, final Node n, final Functor f) {
-                this.a = a;
-                upstream = inner2 = new Stream() {
-                        public boolean _read(Node n) {
-                            if (!Graft.this.a.peek(n)) return false;
-                            if (net + n.delta <= 0) return false;
-                            Graft.this.a.read(n);
-                            net += n.delta;
-                            return true;
-                        } };
-                wrapUpstream(f);
-                if (__read(pending)) pending.delta = n.delta;
-                total = n.delta;
-            }
-            public boolean _read(Node n) {
-                if (pending != null) { n.copyFrom(pending); pending = null; return true; }
-                boolean ret = __read(n);
-                if (ret) total += n.delta;
-                return ret;
-            }
-            public boolean __read(Node n) {
-                if (simple) return a.read(n);
-                if (upstreamRead(n)) return true;
-                while(inner2.read(n));
-                if (!a.read(n)) return false;
-                n.delta += net - total;
-                simple = true;
-                return true;
-            }
-        }
-
-        public static class FromXML extends Node.Stream {
-            private final XML.Pull xml;
-            private XML.Element parent = null;
-            private XML.Element e;
-            private int currentdelta = 0;
-            public FromXML(Reader r) { this.xml = new XML.Pull(r); }
-            protected boolean _read(Node n) { try {
-                Object ret = xml.read();
-                if (ret == null) return false;
-                if (ret instanceof String) {
-                    n.cdata = (String)ret;
-                    n.delta = xml.level - currentdelta;
-                    currentdelta = xml.level;
-                    return true;
-                }
-                XML.Element e = (XML.Element)ret;
-                n.name = e.getLocalName();
-                n.uri = e.getUri();
-                n.numattrs = e.getAttrLen();
-                n.delta = e.level - currentdelta;
-                currentdelta = e.level;
-                if (n.attrs == null || n.attrs.length < n.numattrs*2) n.attrs = new String[n.numattrs*4];
-                for(int i=0; i<n.numattrs; i++) { n.attrs[i*2]   = e.getAttrKey(i); n.attrs[i*2+1] = e.getAttrVal(i); }
-                return true;
-            } catch (Exception e) { throw new RuntimeException(e); } }
-        }
-
-        public void toXML(Writer writer) throws IOException { Node n = new Node(); if (read(n)) toXML(writer, n); }
-        private Node toXML(Writer w, Node n) throws IOException {
-            final String name = n.name;
-            if (n.cdata != null) {
-                w.write(n.cdata);
-                if (!read(n)) n = null;
-            } else {
-                w.write("<");
-                w.write(name);
-                for(int i=0; i < n.numattrs * 2; i+=2) {
-                    w.write(" ");
-                    w.write(n.attrs[i]);
-                    w.write("=\"");
-                    w.write(n.attrs[i+1]);
-                    w.write("\"");
-                }
-                if (!read(n)) n = null;
-                if (n == null || n.delta <= 0) {
-                    w.write("/>");
-                } else {
-                    w.write(">");
-                    while(n != null && n.delta > 0) n = toXML(w, n);
-                    w.write("</");
-                    w.write(name);
-                    w.write(">");
-                }
-            }
-            if (n != null) n.delta++;
-            return n;
-        }
-    }
-}
diff --git a/src/org/ibex/xt/Template.java b/src/org/ibex/xt/Template.java
deleted file mode 100644 (file)
index f3955f2..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-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;
-                    }
-                };
-        }
-    }
-}