Return-Path: Delivered-To: apmail-cocoon-cvs-archive@www.apache.org Received: (qmail 58110 invoked from network); 1 Aug 2005 15:52:21 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 1 Aug 2005 15:52:21 -0000 Received: (qmail 40075 invoked by uid 500); 1 Aug 2005 15:52:21 -0000 Delivered-To: apmail-cocoon-cvs-archive@cocoon.apache.org Received: (qmail 40026 invoked by uid 500); 1 Aug 2005 15:52:20 -0000 Mailing-List: contact cvs-help@cocoon.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@cocoon.apache.org list-help: list-unsubscribe: List-Post: List-Id: Delivered-To: mailing list cvs@cocoon.apache.org Received: (qmail 40013 invoked by uid 99); 1 Aug 2005 15:52:20 -0000 X-ASF-Spam-Status: No, hits=-9.8 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Mon, 01 Aug 2005 08:52:08 -0700 Received: (qmail 58087 invoked by uid 65534); 1 Aug 2005 15:52:06 -0000 Message-ID: <20050801155206.58086.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r226828 - in /cocoon: blocks/xsp/trunk/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionParser.java blocks/xsp/trunk/samples/java/interpolation.xsp trunk/status.xml Date: Mon, 01 Aug 2005 15:52:05 -0000 To: cvs@cocoon.apache.org From: anathaniel@apache.org X-Mailer: svnmailer-1.0.2 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Author: anathaniel Date: Mon Aug 1 08:51:59 2005 New Revision: 226828 URL: http://svn.apache.org/viewcvs?rev=226828&view=rev Log: XSP block: Extend {#expr} interpolation parser to understand {}, "}", '}' as not being the closing brace. The previously implemented mechanism #} to escape braces within the statement is no longer supported. Modified: cocoon/blocks/xsp/trunk/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionParser.java cocoon/blocks/xsp/trunk/samples/java/interpolation.xsp cocoon/trunk/status.xml Modified: cocoon/blocks/xsp/trunk/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionParser.java URL: http://svn.apache.org/viewcvs/cocoon/blocks/xsp/trunk/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionParser.java?rev=226828&r1=226827&r2=226828&view=diff ============================================================================== --- cocoon/blocks/xsp/trunk/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionParser.java (original) +++ cocoon/blocks/xsp/trunk/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionParser.java Mon Aug 1 08:51:59 2005 @@ -24,7 +24,12 @@ * expanded by the * {@link org.apache.cocoon.components.language.markup.xsp.XSPMarkupLanguage.PreProcessFilter PreProcessFilter} * and have the form {#expression}. To prevent interpolation, use {##quote}, which results in the - * text {#quote}. Inside expressions, to quote '}' write "#}" and to quote '#' write "##". + * text {#quote}. + * An exception is thrown if the closing brace is missing. + *

+ * The parser has a rudimentary understanding of expressions concerning + * nested braces and braces inside quoted strings and character constants. + * All valid Java, Javascript, and Python expressions can be used. *

