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: Delivered-To: apmail-xml-cocoon-cvs-archive@xml.apache.org Received: (qmail 69891 invoked by uid 500); 14 Apr 2003 04:41:40 -0000 Mailing-List: contact cocoon-cvs-help@xml.apache.org; run by ezmlm Precedence: bulk Reply-To: cocoon-dev@xml.apache.org list-help: list-unsubscribe: list-post: Delivered-To: mailing list cocoon-cvs@xml.apache.org Received: (qmail 69880 invoked by uid 500); 14 Apr 2003 04:41:40 -0000 Delivered-To: apmail-cocoon-2.1-cvs@apache.org Received: (qmail 69877 invoked from network); 14 Apr 2003 04:41:40 -0000 Received: from icarus.apache.org (208.185.179.13) by daedalus.apache.org with SMTP; 14 Apr 2003 04:41:40 -0000 Received: (qmail 67264 invoked by uid 1544); 14 Apr 2003 04:41:38 -0000 Date: 14 Apr 2003 04:41:38 -0000 Message-ID: <20030414044138.67262.qmail@icarus.apache.org> From: coliver@apache.org To: cocoon-2.1-cvs@apache.org Subject: cvs commit: cocoon-2.1/src/scratchpad/src/org/apache/cocoon/generation JXPathTemplate.java X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N coliver 2003/04/13 21:41:38 Modified: src/scratchpad/src/org/apache/cocoon/generation JXPathTemplate.java Log: added supprot for expression substitution in character data and comments and also added Javadoc Revision Changes Path 1.3 +227 -33 cocoon-2.1/src/scratchpad/src/org/apache/cocoon/generation/JXPathTemplate.java Index: JXPathTemplate.java =================================================================== RCS file: /home/cvs/cocoon-2.1/src/scratchpad/src/org/apache/cocoon/generation/JXPathTemplate.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- JXPathTemplate.java 13 Apr 2003 22:07:51 -0000 1.2 +++ JXPathTemplate.java 14 Apr 2003 04:41:38 -0000 1.3 @@ -70,6 +70,70 @@ import org.xml.sax.helpers.*; import org.apache.commons.jxpath.*; +/** + * + *

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.

+ * The embedded expression language allows a page author to access an + * object using a simplified syntax such as + *

  + *  <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; } } - }