On 28/10/2011 11:17, Thorsten Scherler wrote:
I tried to use the "string-template" component but I am running into a problem 
when using $if(boolean)$.

I am ATM integrating Apache Shiro, a security framework that performs 
authentication, authorization, cryptography, and session management, into c3 
and want to use something in the spirit of their taglibs
<shiro:notAuthenticated>
      Please<a href="login.jsp">login</a>.
</shiro:notAuthenticated>

So what I did is I wrote a ShiroInterpreter implements LanguageInterpreter that 
can extract this info.
However the interface LanguageInterpreter defines the return type to String.

<map:transform type="string-template">
    <map:parameter name="isUser" value="{shiro:authenticated}"/>
</map:transform>

will fail then interpreting
$if(!isUser)$
    <a href="/logout">/logout</a>
$else$
    <a href="/login">/login</a>
$endif$

with
...
Caused by: org.stringtemplate.v4.compiler.STException: null
        at 
org.stringtemplate.v4.compiler.Compiler.reportMessageAndThrowSTException(Compiler.java:213)
 ~[ST4-4.0.4.jar:na]

If I use only $isUser$ I get the correct value. Reading "Conditional 
subtemplates" [1]
"You may only test whether an attribute is present or absent, preserving 
separation of model
and view. The only exception is that if an attribute value is a Boolean object, it 
will test it for true/false."
I think you've hit a bug of StringTemplate transformer: see the attached patch 
(for cocoon-sample in C3 sources) using conditional and boolean value from 
sitemap; ST generator works well and ST transformer raises an exception similar 
to the one reported above.
Ok I will have a look.

Thorsten,
see attached a patch for cocoon-stringtemplate and cocoon-sample that should fix your issue about $if$ with StringTemplateTransformer.

In fact, I've checked that problems arise with transformer only, while generator is safe in this respect; this happens because generator let ST handle the whole file, while transformer doesn't. Transformer's approach makes it able, however, to parse ST expressions in XML attributes as well.

If you want to try my patch, just apply it to fresh C3 sources, do an "mvn clean install" of cocoon-stringtemplate and afterwards launch "mvn jetty:run" under cocoon-sample: you will be able to watch results at http://localhost:8888/string-template/generator and http://localhost:8888/string-template/transformer.

I did not commit this yet because, as you can see from StringTemplateTransformer's source, my patch is limited to "$if$" case, and only when the whole $if$ evaluates to CDATA from XML point of view: i.e. this work

<root>
<a alt="$parameter$" href="$if(!booleanParameter)$
expression evaluated to true
$else$
expression evaluated to false
$endif$">link</a>
</root>

while this does not:

<root>
$if(!booleanParameter)$
<element>expression evaluated to true</element>
$else$
<element>expression evaluated to false</element>
$endif$
</root>

As an alternative to my patch's approach, StringTemplateTransformer could act in a way similar to StringTemplateGenerator's, i.e. buffering the whole input document and passing it as a whole to ST: what do you think?

Cheers.

--
Francesco Chicchiriccò

Apache Cocoon Committer and PMC Member
http://people.apache.org/~ilgrosso/

Index: cocoon-stringtemplate/src/test/java/org/apache/cocoon/stringtemplate/StringTemplateTransformerTest.java
===================================================================
--- cocoon-stringtemplate/src/test/java/org/apache/cocoon/stringtemplate/StringTemplateTransformerTest.java	(revisione 1190180)
+++ cocoon-stringtemplate/src/test/java/org/apache/cocoon/stringtemplate/StringTemplateTransformerTest.java	(copia locale)
@@ -15,41 +15,43 @@
  */
 package org.apache.cocoon.stringtemplate;
 
-import static junit.framework.Assert.*;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertNotNull;
 
 import java.io.ByteArrayOutputStream;
-import java.util.Collections;
-import junit.framework.Assert;
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
 import org.apache.cocoon.pipeline.NonCachingPipeline;
 import org.apache.cocoon.pipeline.Pipeline;
 import org.apache.cocoon.sax.SAXPipelineComponent;
 import org.apache.cocoon.sax.component.XMLGenerator;
 import org.apache.cocoon.sax.component.XMLSerializer;
