eval expressions in normal html attributes
[org.ibex.xt-crawshaw.git] / src / java / org / ibex / xt / JSLeaf.java
index 2def4c1..63bffbd 100644 (file)
@@ -1,5 +1,6 @@
 package org.ibex.xt;
 
+import java.io.Reader;
 import java.io.Serializable;
 import java.io.StringReader;
 import java.io.Writer;
@@ -34,42 +35,53 @@ public class JSLeaf implements Tree.Leaf, Serializable {
             while (e != null && !(e instanceof JSLeaf)) e = e.getParent();
             scope = new JSScope(e == null ? null : ((JSLeaf)e).scope());
         }
-
         return scope;
     }
 
-    public void out(OutputStream o) throws IOException { wrapped.out(o); }
     public void out(Writer w) throws IOException { wrapped.out(w); }
+    public void out(OutputStream o) throws IOException { wrapped.out(o); }
 
     protected 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))
+        int exp, pos = 0;
+        while ((exp = s.indexOf("${", pos)) >= 0) {
+            ret.append(s.substring(pos, exp));
+            pos = s.indexOf("}", exp);
+            Object app = exec("return (" + s.substring(exp + 2, pos) + ");");
+            pos++;
+
+            if (!(app == null || app instanceof String ||
+                                 app instanceof Number ||
+                                 app instanceof Boolean))
                 throw new Exn("javascripts within ${...} can only return " +
                               "strings, numbers, and booleans; not a " +
                               app.getClass().getName());
-
             ret.append(app == null ? "null" : app.toString());
         }
-        ret.append(s);
+
+        if (pos < s.length()) ret.append(s.substring(pos));
         return ret.toString();
     }
 
     protected Object exec(String s) {
+        StringBuffer ret = new StringBuffer();
+        int exp, pos = 0;
+        while ((exp = s.indexOf("${", pos)) >= 0) {
+            ret.append(s.substring(pos, exp));
+            pos = s.indexOf("}", exp);
+            ret.append("return (");
+            ret.append(s.substring(exp + 2, pos));
+            ret.append(");");
+            pos++;
+        }
+
+        Reader r = new StringReader(ret.toString());
         try {
             return JS.eval(JS.cloneWithNewParentScope(
-                           JS.fromReader("input", 0, new StringReader(s)), scope()));
+                           JS.fromReader("input", 0, r), scope()));
         } catch (IOException e) {
-            e.printStackTrace();
             throw new Exn("error parsing script", e);
         } catch (JSExn e) {
             throw new Exn(e);
@@ -97,29 +109,15 @@ public class JSLeaf implements Tree.Leaf, Serializable {
         public Tree.Attributes getAttributes() { return ((Tree.Element)wrapped).getAttributes(); }
         public Tree.Prefixes getPrefixes()     { return ((Tree.Element)wrapped).getPrefixes(); }
 
+        public void setAttributes(Tree.Attributes a) { ((Tree.Element)wrapped).setAttributes(a); }
+        public void setPrefixes(Tree.Prefixes p) { ((Tree.Element)wrapped).setPrefixes(p); }
+
         public String getQName()     { return ((Tree.Element)wrapped).getQName(); }
         public String getLocalName() { return ((Tree.Element)wrapped).getLocalName(); }
         public String getPrefix()    { return ((Tree.Element)wrapped).getPrefix(); }
         public String getUri()       { return ((Tree.Element)wrapped).getUri(); }
     }
 
-    /** A JSLeaf.Element with the element attributes merged with a second
-     *  element.
-     *
-     *  All functions of the Tree.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 Element {
-        private final Tree.Attributes a;
-        public Merge(Tree.Element wrapped, Tree.Element merge) {
-            super(wrapped);
-            a = new MergeAttr(merge.getAttributes(), wrapped.getAttributes());
-        }
-        public Tree.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
@@ -151,9 +149,9 @@ public class JSLeaf implements Tree.Leaf, Serializable {
         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); }
+            return i >= a.attrSize() ? b.getPrefix(i-a.attrSize()) : a.getPrefix(i); }
         public String getQName(int i) {
-            return i >= a.attrSize() ? b.getUri(i-a.attrSize()) : a.getUri(i); }
+            return i >= a.attrSize() ? b.getQName(i-a.attrSize()) : a.getQName(i); }
         public int attrSize() { return a.attrSize() + b.attrSize(); }
     }