You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by an...@apache.org on 2005/07/19 15:27:32 UTC

svn commit: r219681 - in /cocoon/branches/BRANCH_2_1_X: ./ src/blocks/xsp/conf/ src/blocks/xsp/java/org/apache/cocoon/components/language/markup/ src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/ src/blocks/xsp/samples/ src/blocks/x...

Author: anathaniel
Date: Tue Jul 19 06:27:29 2005
New Revision: 219681

URL: http://svn.apache.org/viewcvs?rev=219681&view=rev
Log:
XSP block: Added short-cut notation {#expr} for interpolation of
XSP expressions in attribute values and text nodes.
Removed wrapping of all text nodes from XSP source into <xsp:text>
elements before passing them to logicsheets.
(This feature was not used by the standard logicsheets but check your
custom logicsheets whether they depend on it.)

Contribution by Jochen Kuhnle (werbung@kuhnle.net) Bugzilla #35228.

Added:
    cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionFilter.java   (with props)
    cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionParser.java   (with props)
    cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/samples/java/interpolation.xsp   (with props)
Modified:
    cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/conf/xsp-markup.xconf
    cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/AbstractMarkupLanguage.java
    cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/CocoonMarkupLanguage.java
    cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPMarkupLanguage.java
    cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/samples/samples.xml
    cocoon/branches/BRANCH_2_1_X/status.xml

Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/conf/xsp-markup.xconf
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/conf/xsp-markup.xconf?rev=219681&r1=219680&r2=219681&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/conf/xsp-markup.xconf (original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/conf/xsp-markup.xconf Tue Jul 19 06:27:29 2005
@@ -31,6 +31,15 @@
     <xsp-language logger="core.markup.xsp" name="xsp">
       <parameter name="prefix" value="xsp"/>
       <parameter name="uri" value="http://apache.org/xsp"/>
+      <!--+
+          | These two flags determine whether XSP expression interpolation
+          | shall take place in attribute values and text nodes, respectively.
+          | The syntax {#expr} has been chosen that a clash with existing
+          | XSPs and logicsheets is unlikely.  The possibility to switch these
+          | features off is here only for safety.
+          +-->
+      <parameter name="attr-interpolation" value="true"/>
+      <parameter name="text-interpolation" value="true"/>
 
       <!--+
           | Properties for the java language

Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/AbstractMarkupLanguage.java
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/AbstractMarkupLanguage.java?rev=219681&r1=219680&r2=219681&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/AbstractMarkupLanguage.java (original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/AbstractMarkupLanguage.java Tue Jul 19 06:27:29 2005
@@ -65,6 +65,18 @@
         implements MarkupLanguage, Serviceable, Configurable, Parameterizable,
                    Recyclable, Disposable
 {
+    /**
+     * Name "attr-interpolation" of boolean attribute to enable
+     * expression interpolation in attribute values.
+     */
+    public static final String ATTR_INTERPOLATION = "attr-interpolation";
+
+    /**
+     * Name "text-interpolation" of boolean attribute to enable
+     * expression interpolation inside text nodes.
+     */
+    public static final String TEXT_INTERPOLATION = "text-interpolation";
+
     /** The 'file' URL protocol. */
     private static final String FILE = "file:";
 
@@ -81,10 +93,16 @@
     protected Store logicsheetCache;
 
     /** The markup language's namespace uri */
-    protected String uri;
+    private String uri;
 
     /** The markup language's namespace prefix */
-    protected String prefix;
+    private String prefix;
+
+    /** Are attribute expressions to be expanded? */
+    private boolean attrInterpolation;
+
+    /** Are text expressions to be expanded? */
+    private boolean textInterpolation;
 
     /** The service manager */
     protected ServiceManager manager;
@@ -222,6 +240,10 @@
     public void parameterize(Parameters params) throws ParameterException {
         this.uri = params.getParameter("uri");
         this.prefix = params.getParameter("prefix", null);
+        this.attrInterpolation =
+            params.getParameterAsBoolean(ATTR_INTERPOLATION, false);
+        this.textInterpolation =
+            params.getParameterAsBoolean(TEXT_INTERPOLATION, false);
     }
 
     /**
@@ -232,6 +254,36 @@
      */
     public String getName() {
         return this.name;
+    }
+
+    /**
+     * Returns the namespace URI for this language.
+     */
+    public String getURI() {
+        return this.uri;
+    }
+
+    /**
+     * Returns the namespace prefix for this language.
+     */
+    public String getPrefix() {
+        return this.prefix;
+    }
+
+    /**
+     * Returns true if expansion of attribute expressions is enabled
+     * for this language.
+     */
+    public boolean hasAttrInterpolation() {
+        return this.attrInterpolation;
+    }
+
+    /**
+     * Returns true if expansion of expressions inside text nodes is enabled
+     * for this language.
+     */
+    public boolean hasTextInterpolation() {
+        return this.textInterpolation;
     }
 
     /**

Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/CocoonMarkupLanguage.java
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/CocoonMarkupLanguage.java?rev=219681&r1=219680&r2=219681&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/CocoonMarkupLanguage.java (original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/CocoonMarkupLanguage.java Tue Jul 19 06:27:29 2005
@@ -139,13 +139,6 @@
     }
 
     /**
-     * Returns the namespace URI for this language.
-     */
-    public String getURI() {
-        return super.uri;
-    }
-
-    /**
      * Returns the root element for this language.
      */
     public abstract String getRootElement();

Added: cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionFilter.java
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionFilter.java?rev=219681&view=auto
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionFilter.java (added)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionFilter.java Tue Jul 19 06:27:29 2005
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.language.markup.xsp;
+
+import org.apache.cocoon.components.language.markup.AbstractMarkupLanguage;
+import org.apache.cocoon.xml.AbstractXMLPipe;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.XMLConsumer;
+import org.apache.cocoon.xml.XMLUtils;
+import org.apache.commons.lang.StringUtils;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+public class XSPExpressionFilter implements ContentHandler, XSPExpressionParser.Handler {
+
+    public static class XMLPipeAdapter extends AbstractXMLPipe {
+        private XSPExpressionFilter expressionFilter;
+
+        private AbstractXMLPipe additionalFilter;
+
+        public XMLPipeAdapter(XSPExpressionFilter expressionFilter, AbstractXMLPipe additionalFilter) {
+            this.additionalFilter = additionalFilter;
+            this.expressionFilter = expressionFilter;
+            super.setLexicalHandler(additionalFilter);
+            super.setContentHandler(expressionFilter);
+            expressionFilter.setContentHandler(additionalFilter);
+        }
+
+        public void setConsumer(XMLConsumer consumer) {
+            additionalFilter.setConsumer(consumer);
+        }
+
+        public void setContentHandler(ContentHandler handler) {
+            additionalFilter.setContentHandler(handler);
+        }
+
+        public void setLexicalHandler(LexicalHandler handler) {
+            additionalFilter.setLexicalHandler(handler);
+        }
+    }
+
+    public static class XMLFilterAdapter extends XMLFilterImpl {
+        private XSPExpressionFilter expressionFilter;
+
+        public XMLFilterAdapter(XSPExpressionFilter filter) {
+            this.expressionFilter = filter;
+            super.setContentHandler(filter);
+        }
+
+        public void setParent(XMLReader reader) {
+            super.setParent(reader);
+            reader.setContentHandler(this);
+        }
+
+        public void setContentHandler(ContentHandler contentHandler) {
+            expressionFilter.setContentHandler(contentHandler);
+        }
+    }
+
+    /** The markup language URI */
+    private String markupURI;
+
+    /** The markup language prefix */
+    private String markupPrefix;
+
+    /** Set default processing of attribute templates */
+    private boolean defaultProcessAttribs;
+
+    /** Set processing of attribute templates */
+    private boolean processAttribs;
+
+    /** Set default processing of text templates */
+    private boolean defaultProcessText;
+
+    /** Set processing of text templates */
+    private boolean processText;
+
+    /** The parser for XSP value templates */
+    private XSPExpressionParser expressionParser = new XSPExpressionParser(this);
+
+    /** First element was processed */
+    private boolean firstElementProcessed;
+
+    private ContentHandler contentHandler;
+
+    public XSPExpressionFilter(XSPMarkupLanguage markup) {
+        this.markupURI = markup.getURI();
+        this.markupPrefix = markup.getPrefix();
+        this.defaultProcessAttribs = markup.hasAttrInterpolation();
+        this.defaultProcessText = markup.hasTextInterpolation();
+    }
+
+    public void setContentHandler(ContentHandler contentHandler) {
+        this.contentHandler = contentHandler;
+    }
+
+    /**
+     * Create a new <code>{@link XSPExpressionFilter}</code>.
+     * 
+     * @param filter
+     * @param filename
+     * @param language
+     */
+    public void startDocument() throws SAXException {
+        processAttribs = defaultProcessAttribs;
+        processText = defaultProcessText;
+
+        contentHandler.startDocument();
+    }
+
+    /**
+     * Start a new element. If attribute value templates are enabled and the element has attributes
+     * with templates, these are replaced by xsp:attribute tags.
+     * 
+     * @see org.xml.sax.contentHandler.#startElement(java.lang.String, java.lang.String,
+     *      java.lang.String, org.xml.sax.Attributes)
+     */
+    public void startElement(String namespaceURI, String localName, String qName, Attributes attribs)
+            throws SAXException {
+        expressionParser.flush();
+
+        // Check template for processing flags in page
+        if (!firstElementProcessed) {
+            initFromAttribs(attribs);
+            firstElementProcessed = true;
+        }
+
+        if (processAttribs) {
+            // Attribute value templates enabled => process attributes
+            AttributesImpl staticAttribs = new AttributesImpl();
+            AttributesImpl dynamicAttribs = new AttributesImpl();
+
+            // Gather attributes with and without templates separately
+            for (int i = 0; i < attribs.getLength(); ++i) {
+                String value = attribs.getValue(i);
+
+                if (value.indexOf("{#") != -1) {
+                    // The attribute contains templates
+                    dynamicAttribs.addAttribute(attribs.getURI(i), attribs.getLocalName(i), attribs.getQName(i),
+                            attribs.getType(i), value);
+                }
+                else {
+                    // The attribute does not contain templates
+                    staticAttribs.addAttribute(attribs.getURI(i), attribs.getLocalName(i), attribs.getQName(i),
+                            attribs.getType(i), value);
+                }
+            }
+
+            // Start the element with template-free attributes
+            contentHandler.startElement(namespaceURI, localName, qName, staticAttribs);
+
+            // Generate xsp:attribute elements for the attributes containing templates
+            for (int i = 0; i < dynamicAttribs.getLength(); ++i) {
+                AttributesImpl elemAttribs = new AttributesImpl();
+                addAttribute(elemAttribs, "uri", dynamicAttribs.getURI(i));
+
+                String qname = dynamicAttribs.getQName(i);
+
+                if (qname != null) {
+                    addAttribute(elemAttribs, "prefix", StringUtils.left(qname, qname.indexOf(':')));
+                }
+
+                addAttribute(elemAttribs, "name", dynamicAttribs.getLocalName(i));
+
+                contentHandler.startElement(markupURI, "attribute", markupPrefix + ":attribute", elemAttribs);
+
+                expressionParser.consume(dynamicAttribs.getValue(i));
+                expressionParser.flush();
+
+                contentHandler.endElement(markupURI, "attribute", markupPrefix + ":attribute");
+            }
+        }
+        else {
+            // Attribute value templates disabled => pass through element
+            contentHandler.startElement(namespaceURI, localName, qName, attribs);
+        }
+    }
+
+    protected void initFromAttribs(Attributes attribs) {
+        String value = attribs.getValue(markupURI, XSPMarkupLanguage.ATTR_INTERPOLATION);
+
+        if (value != null) {
+            processAttribs = Boolean.valueOf(value).booleanValue();
+        }
+
+        value = attribs.getValue(markupURI, XSPMarkupLanguage.TEXT_INTERPOLATION);
+
+        if (value != null) {
+            processText = Boolean.valueOf(value).booleanValue();
+        }
+    }
+
+    /**
+     * Flush the current expression.
+     */
+    public void endElement(String uri, String loc, String raw) throws SAXException {
+        expressionParser.flush();
+        contentHandler.endElement(uri, loc, raw);
+    }
+
+    /**
+     * Handle characters. If text templates are enabled, the text is parsed and expressions are
+     * replaced.
+     * 
+     * @see org.xml.sax.contentHandler.#characters(char[], int, int)
+     */
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        if (processText) {
+            // Text templated enabled => Replace text expressions
+            expressionParser.consume(ch, start, length);
+        }
+        else {
+            // Text templates disabled => pass through text
+            contentHandler.characters(ch, start, length);
+        }
+    }
+
+    /**
+     * Forward text to parent class.
+     * 
+     * @see org.apache.cocoon.components.language.markup.xsp.XSPExpressionParser.Handler#handleText(char[],
+     *      int, int)
+     */
+    public void handleText(char[] chars, int start, int length) throws SAXException {
+        contentHandler.characters(chars, start, length);
+    }
+
+    /**
+     * Wrap expressions in xsp:expr tags.
+     * 
+     * @see org.apache.cocoon.components.language.markup.xsp.XSPExpressionParser.Handler#handleExpression(char[],
+     *      int, int)
+     */
+    public void handleExpression(char[] chars, int start, int length) throws SAXException {
+        contentHandler.startElement(markupURI, "expr", markupPrefix + ":expr", XMLUtils.EMPTY_ATTRIBUTES);
+        contentHandler.characters(chars, start, length);
+        contentHandler.endElement(markupURI, "expr", markupPrefix + ":expr");
+    }
+
+    /**
+     * Add an attribute if it is neither <code>null</code> nor empty (length 0).
+     * 
+     * @param attribs The attributes
+     * @param name The attribute name
+     * @param value The attribute value
+     */
+    protected void addAttribute(AttributesImpl attribs, String name, String value) {
+        if (value != null && value.length() > 0) {
+            attribs.addCDATAAttribute(name, value);
+        }
+    }
+
+    public void setDocumentLocator(Locator locator) {
+        contentHandler.setDocumentLocator(locator);
+    }
+
+    public void endDocument() throws SAXException {
+        contentHandler.endDocument();
+    }
+
+    public void endPrefixMapping(String prefix) throws SAXException {
+        contentHandler.endPrefixMapping(prefix);
+    }
+
+    public void ignorableWhitespace(char[] chars, int start, int length) throws SAXException {
+        contentHandler.ignorableWhitespace(chars, start, length);
+    }
+
+    public void processingInstruction(String target, String data) throws SAXException {
+        contentHandler.processingInstruction(target, data);
+    }
+
+    public void skippedEntity(String name) throws SAXException {
+        contentHandler.skippedEntity(name);
+    }
+
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {
+        contentHandler.startPrefixMapping(prefix, uri);
+    }
+}

Propchange: cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionParser.java
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionParser.java?rev=219681&view=auto
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionParser.java (added)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionParser.java Tue Jul 19 06:27:29 2005
@@ -0,0 +1,300 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.language.markup.xsp;
+
+import org.apache.cocoon.xml.XMLUtils;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Parse XSP expressions. Expressions are embedded in attribute="value" and text elements and are
+ * 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 "##".
+ * <p>
+ * Example: &lt;h1&gt;Hello {#user.getName()}&lt;/h1&gt; &lt;img or
+ * src=&quot;image_{#image.getId()}&quot;/&gt;
+ * <p>
+ */
+public class XSPExpressionParser {
+
+    /**
+     * Handler interface for parsed expressions and text fragments. The parser calls the handler to
+     * process these.
+     */
+    public static interface Handler {
+        public void handleText(char[] chars, int start, int length) throws SAXException;
+
+        public void handleExpression(char[] chars, int start, int length) throws SAXException;
+    }
+
+    /**
+     * Parser state.
+     */
+    protected static abstract class State {
+        /**
+         * 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 abstract void consume(XSPExpressionParser parser, char ch) throws SAXException;
+
+        /**
+         * Finish processing. Default behaviour is to throw an expression. States that are legal end
+         * states must overwrite this method.
+         * 
+         * @param parser The parser
+         * @throws SAXException It is illegal to finish processing in this state.
+         */
+        public void done(XSPExpressionParser parser) throws SAXException {
+            throw new SAXException("Illegal XSP expression.");
+        }
+    }
+
+    /**
+     * The parser is parsing text.
+     */
+    protected static State TEXT_STATE = new State() {
+        public void consume(XSPExpressionParser parser, char ch) throws SAXException {
+            switch (ch) {
+                case '{':
+                    parser.setState(LBRACE_STATE);
+                    break;
+
+                default:
+                    parser.append(ch);
+            }
+        }
+
+        /**
+         * Handle remaining text. It is legal to end in text mode.
+         * 
+         * @see State#done(XSPExpressionParser)
+         */
+        public void done(XSPExpressionParser parser) throws SAXException {
+            parser.handleText();
+        }
+    };
+
+    /**
+     * The parser has encountered '{' in <code>{@link TEXT_STATE}</code>.
+     */
+    protected static State LBRACE_STATE = new State() {
+        public void consume(XSPExpressionParser parser, char ch) throws SAXException {
+            switch (ch) {
+                case '#':
+                    parser.setState(TEXT_HASH_STATE);
+                    break;
+
+                default:
+                    parser.append('{');
+                    parser.append(ch);
+                    parser.setState(TEXT_STATE);
+            }
+        }
+
+        /**
+         * Handle remaining text. It is legal to end text with '{'.
+         * 
+         * @see State#done(XSPExpressionParser)
+         */
+        public void done(XSPExpressionParser parser) throws SAXException {
+            // Append the pending '{'
+            parser.append('{');
+            parser.handleText();
+        }
+    };
+
+    /**
+     * The parser has encountered '#' in <code>{@link LBRACE_STATE}</code>.
+     */
+    protected static State TEXT_HASH_STATE = new State() {
+        public void consume(XSPExpressionParser parser, char ch) throws SAXException {
+            switch (ch) {
+                case '#':
+                    parser.append('{');
+                    parser.append('#');
+                    parser.setState(TEXT_STATE);
+                    break;
+
+                default:
+                    parser.handleText();
+                    parser.append(ch);
+                    parser.setState(EXPRESSION_STATE);
+            }
+        }
+    };
+
+    /**
+     * The parser is parsing an expression.
+     */
+    protected static State EXPRESSION_STATE = new State() {
+        public void consume(XSPExpressionParser parser, char ch) throws SAXException {
+            switch (ch) {
+                case '}':
+                    parser.handleExpression();
+                    parser.setState(TEXT_STATE);
+                    break;
+
+                case '#':
+                    parser.setState(EXPRESSION_HASH_STATE);
+                    break;
+
+                default:
+                    parser.append(ch);
+            }
+        }
+    };
+
+    /**
+     * The parser has encountered '#' in <code>{@link EXPRESSION_STATE}</code>.
+     */
+    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;
+
+                case '#':
+                    parser.append('#');
+                    parser.setState(EXPRESSION_STATE);
+                    break;
+
+                default:
+                    throw new SAXException("Illegal character '" + ch + "' after '#' in expression.");
+            }
+        }
+    };
+
+    /**
+     * The parser state
+     */
+    private State state = TEXT_STATE;
+
+    /**
+     * The handler for parsed text and expression fragments.
+     */
+    private Handler handler;
+
+    /**
+     * The buffer for the current text or expression fragment. We do our own StringBuffer here to
+     * save some allocations of char arrays for the handler.
+     */
+    private char[] buf = new char[256];
+
+    /**
+     * The current size of the fragment in the buffer.
+     */
+    private int bufSize;
+
+    /**
+     * The delty by which the buffer grows if it is too small.
+     */
+    private int bufGrow = 256;
+
+    /**
+     * Create a new <code>{@link XSPExpressionParser}</code>.
+     * 
+     * @param handler The handler for parsed text and expression fragments.
+     */
+    public XSPExpressionParser(Handler handler) {
+        this.handler = handler;
+    }
+
+    /**
+     * Parses a character sequence.
+     * 
+     * @param chars The character sequence to parse
+     * @throws SAXException If there is an error in the sequence.
+     */
+    public void consume(CharSequence chars) throws SAXException {
+        int end = chars.length();
+
+        for (int i = 0; i < end; ++i) {
+            char ch = chars.charAt(i);
+            state.consume(this, ch);
+        }
+    }
+
+    /**
+     * Parses part of a character array.
+     * 
+     * @param chars The characters
+     * @param start The start position in the character array
+     * @param length The number of characters to parse
+     * @throws SAXException If there is an error in the sequence.
+     */
+    public void consume(char[] chars, int start, int length) throws SAXException {
+        int end = start + length;
+
+        for (int i = start; i < end; ++i) {
+            char ch = chars[i];
+            state.consume(this, ch);
+        }
+    }
+
+    /**
+     * Flushes the parser
+     * 
+     * @throws SAXException If there is an error in the parsed text.
+     */
+    public void flush() throws SAXException {
+        state.done(this);
+        bufSize = 0;
+        state = TEXT_STATE;
+    }
+
+    protected State getState() {
+        return state;
+    }
+
+    protected void setState(State state) {
+        this.state = state;
+    }
+
+    protected void handleText() throws SAXException {
+        if (bufSize > 0) {
+            handler.handleText(buf, 0, bufSize);
+            bufSize = 0;
+        }
+    }
+
+    protected void handleExpression() throws SAXException {
+        if (bufSize == 0) {
+            throw new SAXException("Illegal empty expression.");
+        }
+
+        handler.handleExpression(buf, 0, bufSize);
+
+        bufSize = 0;
+    }
+
+    protected void append(char ch) {
+        if (bufSize + 1 >= buf.length) {
+            char[] newBuf = new char[buf.length + bufGrow];
+            System.arraycopy(buf, 0, newBuf, 0, buf.length);
+            buf = newBuf;
+        }
+
+        buf[bufSize] = ch;
+        ++bufSize;
+    }
+}

Propchange: cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPExpressionParser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPMarkupLanguage.java
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPMarkupLanguage.java?rev=219681&r1=219680&r2=219681&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPMarkupLanguage.java (original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/markup/xsp/XSPMarkupLanguage.java Tue Jul 19 06:27:29 2005
@@ -61,7 +61,7 @@
                                                   AbstractXMLPipe filter,
                                                   ProgrammingLanguage language)
     {
-        PreProcessFilter prefilter = new PreProcessFilter(filter, filename, language);
+        PreProcessFilter prefilter = new PreProcessFilter(filter, filename, language, this);
         prefilter.enableLogging(getLogger());
         return prefilter;
     }
@@ -71,57 +71,14 @@
 //
 
     /**
-     * This preprocessor wraps the PCDATA into xsp:text elements.
+     * <code>{@link CocoonMarkupLanguage.PreProcessFilter PreProcessFilter}</code> that replaces
+     * XSP expressions.
+     * 
      * @see org.xml.sax.ContentHandler
      */
     protected class PreProcessFilter extends CocoonMarkupLanguage.PreProcessFilter {
-
-        private Stack stack;
-
-        public PreProcessFilter (AbstractXMLPipe filter, String filename, ProgrammingLanguage language) {
-            super(filter, filename, language);
-        }
-
-        public void startDocument() throws SAXException {
-            super.startDocument();
-            stack = new Stack();
-        }
-
-        public void startElement (String namespaceURI, String localName,
-                                  String qName, Attributes atts) throws SAXException {
-            stack.push(new String[] { namespaceURI, localName, qName} );
-            super.startElement(namespaceURI, localName, qName, atts);
-        }
-
-        public void endElement (String namespaceURI, String localName,
-                                String qName) throws SAXException {
-            stack.pop();
-            super.endElement(namespaceURI, localName, qName);
-        }
-
-        public void characters(char[] ch, int start, int length) throws SAXException {
-            String[] tag = (String[]) stack.peek();
-            String tagURI = tag[0];
-            String tagLName = tag[1];
-
-            boolean flag = XSPMarkupLanguage.this.getURI().equals(tagURI);
-            if (flag && tagLName.equals("page")) {
-                // Characters after xsp:page and before first element.
-                super.characters(ch, start, length);
-            } else if (flag && (tagLName.equals("expr") ||
-                    tagLName.equals("logic") || tagLName.equals("structure") ||
-                    tagLName.equals("include"))) {
-                super.characters(ch, start, length);
-            } else {
-                // Quote the string depending on the programming language
-                String value = String.valueOf(ch, start, length);
-                // Create a new element <xsp:text> that wrap the quoted PCDATA
-                super.startElement(XSPMarkupLanguage.this.getURI(), "text",
-                        localPrefix + ":text", XMLUtils.EMPTY_ATTRIBUTES);
-                super.characters(value.toCharArray(), 0, value.length());
-                super.endElement(XSPMarkupLanguage.this.getURI(), "text",
-                        localPrefix + ":text");
-            }
+        public PreProcessFilter(AbstractXMLPipe filter, String filename, ProgrammingLanguage language, XSPMarkupLanguage markup) {
+            super(new XSPExpressionFilter.XMLPipeAdapter(new XSPExpressionFilter(markup), filter), filename, language);
         }
     }
 }

Added: cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/samples/java/interpolation.xsp
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/samples/java/interpolation.xsp?rev=219681&view=auto
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/samples/java/interpolation.xsp (added)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/samples/java/interpolation.xsp Tue Jul 19 06:27:29 2005
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<!--
+  Copyright 1999-2004 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<!-- CVS $Id: hello.xsp 30932 2004-07-29 17:35:38Z vgritsenko $ -->
+
+<xsp:page
+  language="java"
+  xmlns:xsp-interpolation="http://apache.org/xsp/interpolation/1.0"
+  xmlns:xsp="http://apache.org/xsp">
+
+  <page>
+    <title>Attribute and Text Interpolation</title>
+    <content>
+      <p>
+        {#Constants.COMPLETE_NAME} allows to use <tt>{##expr}</tt>
+        to replace the value of the Java expression <tt>expr</tt>
+        within attribute values and text nodes.
+      </p>
+
+      <p>
+        For expression interpolation in text nodes the difference is to
+        the usual xsp:expr syntax is minor.
+        For dynamic attribute values, however, interpolation
+        keeps it much more concise and readable.
+      </p>
+      <pre>
+        <![CDATA[
+        <xsp:logic>
+          String world = "world";
+          String color = "red";
+        </xsp:logic>
+        ]]>
+      </pre>
+
+      <xsp:logic>
+        String world = "world";
+        String color = "red";
+      </xsp:logic>
+
+      <p>
+        Just compare this:
+      </p>
+      <pre><![CDATA[
+        <center style="color:{##color}">Hello {##world}!</center>]]>
+      </pre>
+      <center style="color:{#color}">Hello {#world}!</center>
+
+      <p>
+        to that:
+      </p>
+      <pre><![CDATA[
+        <center><xsp:attribute name="style">color:<xsp:expr>color</xsp:expr></xsp:attribute>Hello <xsp:expr>world</xsp:expr>!</center>]]>
+      </pre>
+      <center><xsp:attribute name="style">color:<xsp:expr>color</xsp:expr></xsp:attribute>Hello <xsp:expr>world</xsp:expr>!</center>
+
+    </content>
+  </page>
+
+</xsp:page>

Propchange: cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/samples/java/interpolation.xsp
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/samples/samples.xml
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/samples/samples.xml?rev=219681&r1=219680&r2=219681&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/samples/samples.xml (original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/samples/samples.xml Tue Jul 19 06:27:29 2005
@@ -40,6 +40,9 @@
    <sample name="Hello Stripped" href="java/stripped">
     Hello page with stripped empty spaces
    </sample>
+   <sample name="Interpolation" href="java/interpolation">
+    Using attribute and text interpolation.
+   </sample>
    <sample name="Simple XSP" href="java/simple">
     Simple XSP example showing usage of several logicsheets.
    </sample>

Modified: cocoon/branches/BRANCH_2_1_X/status.xml
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/status.xml?rev=219681&r1=219680&r2=219681&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/status.xml (original)
+++ cocoon/branches/BRANCH_2_1_X/status.xml Tue Jul 19 06:27:29 2005
@@ -196,6 +196,14 @@
 
   <changes>
   <release version="@version@" date="@date@">
+    <action dev="AN" type="add" fixes-bug="35228" due-to="Jochen Kuhnle" due-to-email="werbung@kuhnle.net">
+      XSP block: Added short-cut notation {#expr} for interpolation of
+      XSP expressions in attribute values and text nodes.
+      Removed wrapping of all text nodes from XSP source into &lt;xsp:text&gt;
+      elements before passing them to logicsheets.
+      (This feature was not used by the standard logicsheets but check your
+      custom logicsheets whether they depend on it.)
+    </action>
     <action dev="CZ" type="add">
       Portal block: Convert PortletPortalManager into portal manager aspect.
     </action>