+import org.apache.commons.io.FileUtils;
 import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLUnit;
 import org.junit.Test;
 
 public class StringTemplateTransformerTest {
 
-    private static final String INPUT =
-            "<test>"
-            + "<element param=\"$param$\">"
-            + "This text content also contains $param$"
-            + "</element>"
-            + "and also this contains $param$"
-            + "<element>while this does not</element>"
-            + "</test>";
-
     @Test
     public void test()
             throws Exception {
 
+        final String inputString = FileUtils.readFileToString(new File(
+                getClass().getResource("/transformerTest.xml").toURI()));
+
+
         final StringTemplateTransformer transformer =
                 new StringTemplateTransformer();
-        transformer.setup(Collections.singletonMap("param", (Object) "value"));
+        final Map<String, Object> params = new HashMap<String, Object>();
+        params.put("param", "value");
+        params.put("booleanParam", Boolean.FALSE);
+        transformer.setup(params);
 
         final Pipeline<SAXPipelineComponent> pipeline =
                 new NonCachingPipeline<SAXPipelineComponent>();
-        pipeline.addComponent(new XMLGenerator(INPUT));
+        pipeline.addComponent(new XMLGenerator(inputString));
         pipeline.addComponent(transformer);
         pipeline.addComponent(new XMLSerializer());
 
@@ -58,10 +60,14 @@
         pipeline.execute();
 
         final String actualDocument = new String(baos.toByteArray(), "UTF-8");
-        Assert.assertNotNull(actualDocument);
+        assertNotNull(actualDocument);
 
-        final String expectedDocument = INPUT.replaceAll("\\$param\\$", "value");
+        final String expectedDocument = inputString.replaceAll("\\$param\\$",
+                "value").replaceAll("\\$.*\\$", "").
+                replaceAll("expression evaluated to true", "");
 
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setIgnoreComments(true);
         final Diff diff = new Diff(expectedDocument, actualDocument);
         assertTrue("StringTemplate transformer didn't work as expected " + diff,
                 diff.identical());
Index: cocoon-stringtemplate/src/test/java/org/apache/cocoon/stringtemplate/StringTemplateGeneratorTest.java
===================================================================
--- cocoon-stringtemplate/src/test/java/org/apache/cocoon/stringtemplate/StringTemplateGeneratorTest.java	(revisione 1190180)
+++ cocoon-stringtemplate/src/test/java/org/apache/cocoon/stringtemplate/StringTemplateGeneratorTest.java	(copia locale)
@@ -19,7 +19,8 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import junit.framework.Assert;
 import org.apache.cocoon.pipeline.NonCachingPipeline;
 import org.apache.cocoon.pipeline.Pipeline;
@@ -27,6 +28,7 @@
 import org.apache.cocoon.sax.component.XMLSerializer;
 import org.apache.commons.io.FileUtils;
 import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLUnit;
 import org.junit.Test;
 
 public class StringTemplateGeneratorTest {
@@ -34,7 +36,10 @@
     private boolean doTest(final StringTemplateGenerator generator)
             throws Exception {
 
-        generator.setup(Collections.singletonMap("param", (Object) "value"));
+        final Map<String, Object> params = new HashMap<String, Object>();
+        params.put("param", "value");
+        params.put("booleanParam", Boolean.FALSE);
+        generator.setup(params);
 
         final Pipeline<SAXPipelineComponent> pipeline =
                 new NonCachingPipeline<SAXPipelineComponent>();
@@ -49,9 +54,11 @@
         Assert.assertNotNull(actualDocument);
 
         final String expectedDocument = FileUtils.readFileToString(
-                new File(getClass().getResource("/test.xml").toURI())).
-                replaceAll("\\$param\\$", "value");
+                new File(getClass().getResource("/generatorTest.xml").toURI())).
+                replaceAll("\\$param\\$", "value").replaceAll("\\$.*\\$", "").
+                replaceAll("expression evaluated to true", "");
 
+        XMLUnit.setIgnoreWhitespace(true);
         final Diff diff = new Diff(expectedDocument, actualDocument);
         assertTrue("StringTemplate generation didn't work as expected " + diff,
                 diff.identical());
@@ -64,7 +71,7 @@
             throws Exception {
 
         assertTrue(doTest(new StringTemplateGenerator(
-                getClass().getResource("/test.xml"))));
+                getClass().getResource("/generatorTest.xml"))));
     }
 
     @Test
@@ -72,7 +79,8 @@
             throws Exception {
 
         assertTrue(doTest(new StringTemplateGenerator(
-                new File(getClass().getResource("/test.xml").toURI()))));
+                new File(getClass().getResource("/generatorTest.xml").
+                toURI()))));
     }
 
     @Test
@@ -80,14 +88,16 @@
             throws Exception {
 
         assertTrue(doTest(new StringTemplateGenerator(
-                getClass().getResourceAsStream("/test.xml"))));
+                getClass().getResourceAsStream("/generatorTest.xml"))));
     }
 
     @Test
     public final void testString()
             throws Exception {
 
-        assertTrue(doTest(new StringTemplateGenerator(FileUtils.readFileToString(
-                new File(getClass().getResource("/test.xml").toURI())))));
+        assertTrue(doTest(new StringTemplateGenerator(
+                FileUtils.readFileToString(
+                new File(getClass().getResource("/generatorTest.xml").
+                toURI())))));
     }
 }
