4 import java.io.Serializable;
5 import java.io.StringReader;
7 import java.io.OutputStream;
8 import java.io.IOException;
11 import org.ibex.util.*;
12 import org.ibex.js.JS;
13 import org.ibex.js.JSScope;
14 import org.ibex.js.JSExn;
16 public class JSLeaf implements Tree.Leaf, Serializable {
17 private transient JSScope scope = null;
18 protected Tree.Leaf wrapped;
20 /** Creates a Leaf around <tt>wrapped</tt>, replacing
21 * references to it in its parent with this object. */
22 public JSLeaf(Tree.Leaf wrapped) {
23 this.wrapped = wrapped;
25 // remap parent and children
26 if (wrapped.getParent() != null) {
27 List c = wrapped.getParent().getChildren();
28 c.set(c.indexOf(wrapped), this);
32 public JSScope scope() {
34 Tree.Leaf e = wrapped;
35 while (e != null && !(e instanceof JSLeaf)) e = e.getParent();
36 scope = new JSScope(e == null ? null : ((JSLeaf)e).scope());
41 public void out(Writer w) throws IOException { wrapped.out(w); }
42 public void out(OutputStream o) throws IOException { wrapped.out(o); }
44 protected Object eval(String s) {
45 if (s == null) return null;
47 StringBuffer ret = new StringBuffer();
49 while ((exp = s.indexOf("${", pos)) >= 0) {
50 ret.append(s.substring(pos, exp));
51 pos = s.indexOf("}", exp);
52 Object app = exec("return (" + s.substring(exp + 2, pos) + ");\n");
55 if (!(app == null || app instanceof String ||
56 app instanceof Number ||
57 app instanceof Boolean))
58 throw new Exn("javascripts within ${...} can only return " +
59 "strings, numbers, and booleans; not a " +
60 app.getClass().getName());
61 ret.append(app == null ? "null" : app.toString());
64 if (pos < s.length()) ret.append(s.substring(pos));
65 return ret.toString();
68 protected Object exec(String s) {
69 StringBuffer ret = new StringBuffer();
71 while ((exp = s.indexOf("${", pos)) >= 0) {
72 ret.append(s.substring(pos, exp));
73 pos = s.indexOf("}", exp);
74 ret.append("return (");
75 ret.append(s.substring(exp + 2, pos));
79 if (pos < s.length()) ret.append(s.substring(pos));
81 Reader r = new StringReader(ret.toString());
83 return JS.eval(JS.cloneWithNewParentScope(
84 JS.fromReader("input", 0, r), scope()));
85 } catch (IOException e) {
86 throw new Exn("error parsing script", e);
92 // Pass Through ///////////////////////////////////////////////////////////
94 public void setParent(Tree.Node p) { wrapped.setParent(p); }
95 public Tree.Node getParent() { return wrapped.getParent(); }
97 public static class Node extends JSLeaf implements Tree.Node {
98 public Node(Tree.Node wrapped) {
100 List c = wrapped.getChildren();
101 for (int i=0; i < c.size(); i++) ((Tree.Leaf)c.get(i)).setParent(this);
104 public List getChildren() { return ((Tree.Node)wrapped).getChildren(); }
107 public static class Element extends Node implements Tree.Element {
108 public Element(Tree.Element wrapped) { super(wrapped); }
110 public Tree.Attributes getAttributes() { return ((Tree.Element)wrapped).getAttributes(); }
111 public Tree.Prefixes getPrefixes() { return ((Tree.Element)wrapped).getPrefixes(); }
113 public void setAttributes(Tree.Attributes a) { ((Tree.Element)wrapped).setAttributes(a); }
114 public void setPrefixes(Tree.Prefixes p) { ((Tree.Element)wrapped).setPrefixes(p); }
116 public String getQName() { return ((Tree.Element)wrapped).getQName(); }
117 public String getLocalName() { return ((Tree.Element)wrapped).getLocalName(); }
118 public String getPrefix() { return ((Tree.Element)wrapped).getPrefix(); }
119 public String getUri() { return ((Tree.Element)wrapped).getUri(); }
122 /** Creates a single view onto two sets of Attributes, first
123 * checking the <tt>primary</tt> array for an entry, or
124 * otherwise returning any matching entry in the
125 * <tt>secondary</tt> Attributes object.
127 * FIXME: out(Writer) produces invalid XML if qname in both a and b.
128 * create org.ibex.util.XMLHelper and have a proper version of
129 * this as a subclass.
131 public static final class MergeAttr implements Tree.Attributes {
132 private final Tree.Attributes a, b;
133 public MergeAttr(Tree.Attributes primary, Tree.Attributes secondary) {
134 a = primary; b = secondary;
136 public int getIndex(String qname) {
137 int i = a.getIndex(qname); if (i >= 0) return i;
138 i = b.getIndex(qname); if (i >= 0) return i + a.attrSize();
141 public int getIndex(String uri, String key) {
142 int i = a.getIndex(uri, key); if (i >= 0) return i;
143 i = b.getIndex(uri, key); if (i >= 0) return i + b.attrSize();
146 public String getKey(int i) {
147 return i >= a.attrSize() ? b.getKey(i-a.attrSize()) : a.getKey(i); }
148 public String getVal(int i) {
149 return i >= a.attrSize() ? b.getVal(i-a.attrSize()) : a.getVal(i); }
150 public String getUri(int i) {
151 return i >= a.attrSize() ? b.getUri(i-a.attrSize()) : a.getUri(i); }
152 public String getPrefix(int i) {
153 return i >= a.attrSize() ? b.getPrefix(i-a.attrSize()) : a.getPrefix(i); }
154 public String getQName(int i) {
155 return i >= a.attrSize() ? b.getQName(i-a.attrSize()) : a.getQName(i); }
156 public int attrSize() { return a.attrSize() + b.attrSize(); }
159 public static class Exn extends RuntimeException {
160 public Exn(String cause) { super(cause); }
161 public Exn(JSExn e) { super(e); }
162 public Exn(String msg, Exception e) { super(msg + ": " + e.getMessage()); }
163 public String toString() { return "JSElement.Exn: "+getMessage(); }