From cocoon-cvs-return-11595-apmail-xml-cocoon-cvs-archive=xml.apache.org@xml.apache.org Mon Apr 14 04:41:41 2003
Return-Path: Cocoon {@link Generator} that produces dynamic XML SAX events
+ * fom an XML template file. Provides a tag library with embedded XPath expression substitution
+ * to access data sent by Cocoon flowscripts.
+ * <site signOn="{accountForm/signOn}">
+ *
Embedded XPath expressions are contained in curly braces.
+ *Note that since this generator uses Apache JXPath, 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 XPath variable named continuation
. You would
+ * typically access its id:
+ *
+ * <form action="{$continuation/id}"> + *+ *
You can also reach previous continuations by using the getContinuation()
function:
+ * <form action="{getContinuation($continuation, 1)}" > + *+ *
The if
tag allows the conditional execution of its body
+ * according to value of a test
attribute:
+ * <if test="XPathExpression"> + * body + * </if> + *+ *
The choose
tag performs conditional block execution by the
+ * embedded when
sub tags. It renders the body of the first
+ * when
tag whose test
condition evaluates to true.
+ * If none of the test
conditions of nested when
tags
+ * evaluate to true
, then the body of an otherwise
+ * tag is evaluated, if present:
+ * <choose> + * <when test="XPathExpression"> + * body + * </when> + * <otherwise> + * body + * </otherwise> + * </choose> + *+ *
The value-of
tag evaluates an XPath expression and outputs
+ * the result of the evaluation:
+ * <value-of select="XPathExpression"/> + *+ *
The for-each
tag allows you to iterate over a collection
+ * of objects:
+ *
+ * <for-each select="XPathExpression"> + * body + * </for-each> + *+ * + * + */ + public class JXPathTemplate extends AbstractGenerator { private static final JXPathContextFactory @@ -212,17 +276,78 @@ TextEvent(Locator location, char[] chars, int start, int length) { super(location); - this.chars = new char[this.length = length]; - System.arraycopy(chars, start, this.chars, - this.start = 0, length); - } - final char[] chars; - final int start; - final int length; - public String toString() { - return new String(chars, start, length) + - "("+super.locationString()+")"; + StringBuffer buf = new StringBuffer(); + CharArrayReader in = new CharArrayReader(chars, start, length); + int ch; + boolean inExpr = false; + try { + while ((ch = in.read()) != -1) { + char c = (char)ch; + if (inExpr) { + if (c == '}') { + String str = buf.toString(); + CompiledExpression compiledExpression = + JXPathContext.compile(str); + substitutions.add(compiledExpression); + buf.setLength(0); + inExpr = false; + } else if (c == '\\') { + ch = in.read(); + if (ch == -1) { + buf.append('\\'); + } else { + buf.append((char)ch); + } + } else { + buf.append(c); + } + } else { + if (c == '\\') { + ch = in.read(); + if (ch == -1) { + buf.append('\\'); + } else { + buf.append((char)ch); + } + } 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; + continue; + } + buf.append('{'); + } + if (ch != -1) { + buf.append((char)ch); + } + } + } + } + } catch (IOException ignored) { + ignored.printStackTrace(); + } + if (buf.length() > 0) { + char[] charArray = + new char[buf.length()]; + buf.getChars(0, buf.length(), + charArray, 0); + substitutions.add(charArray); + } else if (substitutions.size() == 0) { + substitutions.add(EMPTY_CHARS); + } } + final List substitutions = new LinkedList(); } class Characters extends TextEvent { @@ -248,10 +373,20 @@ } class EndElement extends Event { - StartElement startElement; - EndElement(Locator location, StartElement startElement) { + final StartElement startElement; + final String namespaceURI; + final String localName; + final String raw; + EndElement(Locator location, + String namespaceURI, + String localName, + String raw, + StartElement startElement) { super(location); this.startElement = startElement; + this.namespaceURI = namespaceURI; + this.localName = localName; + this.raw = raw; } public String toString() { String ns = ""; @@ -547,8 +682,8 @@ this.prefix = prefix; this.uri = uri; } - String prefix; - String uri; + final String prefix; + final String uri; } class Comment extends TextEvent { @@ -610,7 +745,7 @@ super(location); this.compiledExpression = expr; } - CompiledExpression compiledExpression; + final CompiledExpression compiledExpression; } class EndValueOf extends Event { @@ -694,12 +829,20 @@ startChoose.otherwise = startOtherwise; } else if (start instanceof StartValueOf) { newEvent = new EndValueOf(locator); + } else if (start instanceof StartChoose) { + StartChoose startChoose = (StartChoose)start; + newEvent = + startChoose.endChoose = new EndChoose(locator); } else { throw new SAXParseException("unrecognized tag: " + localName, locator, null); } } else { StartElement startElement = (StartElement)start; - newEvent = new EndElement(locator, startElement); + newEvent = new EndElement(locator, + namespaceURI, + localName, + raw, + startElement); } addEvent(newEvent); } @@ -711,15 +854,13 @@ } public void ignorableWhitespace(char[] ch, int start, int length) { - Event chars = new IgnorableWhitespace(locator, - ch, start, length); + Event chars = new IgnorableWhitespace(locator, ch, start, length); addEvent(chars); } public void processingInstruction(String target, String data) { - Event pi = new ProcessingInstruction(locator, target, - data); + Event pi = new ProcessingInstruction(locator, target, data); addEvent(pi); } @@ -748,7 +889,9 @@ if (localName.equals(FOR_EACH)) { String select = attrs.getValue("select"); if (select == null) { - throw new SAXParseException("for-each: \"select\" is required", locator, null); + throw + new SAXParseException("for-each: \"select\" is required", + locator, null); } CompiledExpression expr = JXPathContext.compile(getExpr(select)); @@ -926,6 +1069,41 @@ execute(rootContext, startEvent, null); } + final static char[] EMPTY_CHARS = "".toCharArray(); + + interface CharHandler { + public void characters(char[] ch, int offset, int length) + throws SAXException; + } + + private void characters(JXPathContext context, + TextEvent event, + CharHandler handler) throws SAXException { + Iterator iter = event.substitutions.iterator(); + while (iter.hasNext()) { + Object subst = iter.next(); + char[] chars; + if (subst instanceof char[]) { + chars = (char[])subst; + } else { + CompiledExpression expr = (CompiledExpression)subst; + try { + Object val = expr.getValue(context); + if (val != null) { + chars = val.toString().toCharArray(); + } else { + chars = EMPTY_CHARS; + } + } catch (Exception e) { + throw new SAXParseException(e.getMessage(), + event.location, + e); + } + } + handler.characters(chars, 0, chars.length); + } + } + private void execute(JXPathContext context, Event startEvent, Event endEvent) throws SAXException { @@ -934,24 +1112,36 @@ consumer.setDocumentLocator(ev.location); if (ev instanceof Characters) { TextEvent text = (TextEvent)ev; - consumer.characters(text.chars, text.start, - text.length); + characters(context, text, new CharHandler() { + public void characters(char[] ch, int offset, + int len) + throws SAXException { + + consumer.characters(ch, offset, len); + } + }); } else if (ev instanceof EndDocument) { consumer.endDocument(); } else if (ev instanceof EndElement) { EndElement endElement = (EndElement)ev; - StartElement startElement = (StartElement)endElement.startElement; - consumer.endElement(startElement.namespaceURI, - startElement.localName, - startElement.raw); + StartElement startElement = + (StartElement)endElement.startElement; + consumer.endElement(endElement.namespaceURI, + endElement.localName, + endElement.raw); } else if (ev instanceof EndPrefixMapping) { EndPrefixMapping endPrefixMapping = (EndPrefixMapping)ev; consumer.endPrefixMapping(endPrefixMapping.prefix); } else if (ev instanceof IgnorableWhitespace) { TextEvent text = (TextEvent)ev; - consumer.ignorableWhitespace(text.chars, text.start, - text.length); + characters(context, text, new CharHandler() { + public void characters(char[] ch, int offset, + int len) + throws SAXException { + consumer.ignorableWhitespace(ch, offset, len); + } + }); } else if (ev instanceof ProcessingInstruction) { ProcessingInstruction pi = (ProcessingInstruction)ev; consumer.processingInstruction(pi.target, pi.data); @@ -1089,9 +1279,14 @@ startPrefixMapping.uri); } else if (ev instanceof Comment) { TextEvent text = (TextEvent)ev; - consumer.comment(text.chars, text.start, - text.length); - } else if (ev instanceof EndCDATA) { + characters(context, text, new CharHandler() { + public void characters(char[] ch, int offset, + int len) + throws SAXException { + consumer.comment(ch, offset, len); + } + }); + } else if (ev instanceof EndCDATA) { consumer.endCDATA(); } else if (ev instanceof EndDTD) { consumer.endDTD(); @@ -1126,6 +1321,5 @@ ev = ev.next; } } - }