Index: cocoon-stringtemplate/src/test/resources/transformerTest.xml
===================================================================
--- cocoon-stringtemplate/src/test/resources/transformerTest.xml	(revisione 0)
+++ cocoon-stringtemplate/src/test/resources/transformerTest.xml	(revisione 0)
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+ -->
+<test>
+    <element param="$param$">
+        This text content also contains $param$
+    </element>
+    and also this contains $param$
+    <element>while this does not</element>
+$if(!booleanParam)$
+    expression evaluated to true
+$else$
+    expression evaluated to false
+$endif$
+</test>
Index: cocoon-stringtemplate/src/test/resources/generatorTest.xml
===================================================================
--- cocoon-stringtemplate/src/test/resources/generatorTest.xml	(revisione 1190180)
+++ cocoon-stringtemplate/src/test/resources/generatorTest.xml	(copia locale)
@@ -19,4 +19,9 @@
  -->
 <test>
     <element>$param$</element>
+$if(!booleanParam)$
+    expression evaluated to true
+$else$
+    expression evaluated to false
+$endif$
 </test>
Index: cocoon-stringtemplate/src/test/resources/test.xml
===================================================================
--- cocoon-stringtemplate/src/test/resources/test.xml	(revisione 1190180)
+++ cocoon-stringtemplate/src/test/resources/test.xml	(copia locale)
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  The ASF licenses this file
-  to you 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.
- -->
-<test>
-    <element>$param$</element>
-</test>
Index: cocoon-stringtemplate/src/main/java/org/apache/cocoon/stringtemplate/StringTemplateTransformer.java
===================================================================
--- cocoon-stringtemplate/src/main/java/org/apache/cocoon/stringtemplate/StringTemplateTransformer.java	(revisione 1190180)
+++ cocoon-stringtemplate/src/main/java/org/apache/cocoon/stringtemplate/StringTemplateTransformer.java	(copia locale)
@@ -18,7 +18,6 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
-
 import org.apache.cocoon.sax.AbstractSAXTransformer;
 import org.apache.commons.lang3.StringEscapeUtils;
 import org.slf4j.Logger;
@@ -37,13 +36,17 @@
             LoggerFactory.getLogger(StringTemplateTransformer.class);
 
     private final transient Map<String, Object> parameters;
+
     private final transient StringBuilder accumulator;
 
+    private boolean flushSuspended;
+
     public StringTemplateTransformer() {
         super();
 
         this.parameters = new HashMap<String, Object>();
         this.accumulator = new StringBuilder();
+        this.flushSuspended = false;
     }
 
     @Override
@@ -54,7 +57,8 @@
     }
 
     @Override
