coliver 2003/04/15 13:31:19
Modified: src/scratchpad/src/org/apache/cocoon/generation
JexlTemplate.java
Log:
added <define> tag (macro facility), and changed to enclose XPath EL in #{} rather
than {} (which is too big a pain to escape)
Revision Changes Path
1.2 +255 -101 cocoon-2.1/src/scratchpad/src/org/apache/cocoon/generation/JexlTemplate.java
Index: JexlTemplate.java
===================================================================
RCS file: /home/cvs/cocoon-2.1/src/scratchpad/src/org/apache/cocoon/generation/JexlTemplate.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- JexlTemplate.java 15 Apr 2003 06:52:28 -0000 1.1
+++ JexlTemplate.java 15 Apr 2003 20:31:19 -0000 1.2
@@ -78,9 +78,6 @@
import java.beans.PropertyDescriptor;
/**
*
- * <p>Cocoon {@link Generator} that produces dynamic XML SAX events
- * fom an XML template file.</p>
- *
* <p>Provides a tag library with embedded JSTL and XPath expression substitution
* to access data sent by Cocoon flowscripts.</p>
* The embedded expression language allows a page author to access an
@@ -88,13 +85,13 @@
* <p><pre>
* <site signOn="${accountForm.signOn}">
* </pre></p>
- * <p>Embedded Jexl expressions are contained in ${}.</p>
- * <p>Embedded XPath expressions are contained in {}.</p>
- * <p>Note that since this generator uses <a href="http://jakarta.apache.org/commons/jxpath">Apache
JXPath</a> and <a href="http://jakarta.apache.org/commons/jexl">Apache Jexl, the
referenced
+ * <p>Embedded Jexl expressions are contained in <code>${}</code>.</p>
+ * <p>Embedded XPath expressions are contained in <code>#{}</code>.</p>
+ * <p>Note that since this generator uses <a href="http://jakarta.apache.org/commons/jxpath">Apache
JXPath</a> and <a href="http://jakarta.apache.org/commons/jexl">Apache Jexl</a>,
the referenced
* objects may be Java Beans, DOM, JDOM, or JavaScript objects from a
* Flowscript. The current Web Continuation from the Flowscript
* is also available as an variable named <code>continuation</code>. You would
- * typically access its id:
+ * typically access its <code>id</code>:
* <p><pre>
* <form action="${continuation.id}">
* </pre></p>
@@ -125,7 +122,7 @@
* </otherwise>
* </choose>
* </pre></p>
- * <p>The <code>value-of</code> tag evaluates an XPath expression and
outputs
+ * <p>The <code>out</code> tag evaluates an expression and outputs
* the result of the evaluation:</p>
* <p><pre>
* <out value="Expression"/>
@@ -135,7 +132,10 @@
* <p><pre>
* <forEach var="name" items="Expression" begin="n" end="n" step="n">
* body
- * </for-each>
+ * </forEach>
+ * <forEach select="XPathExpression" begin="n" end="n" step="n">
+ * body
+ * </forEach>
* </pre></p>
*
*
@@ -421,9 +421,38 @@
}
}
- static class MyVariables implements Variables {
+ static class MyJexlContext
+ extends HashMap implements JexlContext {
+ public Map getVars() {
+ return this;
+ }
+ public void setVars(Map map) {
+ putAll(map);
+ }
+ public Object get(Object key) {
+ Object result = super.get(key);
+ if (result != null) {
+ return result;
+ }
+ MyJexlContext c = closure;
+ for (; c != null; c = c.closure) {
+ result = c.get(key);
+ if (result != null) {
+ return result;
+ }
+ }
+ return result;
+ }
+ MyJexlContext closure;
+ MyJexlContext() {
+ }
+ MyJexlContext(MyJexlContext closure) {
+ this.closure = closure;
+ }
+ }
- Map myVariables = new HashMap();
+ static class MyVariables implements Variables {
+ Map localVariables = new HashMap();
static final String[] VARIABLES = new String[] {
"continuation",
@@ -457,7 +486,7 @@
return true;
}
}
- return myVariables.containsKey(varName);
+ return localVariables.containsKey(varName);
}
public Object getVariable(String varName) {
@@ -476,15 +505,15 @@
} else if (varName.equals("parameters")) {
return parameters;
}
- return myVariables.get(varName);
+ return localVariables.get(varName);
}
public void declareVariable(String varName, Object value) {
- myVariables.put(varName, value);
+ localVariables.put(varName, value);
}
public void undeclareVariable(String varName) {
- myVariables.remove(varName);
+ localVariables.remove(varName);
}
}
@@ -512,11 +541,12 @@
final static String OTHERWISE = "otherwise";
final static String OUT = "out";
final static String IMPORT = "import";
+ final static String DEFINE = "define";
/**
* Compile a single Jexl expr (contained in ${}) or XPath expression
- * (contained in {})
+ * (contained in #{})
*/
private Object compileExpr(String inStr) throws Exception {
@@ -544,22 +574,14 @@
expr.append(c);
}
} else {
- if (c == '$') {
+ if (c == '$' || c == '#') {
ch = in.read();
if (ch == '{') {
inExpr = true;
- xpath = false;
- continue;
- }
- } else if (c == '{') {
- ch = in.read();
- if (ch != -1) {
- inExpr = true;
- xpath = true;
- expr.append((char)ch);
+ xpath = c == '#';
continue;
}
- }
+ }
// hack: invalid expression?
// just return the original and swallow exception
return inStr;
@@ -626,7 +648,7 @@
boolean inExpr = false;
boolean xpath = false;
try {
- while ((ch = in.read()) != -1) {
+ top: while ((ch = in.read()) != -1) {
char c = (char)ch;
if (inExpr) {
if (c == '}') {
@@ -666,44 +688,30 @@
} else {
buf.append((char)ch);
}
- } else {
- if (c == '$') {
+ } else if (c == '$' || c == '#') {
+ while (c == '$' || c == '#') {
ch = in.read();
if (ch == '{') {
- xpath = false;
+ xpath = c == '#';
inExpr = true;
if (buf.length() > 0) {
char[] charArray =
- new char[buf.length()];
+ new char[buf.length()];
buf.getChars(0, buf.length(),
charArray, 0);
substitutions.add(charArray);
buf.setLength(0);
}
- continue;
- }
- } else if (c == '{') {
- ch = in.read();
- if (ch != -1) {
- if (buf.length() > 0) {
- char[] charArray =
- new char[buf.length()];
-
- buf.getChars(0, buf.length(),
- charArray, 0);
- substitutions.add(charArray);
- buf.setLength(0);
- }
- buf.append((char)ch);
- inExpr = true;
- xpath = true;
- continue;
+ continue top;
+ } else if (ch == -1) {
+ break;
}
+ buf.append(c);
+ c = (char)ch;
}
- }
- if (ch != -1) {
- buf.append((char)ch);
+ } else {
+ buf.append(c);
}
}
}
@@ -852,6 +860,7 @@
this.namespaceURI = namespaceURI;
this.localName = localName;
this.raw = raw;
+ this.qname = "{"+namespaceURI+"}"+localName;
StringBuffer buf = new StringBuffer();
for (int i = 0, len = attrs.getLength(); i < len; i++) {
String uri = attrs.getURI(i);
@@ -866,7 +875,7 @@
List substEvents = new LinkedList();
boolean xpath = false;
try {
- while ((ch = in.read()) != -1) {
+ top: while ((ch = in.read()) != -1) {
char c = (char)ch;
if (inExpr) {
if (c == '}') {
@@ -903,33 +912,25 @@
buf.append((char)ch);
}
} else {
- if (c == '$') {
- ch = in.read();
- if (ch == '{') {
- if (buf.length() > 0) {
- substEvents.add(new Literal(buf.toString()));
- buf.setLength(0);
+ if (c == '$' || c == '#') {
+ while (c == '$' || c == '#') {
+ ch = in.read();
+ if (ch == '{') {
+ if (buf.length() > 0) {
+ substEvents.add(new Literal(buf.toString()));
+ buf.setLength(0);
+ }
+ inExpr = true;
+ xpath = c == '#';
+ continue top;
+ } else if (ch == -1) {
+ break;
}
- inExpr = true;
- xpath = false;
- continue;
+ buf.append(c);
+ c = (char)ch;
}
- } else if (c == '{') {
- ch = in.read();
- if (ch != -1) {
- if (buf.length() > 0) {
- substEvents.add(new Literal(buf.toString()));
- buf.setLength(0);
- }
- buf.append((char)ch);
- inExpr = true;
- xpath = true;
- continue;
- }
- buf.append('{');
- }
- if (ch != -1) {
- buf.append((char)ch);
+ } else {
+ buf.append(c);
}
}
}
@@ -970,7 +971,9 @@
final String namespaceURI;
final String localName;
final String raw;
+ final String qname;
final List attributeEvents = new LinkedList();
+ EndElement endElement;
}
class StartForEach extends Event {
@@ -1167,6 +1170,29 @@
}
}
+ class StartDefine extends Event {
+ StartDefine(Locator location, String namespace, String name,
+ Map formalParameters) {
+ super(location);
+ this.namespace = namespace;
+ this.name = name;
+ this.qname = "{"+namespace+"}"+name;
+ this.parameters = formalParameters;
+ }
+ final String namespace;
+ final String name;
+ final String qname;
+ final Map parameters;
+ EndDefine endDefine;
+ }
+
+ class EndDefine extends Event {
+ EndDefine(Locator location) {
+ super(location);
+ }
+ }
+
+
class Parser implements LexicalHandler, ContentHandler {
StartDocument startEvent;
@@ -1255,13 +1281,17 @@
StartTemplate startTemplate = (StartTemplate)start;
newEvent =
startTemplate.endTemplate = new EndTemplate(locator);
+ } else if (start instanceof StartDefine) {
+ StartDefine startDefine = (StartDefine)start;
+ newEvent =
+ startDefine.endDefine = new EndDefine(locator);
} else {
throw new SAXParseException("unrecognized tag: " + localName, locator,
null);
}
} else {
StartElement startElement = (StartElement)start;
- newEvent = new EndElement(locator,
- startElement);
+ newEvent = startElement.endElement =
+ new EndElement(locator, startElement);
}
addEvent(newEvent);
}
@@ -1341,7 +1371,8 @@
StartChoose startChoose = new StartChoose(locator);
newEvent = startChoose;
} else if (localName.equals(WHEN)) {
- if (!(stack.peek() instanceof StartChoose)) {
+ if (stack.size() == 0 ||
+ !(stack.peek() instanceof StartChoose)) {
throw new SAXParseException("<when> must be within <choose>",
locator, null);
}
String test = attrs.getValue("test");
@@ -1357,19 +1388,20 @@
StartWhen startWhen = new StartWhen(locator, expr);
newEvent = startWhen;
} else if (localName.equals(OUT)) {
- String select = attrs.getValue("value");
- if (select == null) {
+ String value = attrs.getValue("value");
+ if (value == null) {
throw new SAXParseException("out: \"value\" is required", locator,
null);
}
Object expr;
try {
- expr = compileExpr(select);
+ expr = compileExpr(value);
} catch (Exception e) {
throw new SAXParseException("out: \"value\": " + e.getMessage(),
locator, null);
}
newEvent = new StartOut(locator, expr);
} else if (localName.equals(OTHERWISE)) {
- if (!(stack.peek() instanceof StartChoose)) {
+ if (stack.size() == 0 ||
+ !(stack.peek() instanceof StartChoose)) {
throw new SAXParseException("<otherwise> must be within <choose>",
locator, null);
}
StartOtherwise startOtherwise =
@@ -1389,6 +1421,32 @@
StartIf startIf =
new StartIf(locator, expr);
newEvent = startIf;
+ } else if (localName.equals(DEFINE)) {
+ // <define namespace="a" name="b">
+ // body
+ // </define>
+ String namespace = attrs.getValue(JEXL_NS, "namespace");
+ if (namespace == null) {
+ namespace = "";
+ }
+ String name = attrs.getValue(JEXL_NS, "name");
+ if (name == null) {
+ throw new SAXParseException("define: template \"name\" is required",
locator, null);
+ }
+ Map formalParams = new HashMap();
+ for (int i = 0, len = attrs.getLength(); i < len; i++) {
+ String uri = attrs.getURI(i);
+ if (uri.equals("")) {
+ String n = attrs.getLocalName(i);
+ String v = attrs.getValue(i);
+ if (v == null) v = "";
+ formalParams.put(n, v);
+ }
+ }
+ StartDefine startDefine =
+ new StartDefine(locator, namespace, name,
+ formalParams);
+ newEvent = startDefine;
} else if (localName.equals(IMPORT)) {
// <import uri="${root}/foo/bar.xml" select="{.}"/>
// Allow expression substituion in "uri" attribute
@@ -1474,18 +1532,20 @@
private XMLConsumer consumer;
private JXPathContext jxpathContext;
- private JexlContext jexlContext;
+ private MyJexlContext globalJexlContext;
private Variables variables;
private static Map cache = new HashMap();
private Source inputSource;
+ private Map definitions;
public void recycle() {
super.recycle();
consumer = null;
jxpathContext = null;
- jexlContext = null;
+ globalJexlContext = null;
variables = null;
inputSource = null;
+ definitions = null;
}
public void setup(SourceResolver resolver, Map objectModel,
@@ -1518,6 +1578,7 @@
ObjectModelHelper.getResponse(objectModel),
ObjectModelHelper.getContext(objectModel),
parameters);
+ definitions = new HashMap();
}
private void setContexts(Object contextObject,
@@ -1574,9 +1635,10 @@
}
}
jxpathContext = jxpathContextFactory.newContext(null, contextObject);
- jexlContext = JexlHelper.createContext();
- jexlContext.setVars(map);
- map = jexlContext.getVars();
+ jxpathContext.setVariables(variables);
+ globalJexlContext = new MyJexlContext();
+ globalJexlContext.setVars(map);
+ map = globalJexlContext.getVars();
map.put("flowContext", contextObject);
map.put("continuation", kont);
map.put("request", request);
@@ -1605,7 +1667,7 @@
cache.put(inputSource.getURI(), startEvent);
}
}
- execute(jexlContext, jxpathContext, startEvent, null);
+ execute(globalJexlContext, jxpathContext, startEvent, null);
}
final static char[] EMPTY_CHARS = "".toCharArray();
@@ -1644,7 +1706,7 @@
}
}
- private void execute(JexlContext jexlContext,
+ private void execute(MyJexlContext jexlContext,
JXPathContext jxpathContext,
Event startEvent, Event endEvent)
throws SAXException {
@@ -1704,7 +1766,7 @@
StartIf startIf = (StartIf)ev;
Object val;
try {
- val = getValue(startIf.test,jexlContext, jxpathContext);
+ val = getValue(startIf.test, jexlContext, jxpathContext);
} catch (Exception e) {
throw new SAXParseException(e.getMessage(),
ev.location,
@@ -1757,6 +1819,8 @@
int begin = startForEach.begin;
int end = startForEach.end;
int step = startForEach.step;
+ MyJexlContext localJexlContext =
+ new MyJexlContext(jexlContext);
for (i = 0; i < begin && iter.hasNext(); i++) {
iter.next();
}
@@ -1776,15 +1840,14 @@
} else {
value = iter.next();
}
- JXPathContext newJXPathContext =
- jxpathContextFactory.newContext(null,
- jxpathContext);
- newJXPathContext.setVariables(variables);
+ JXPathContext localJXPathContext =
+ jxpathContextFactory.newContext(null, value);
+ localJXPathContext.setVariables(variables);
if (startForEach.var != null) {
- jexlContext.getVars().put(startForEach.var, value);
+ localJexlContext.put(startForEach.var, value);
}
- execute(jexlContext,
- newJXPathContext,
+ execute(localJexlContext,
+ localJXPathContext,
startForEach.next,
startForEach.endForEach);
for (int skip = step-1;
@@ -1828,6 +1891,93 @@
continue;
} else if (ev instanceof StartElement) {
StartElement startElement = (StartElement)ev;
+ StartDefine def =
+ (StartDefine)definitions.get(startElement.qname);
+ if (def != null) {
+ MyVariables vars =
+ (MyVariables)jxpathContext.getVariables();
+ MyJexlContext local = new MyJexlContext(globalJexlContext);
+ final Map localVariables = vars.localVariables;
+ vars.localVariables = new HashMap();
+ Iterator i = startElement.attributeEvents.iterator();
+ while (i.hasNext()) {
+ AttributeEvent attrEvent = (AttributeEvent)i.next();
+ Object defVal;
+ if (attrEvent.namespaceURI.length() == 0) {
+ defVal = def.parameters.get(attrEvent.localName);
+ if (defVal == null) continue;
+ }
+ if (attrEvent instanceof CopyAttribute) {
+ CopyAttribute copy =
+ (CopyAttribute)attrEvent;
+ vars.declareVariable(attrEvent.localName,
+ copy.value);
+ local.put(attrEvent.localName,
+ copy.value);
+ } else if (attrEvent instanceof
+ SubstituteAttribute) {
+ SubstituteAttribute substEvent =
+ (SubstituteAttribute)attrEvent;
+ if (substEvent.substitutions.size() == 1) {
+ Subst subst =
+ (Subst)substEvent.substitutions.get(0);
+ if (subst instanceof Expression) {
+ Object val;
+ Expression expr = (Expression)subst;
+ try {
+ val =
+ getValue(expr.compiledExpression,
+ jexlContext,
+ jxpathContext);
+ } catch (Exception e) {
+ throw new SAXParseException(e.getMessage(),
+ ev.location,
+ e);
+ }
+ vars.declareVariable(attrEvent.localName,
+ val);
+ local.put(attrEvent.localName, val);
+ continue;
+ }
+ }
+ StringBuffer buf = new StringBuffer();
+ Iterator ii = substEvent.substitutions.iterator();
+ while (ii.hasNext()) {
+ Subst subst = (Subst)ii.next();
+ if (subst instanceof Literal) {
+ Literal lit = (Literal)subst;
+ buf.append(lit.value);
+ } else if (subst instanceof Expression) {
+ Expression expr = (Expression)subst;
+ Object val;
+ try {
+ val =
+ getValue(expr.compiledExpression,
+ jexlContext,
+ jxpathContext);
+ } catch (Exception e) {
+ throw new SAXParseException(e.getMessage(),
+ ev.location,
+ e);
+ }
+ if (val == null) {
+ val = "";
+ }
+ buf.append(val.toString());
+ }
+ }
+ vars.declareVariable(attrEvent.localName,
+ buf.toString());
+ local.put(attrEvent.localName,
+ buf.toString());
+ }
+ }
+ execute(local, jxpathContext,
+ def.next, def.endDefine);
+ vars.localVariables = localVariables;
+ ev = startElement.endElement.next;
+ continue;
+ }
Iterator i = startElement.attributeEvents.iterator();
AttributesImpl attrs = new AttributesImpl();
while (i.hasNext()) {
@@ -1937,6 +2087,10 @@
consumer.characters(ch, 0, ch.length);
} else if (ev instanceof StartTemplate) {
// no action
+ } else if (ev instanceof StartDefine) {
+ StartDefine startDefine = (StartDefine)ev;
+ definitions.put(startDefine.qname, startDefine);
+ ev = startDefine.endDefine.next;
} else if (ev instanceof StartImport) {
String uri;
StartImport startImport = (StartImport)ev;
|