Skip to content

Commit

Permalink
Merge pull request #525 from apache/WW-5117-evaluate-dynamic-attribut…
Browse files Browse the repository at this point in the history
…es-cherrypick

[WW-5117] Evaluates dynamic attributes - cherrypick
  • Loading branch information
yasserzamani committed Jan 18, 2022
2 parents 60e1212 + 7ed77f5 commit 79e1b22
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 24 deletions.
16 changes: 13 additions & 3 deletions core/src/main/java/org/apache/struts2/components/CheckboxList.java
Expand Up @@ -48,17 +48,27 @@
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);
}

protected String getDefaultTemplate() {
return TEMPLATE;
}

public void evaluateExtraParams() {
super.evaluateExtraParams();
}

}
/**
* Checkboxlist tag requires lazy evaluation as list of tags is dynamically generated using <s:iterator/>
*
* @return boolean true by default
*/
@Override
protected boolean lazyEvaluation() {
return true;
}

}
Expand Up @@ -195,7 +195,6 @@ public void setListTitle(String listTitle) {
this.listTitle = listTitle;
}


public void setThrowExceptionOnNullValueAttribute(boolean throwExceptionOnNullValueAttribute) {
this.throwExceptionOnNullValueAttribute = throwExceptionOnNullValueAttribute;
}
Expand Down
17 changes: 14 additions & 3 deletions core/src/main/java/org/apache/struts2/components/Radio.java
Expand Up @@ -57,16 +57,27 @@
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);
}

protected String getDefaultTemplate() {
return TEMPLATE;
}

public void evaluateExtraParams() {
super.evaluateExtraParams();
}
}

/**
* Radio tag requires lazy evaluation as list of tags is dynamically generated using <s:iterator/>
*
* @return boolean true by default
*/
@Override
protected boolean lazyEvaluation() {
return true;
}

}
25 changes: 22 additions & 3 deletions core/src/main/java/org/apache/struts2/components/UIBean.java
Expand Up @@ -20,7 +20,9 @@

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;
Expand All @@ -31,6 +33,7 @@
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;
Expand Down Expand Up @@ -1272,10 +1275,16 @@ public void setTooltipIconPath(String tooltipIconPath) {

public void setDynamicAttributes(Map<String, String> tagDynamicAttributes) {
for (Map.Entry<String, String> 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);
}
}
}
}
Expand All @@ -1296,4 +1305,14 @@ public void copyParams(Map<String, Object> params) {
}
}

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

}
Expand Up @@ -148,16 +148,13 @@ public void close() throws IOException {
}
};

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();
}
}

Expand Down
8 changes: 4 additions & 4 deletions core/src/main/resources/template/simple/checkboxlist.ftl
Expand Up @@ -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??>
Expand Down Expand Up @@ -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" />
/>
<label<#rt/>
<label<#rt/>
<#if parameters.id?has_content>
for="${parameters.id}-${itemCount}"<#rt/>
<#else>
Expand All @@ -106,11 +107,10 @@
class="checkboxLabel">${itemValue}</label>
</@s.iterator>
<#else>
&nbsp;
</#if>
<input type="hidden" id="__multiselect_${parameters.id}" name="__multiselect_${parameters.name}"
value=""<#rt/>
<#if parameters.disabled!false>
disabled="disabled"<#rt/>
</#if>
/>
/>
Expand Up @@ -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>
Expand Down
5 changes: 3 additions & 2 deletions core/src/main/resources/template/simple/radiomap.ftl
Expand Up @@ -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>
Expand Down Expand Up @@ -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/>
<label for="${parameters.id}${itemKeyStr?replace(".", "_")}"<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl"/>><#rt/>
${itemValue}<#t/>
</label>
</@s.iterator>
</@s.iterator>
Expand Up @@ -347,6 +347,7 @@ public void testSimple_recursionTest() throws Exception {
tag.setValue("%{foo}");
tag.setSize("10");
tag.setDynamicAttribute(null, "anotherAttr", "%{foo}");
tag.setDynamicAttribute(null, "secondAttr", "second_%{foo}");

tag.doStartTag();
tag.doEndTag();
Expand All @@ -373,6 +374,7 @@ public void testSimple_recursionTest_clearTagStateSet() throws Exception {
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).
Expand Down
@@ -1,4 +1,4 @@
<tr>
<td class="tdLabel"><label for="myname" class="label">mylabel:</label></td>
<td class="tdInput"><input type="text" name="myname" size="10" value="%{1+1}" id="myname" anotherAttr="%{1+1}"/></td>
<td class="tdInput"><input type="text" name="myname" size="10" value="%{1+1}" id="myname" anotherAttr="%{1+1}" secondAttr="second_%{1+1}"/></td>
</tr>

0 comments on commit 79e1b22

Please sign in to comment.