-    public void setConfiguration(final Map<String, ? extends Object> configuration) {
+    public void setConfiguration(
+            final Map<String, ? extends Object> configuration) {
         this.setup((Map<String, Object>) configuration);
     }
 
@@ -68,7 +72,8 @@
 
         for (Entry<String, Object> eachEntry : this.parameters.entrySet()) {
             final String key = eachEntry.getKey().replace(".", "_");
-            final String value = StringEscapeUtils.escapeXml(eachEntry.getValue().toString());
+            final String value = StringEscapeUtils.escapeXml(
+                    eachEntry.getValue().toString());
             stringTemplate.add(key, value);
 
             if (LOG.isDebugEnabled()) {
@@ -81,8 +86,10 @@
         return stringTemplate.render();
     }
 
-    private void flushAccumulator() throws SAXException {
-        if (this.accumulator.length() == 0) {
+    private void flushAccumulator()
+            throws SAXException {
+
+        if (this.accumulator.length() == 0 || this.flushSuspended) {
             return;
         }
 
@@ -93,31 +100,49 @@
     }
 
     @Override
-    public void startElement(final String uri, final String localName, final String name, final Attributes attributes)
+    public void startElement(final String uri, final String localName,
+            final String name, final Attributes attrs)
             throws SAXException {
-        final AttributesImpl renderedAttributes = new AttributesImpl();
 
-        for (int i = 0; i < attributes.getLength(); i++) {
-            final String attributeURI = attributes.getURI(i);
-            final String attributeLocalName = attributes.getLocalName(i);
-            final String attributeQName = attributes.getQName(i);
-            final String attributeType = attributes.getType(i);
-            final String attributeValue = this.renderTemplate(attributes.getValue(i));
-            renderedAttributes.addAttribute(attributeURI, attributeLocalName, attributeQName, attributeType, attributeValue);
+        final AttributesImpl renderAttrs = new AttributesImpl();
+
+        for (int i = 0; i < attrs.getLength(); i++) {
+            renderAttrs.addAttribute(attrs.getURI(i),
+                    attrs.getLocalName(i),
+                    attrs.getQName(i),
+                    attrs.getType(i),
+                    this.renderTemplate(attrs.getValue(i)));
         }
 
         this.flushAccumulator();
-        super.startElement(uri, localName, name, renderedAttributes);
+        if (!this.flushSuspended) {
+            super.startElement(uri, localName, name, renderAttrs);
+        }
     }
 
     @Override
-    public void characters(final char[] characters, final int start, final int length) throws SAXException {
+    public void characters(final char[] characters, final int start,
+            final int length)
+            throws SAXException {
+
+        final String str = new String(characters, start, length);
+        if (str.contains("$if")) {
+            this.flushSuspended = true;
+        } else if (str.contains("$endif$")) {
+            this.flushSuspended = false;
+        }
+
         this.accumulator.append(characters, start, length);
     }
 
     @Override
-    public void endElement(final String uri, final String localName, final String name) throws SAXException {
+    public void endElement(final String uri, final String localName,
+            final String name)
+            throws SAXException {
+
         this.flushAccumulator();
-        super.endElement(uri, localName, name);
+        if (!this.flushSuspended) {
+            super.endElement(uri, localName, name);
+        }
     }
 }
Index: cocoon-sample/src/main/resources/COB-INF/string-template/generatorTemplate.xml
===================================================================
--- cocoon-sample/src/main/resources/COB-INF/string-template/generatorTemplate.xml	(revisione 1190180)
+++ cocoon-sample/src/main/resources/COB-INF/string-template/generatorTemplate.xml	(copia locale)
@@ -24,5 +24,11 @@
   <body>
     <h3>StringTemplate demo</h3>
     <p>parameter=$parameter$</p>
+    <p>booleanParameter=$booleanParameter$</p>
+$if(!booleanParameter)$
+expression evaluated to true
+$else$
+expression evaluated to false
+$endif$
   </body>
 </html>
Index: cocoon-sample/src/main/resources/COB-INF/string-template/template.xml
===================================================================
--- cocoon-sample/src/main/resources/COB-INF/string-template/template.xml	(revisione 1190180)
+++ cocoon-sample/src/main/resources/COB-INF/string-template/template.xml	(copia locale)
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  The ASF licenses this file
-  to you 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.
- -->
-<html>
-  <head>
-    <title>StringTemplate demo</title>
-  </head>
-  <body>
-    <h3>StringTemplate demo</h3>
-    <p>parameter=$parameter$</p>
-  </body>
-</html>
Index: cocoon-sample/src/main/resources/COB-INF/string-template/transformerTemplate.xml
===================================================================
--- cocoon-sample/src/main/resources/COB-INF/string-template/transformerTemplate.xml	(revisione 0)
+++ cocoon-sample/src/main/resources/COB-INF/string-template/transformerTemplate.xml	(revisione 0)
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+ -->
+<html>
+  <head>
+    <title>StringTemplate demo</title>
+  </head>
+  <body>
+    <h3>StringTemplate demo</h3>
+    <p>parameter=$parameter$</p>
+    <p>booleanParameter=$booleanParameter$</p>
+<a alt="$parameter$" href="$if(!booleanParameter)$
+expression evaluated to true
+$else$
+expression evaluated to false
+$endif$">link</a>
+  </body>
+</html>
Index: cocoon-sample/src/main/resources/COB-INF/sitemap.xmap
===================================================================
--- cocoon-sample/src/main/resources/COB-INF/sitemap.xmap	(revisione 1190180)
+++ cocoon-sample/src/main/resources/COB-INF/sitemap.xmap	(copia locale)
@@ -550,15 +550,17 @@
     <!-- StringTemplate -->
     <map:pipeline type="noncaching">
       <map:match wildcard="string-template/generator">
-        <map:generate type="string-template" src="string-template/template.xml">
+        <map:generate type="string-template" src="string-template/generatorTemplate.xml">
 	  <map:parameter name="parameter" value="A value"/>
+	  <map:parameter name="booleanParameter" value="{jexl:cocoon.request.isSecure()}"/>
         </map:generate>
         <map:serialize/>
       </map:match>
       <map:match wildcard="string-template/transformer">
-        <map:generate src="string-template/template.xml"/>
+        <map:generate src="string-template/transformerTemplate.xml"/>
         <map:transform type="string-template">
 	  <map:parameter name="parameter" value="Another value"/>
+	  <map:parameter name="booleanParameter" value="{jexl:cocoon.request.isSecure()}"/>
         </map:transform>
         <map:serialize/>
       </map:match>

Reply via email to