diff --git a/core/src/main/java/org/apache/struts2/components/CheckboxList.java b/core/src/main/java/org/apache/struts2/components/CheckboxList.java
index e83091888..601ce5bdf 100644
--- a/core/src/main/java/org/apache/struts2/components/CheckboxList.java
+++ b/core/src/main/java/org/apache/struts2/components/CheckboxList.java
@@ -48,7 +48,7 @@ import com.opensymphony.xwork2.util.ValueStack;
allowDynamicAttributes = true)
public class CheckboxList extends ListUIBean {
final public static String TEMPLATE = "checkboxlist";
-
+
public CheckboxList(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
}
@@ -56,9 +56,19 @@ public class CheckboxList extends ListUIBean {
protected String getDefaultTemplate() {
return TEMPLATE;
}
-
+
public void evaluateExtraParams() {
super.evaluateExtraParams();
}
-}
\ No newline at end of file
+ /**
+ * Checkboxlist tag requires lazy evaluation as list of tags is dynamically generated using
+ *
+ * @return boolean true by default
+ */
+ @Override
+ protected boolean lazyEvaluation() {
+ return true;
+ }
+
+}
diff --git a/core/src/main/java/org/apache/struts2/components/ListUIBean.java b/core/src/main/java/org/apache/struts2/components/ListUIBean.java
index 26484f250..bfaffe6f1 100644
--- a/core/src/main/java/org/apache/struts2/components/ListUIBean.java
+++ b/core/src/main/java/org/apache/struts2/components/ListUIBean.java
@@ -195,7 +195,6 @@ public abstract class ListUIBean extends UIBean {
this.listTitle = listTitle;
}
-
public void setThrowExceptionOnNullValueAttribute(boolean throwExceptionOnNullValueAttribute) {
this.throwExceptionOnNullValueAttribute = throwExceptionOnNullValueAttribute;
}
diff --git a/core/src/main/java/org/apache/struts2/components/Radio.java b/core/src/main/java/org/apache/struts2/components/Radio.java
index 0315cb65b..ba5eb471f 100644
--- a/core/src/main/java/org/apache/struts2/components/Radio.java
+++ b/core/src/main/java/org/apache/struts2/components/Radio.java
@@ -57,7 +57,7 @@ import javax.servlet.http.HttpServletResponse;
allowDynamicAttributes = true)
public class Radio extends ListUIBean {
final public static String TEMPLATE = "radiomap";
-
+
public Radio(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
}
@@ -65,8 +65,19 @@ public class Radio extends ListUIBean {
protected String getDefaultTemplate() {
return TEMPLATE;
}
-
+
public void evaluateExtraParams() {
super.evaluateExtraParams();
}
-}
\ No newline at end of file
+
+ /**
+ * Radio tag requires lazy evaluation as list of tags is dynamically generated using
+ *
+ * @return boolean true by default
+ */
+ @Override
+ protected boolean lazyEvaluation() {
+ return true;
+ }
+
+}
diff --git a/core/src/main/java/org/apache/struts2/components/UIBean.java b/core/src/main/java/org/apache/struts2/components/UIBean.java
index 24b47fa14..85b3d1bb9 100644
--- a/core/src/main/java/org/apache/struts2/components/UIBean.java
+++ b/core/src/main/java/org/apache/struts2/components/UIBean.java
@@ -20,7 +20,9 @@ package org.apache.struts2.components;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
+import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -31,6 +33,7 @@ import org.apache.struts2.components.template.TemplateEngine;
import org.apache.struts2.components.template.TemplateEngineManager;
import org.apache.struts2.components.template.TemplateRenderingContext;
import org.apache.struts2.dispatcher.StaticContentLoader;
+import org.apache.struts2.util.ComponentUtils;
import org.apache.struts2.util.TextProviderHelper;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import org.apache.struts2.views.util.ContextUtil;
@@ -1272,10 +1275,16 @@ public abstract class UIBean extends Component {
public void setDynamicAttributes(Map tagDynamicAttributes) {
for (Map.Entry entry : tagDynamicAttributes.entrySet()) {
- String entryKey = entry.getKey();
+ String attrName = entry.getKey();
+ String attrValue = entry.getValue();
- if (!isValidTagAttribute(entryKey)) {
- dynamicAttributes.put(entryKey, entry.getValue());
+ if (!isValidTagAttribute(attrName)) {
+ if (ComponentUtils.containsExpression(attrValue) && !lazyEvaluation()) {
+ String translated = TextParseUtil.translateVariables('%', attrValue, stack);
+ dynamicAttributes.put(attrName, ObjectUtils.defaultIfNull(translated, attrValue));
+ } else {
+ dynamicAttributes.put(attrName, attrValue);
+ }
}
}
}
@@ -1296,4 +1305,14 @@ public abstract class UIBean extends Component {
}
}
+ /**
+ * Used to avoid evaluating attributes in {@link #evaluateParams()} or {@link #evaluateExtraParams()}
+ * as evaluation will happen in tag's template
+ *
+ * @return boolean false if evaluation should be performed in ftl
+ */
+ protected boolean lazyEvaluation() {
+ return false;
+ }
+
}
diff --git a/core/src/main/java/org/apache/struts2/components/template/FreemarkerTemplateEngine.java b/core/src/main/java/org/apache/struts2/components/template/FreemarkerTemplateEngine.java
index 3bbd2196b..91e155867 100644
--- a/core/src/main/java/org/apache/struts2/components/template/FreemarkerTemplateEngine.java
+++ b/core/src/main/java/org/apache/struts2/components/template/FreemarkerTemplateEngine.java
@@ -148,16 +148,13 @@ public class FreemarkerTemplateEngine extends BaseTemplateEngine {
}
};
- LOG.debug("Puts action on the top of ValueStack, just before the tag");
- action = stack.pop();
+ LOG.debug("Push tag on top of the stack");
stack.push(templateContext.getTag());
- stack.push(action);
try {
template.process(model, writer);
} finally {
- stack.pop(); // removes action
- stack.pop(); // removes tag
- stack.push(action); // puts back action
+ LOG.debug("Removes tag from top of the stack");
+ stack.pop();
}
}
diff --git a/core/src/main/resources/template/simple/checkboxlist.ftl b/core/src/main/resources/template/simple/checkboxlist.ftl
index 3fa27f189..87bef6651 100644
--- a/core/src/main/resources/template/simple/checkboxlist.ftl
+++ b/core/src/main/resources/template/simple/checkboxlist.ftl
@@ -30,7 +30,7 @@
<#assign itemKeyStr = stack.findString('top')>
#if>
<#if parameters.listLabelKey??>
- <#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
+ <#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
file for it's localized value. This is then used as a label -->
<#assign itemValue = struts.getText(stack.findString(parameters.listLabelKey))/>
<#elseif parameters.listValue??>
@@ -95,9 +95,10 @@
<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
+ <#global evaluate_dynamic_attributes = true/>
<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
/>
-
+
<#if parameters.id?has_content>
for="${parameters.id}-${itemCount}"<#rt/>
<#else>
@@ -106,11 +107,10 @@
class="checkboxLabel">${itemValue}
@s.iterator>
<#else>
-
#if>
<#if parameters.disabled!false>
disabled="disabled"<#rt/>
#if>
- />
\ No newline at end of file
+ />
diff --git a/core/src/main/resources/template/simple/dynamic-attributes.ftl b/core/src/main/resources/template/simple/dynamic-attributes.ftl
index 47a91139f..7f15aa48f 100644
--- a/core/src/main/resources/template/simple/dynamic-attributes.ftl
+++ b/core/src/main/resources/template/simple/dynamic-attributes.ftl
@@ -30,7 +30,11 @@
<#list aKeys?filter(acceptKey) as aKey><#rt/>
<#assign keyValue = parameters.dynamicAttributes.get(aKey)/>
<#if keyValue?is_string>
- <#assign value = struts.translateVariables(keyValue)!keyValue/>
+ <#if evaluate_dynamic_attributes!false == true>
+ <#assign value = struts.translateVariables(keyValue)!keyValue/><#rt/>
+ <#else>
+ <#assign value = keyValue/><#rt/>
+ #if>
<#else>
<#assign value = keyValue?string/>
#if>
diff --git a/core/src/main/resources/template/simple/radiomap.ftl b/core/src/main/resources/template/simple/radiomap.ftl
index 5c37a4b4b..597300e95 100644
--- a/core/src/main/resources/template/simple/radiomap.ftl
+++ b/core/src/main/resources/template/simple/radiomap.ftl
@@ -27,7 +27,7 @@
<#assign itemKeyStr = stack.findString('top')>
#if>
<#if parameters.listValueKey??>
- <#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
+ <#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
file for it's localized value. This is then used as a label -->
<#assign valueKey = stack.findString(parameters.listValueKey)!''/>
<#if valueKey?has_content>
@@ -94,9 +94,10 @@
<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
+<#global evaluate_dynamic_attributes = true/>
<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
/><#rt/>
><#rt/>
${itemValue}<#t/>
-@s.iterator>
\ No newline at end of file
+@s.iterator>
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java
index cf300e806..f42fdbccf 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java
@@ -347,6 +347,7 @@ public class TextfieldTest extends AbstractUITagTest {
tag.setValue("%{foo}");
tag.setSize("10");
tag.setDynamicAttribute(null, "anotherAttr", "%{foo}");
+ tag.setDynamicAttribute(null, "secondAttr", "second_%{foo}");
tag.doStartTag();
tag.doEndTag();
@@ -373,6 +374,7 @@ public class TextfieldTest extends AbstractUITagTest {
tag.setValue("%{foo}");
tag.setSize("10");
tag.setDynamicAttribute(null, "anotherAttr", "%{foo}");
+ tag.setDynamicAttribute(null, "secondAttr", "second_%{foo}");
tag.doStartTag();
setComponentTagClearTagState(tag, true); // Ensure component tag state clearing is set true (to match tag).
diff --git a/core/src/test/resources/org/apache/struts2/views/jsp/ui/Textfield-5.txt b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Textfield-5.txt
index 281847627..5dc125c18 100644
--- a/core/src/test/resources/org/apache/struts2/views/jsp/ui/Textfield-5.txt
+++ b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Textfield-5.txt
@@ -1,4 +1,4 @@
|
- |
+ |