* Example: <h1>Hello {#user.getName()}</h1> <img or * src="image_{#image.getId()}"/> @@ -63,14 +68,47 @@ * @throws SAXException It is illegal to finish processing in this state. */ public void done(XSPExpressionParser parser) throws SAXException { - throw new SAXException("Illegal XSP expression."); + throw new SAXException("Incomplete XSP expression {#"+parser.getExpression()); + } + } + + /** + * Parser state in a quoted string. + */ + protected static class QuotedState extends State { + private final char quote; + + /** + * Create state to process quotes strings. + * + * @param quote The quote character to delimit strings + */ + public QuotedState(char quote) { + this.quote = quote; + } + + /** + * Consume the next character + * + * @param parser The parser + * @param ch The character to consume + * @throws SAXException If there is an error in the expression + */ + public void consume(XSPExpressionParser parser, char ch) throws SAXException { + parser.append(ch); + if (ch == quote && !parser.isEscaped()) + parser.setState(EXPRESSION_STATE); + else if (ch == '\\') + parser.setEscaped(!parser.isEscaped()); + else + parser.setEscaped(false); } } /** * The parser is parsing text. */ - protected static State TEXT_STATE = new State() { + protected static final State TEXT_STATE = new State() { public void consume(XSPExpressionParser parser, char ch) throws SAXException { switch (ch) { case '{': @@ -95,7 +133,7 @@ /** * The parser has encountered '{' in {@link TEXT_STATE}. */ - protected static State LBRACE_STATE = new State() { + protected static final State LBRACE_STATE = new State() { public void consume(XSPExpressionParser parser, char ch) throws SAXException { switch (ch) { case '#': @@ -124,7 +162,7 @@ /** * The parser has encountered '#' in {@link LBRACE_STATE}. */ - protected static State TEXT_HASH_STATE = new State() { + protected static final State TEXT_HASH_STATE = new State() { public void consume(XSPExpressionParser parser, char ch) throws SAXException { switch (ch) { case '#': @@ -135,8 +173,9 @@ default: parser.handleText(); - parser.append(ch); + parser.initExpression(); parser.setState(EXPRESSION_STATE); + EXPRESSION_STATE.consume(parser, ch); } } }; @@ -144,16 +183,37 @@ /** * The parser is parsing an expression. */ - protected static State EXPRESSION_STATE = new State() { + protected static final State EXPRESSION_STATE = new State() { public void consume(XSPExpressionParser parser, char ch) throws SAXException { switch (ch) { + case '{': + parser.incrNesting(); + parser.append(ch); + break; + case '}': - parser.handleExpression(); - parser.setState(TEXT_STATE); + if (parser.decrNesting() > 0) { + parser.append(ch); + } + else { + parser.handleExpression(); + parser.setState(TEXT_STATE); + } break; - case '#': - parser.setState(EXPRESSION_HASH_STATE); + case '"': + parser.append(ch); + parser.setState(EXPRESSION_STRING_STATE); + break; + + case '\'': + parser.append(ch); + parser.setState(EXPRESSION_CHAR_STATE); + break; + + case '´': + parser.append(ch); + parser.setState(EXPRESSION_SHELL_STATE); break; default: @@ -163,26 +223,22 @@ }; /** - * The parser has encountered '#' in {@link EXPRESSION_STATE}. + * The parser has encountered '"' in {@link EXPRESSION_STATE} + * to start a string constant. */ - protected static State EXPRESSION_HASH_STATE = new State() { - public void consume(XSPExpressionParser parser, char ch) throws SAXException { - switch (ch) { - case '}': - parser.append('}'); - parser.setState(EXPRESSION_STATE); - break; + protected static final State EXPRESSION_STRING_STATE = new QuotedState('"'); - case '#': - parser.append('#'); - parser.setState(EXPRESSION_STATE); - break; + /** + * The parser has encountered '\'' in {@link EXPRESSION_STATE} + * to start a character constant. + */ + protected static final State EXPRESSION_CHAR_STATE = new QuotedState('\''); - default: - throw new SAXException("Illegal character '" + ch + "' after '#' in expression."); - } - } - }; + /** + * The parser has encountered '´' in {@link EXPRESSION_STATE} + * to start a Python string constant. + */ + protected static final State EXPRESSION_SHELL_STATE = new QuotedState('´'); /** * The parser state @@ -190,6 +246,16 @@ private State state = TEXT_STATE; /** + * The nesting level of braces. + */ + private int nesting = 0; + + /** + * Flag whether previous character was a backslash to escape quotes. + */ + private boolean escaped = false; + + /** * The handler for parsed text and expression fragments. */ private Handler handler; @@ -268,6 +334,31 @@ protected void setState(State state) { this.state = state; + } + + protected void initExpression() { + nesting = 1; + escaped = false; + } + + protected void incrNesting() { + nesting++; + } + + protected int decrNesting() { + return --nesting; + } + + protected void setEscaped(boolean escaped) { + this.escaped = escaped; + } + + protected boolean isEscaped() { + return escaped; + } + + protected String getExpression() { + return new String(buf, 0, bufSize); } protected void handleText() throws SAXException { Modified: cocoon/blocks/xsp/trunk/samples/java/interpolation.xsp URL: http://svn.apache.org/viewcvs/cocoon/blocks/xsp/trunk/samples/java/interpolation.xsp?rev=226828&r1=226827&r2=226828&view=diff ============================================================================== --- cocoon/blocks/xsp/trunk/samples/java/interpolation.xsp (original) +++ cocoon/blocks/xsp/trunk/samples/java/interpolation.xsp Mon Aug 1 08:51:59 2005 @@ -75,6 +75,52 @@ Interpolation can also be used in logicsheets:

+ +

+ The XSP expression parser understands nested braces, + single and double quoted strings, and backslash-escaped + quotes and apostrophes. +

+ + + + + + + + + + + + + + + + + + + + + +
ExpressionResultExpected
+ {#new String[]{}.length} + + {#new String[]{}.length} + + 0 +
+ {#new String[]{"}"}.length} + + {#new String[]{"}"}.length} + + 1 +
+ {#"'\"}".indexOf('}')} + + {#"'\"}".indexOf('}')} + + 2 +
Modified: cocoon/trunk/status.xml URL: http://svn.apache.org/viewcvs/cocoon/trunk/status.xml?rev=226828&r1=226827&r2=226828&view=diff ============================================================================== --- cocoon/trunk/status.xml (original) +++ cocoon/trunk/status.xml Mon Aug 1 08:51:59 2005 @@ -198,6 +198,12 @@ + XSP block: Extend {#expr} interpolation parser to understand {}, "}", '}' + as not being the closing brace. + The previously implemented mechanism #} to escape braces within the + statement is no longer supported. + + XSP block: Implement {#expr} interpolation also for logicsheets.