Accessing the original expression of the ValueExpression to the taglib component

Can I access the ValueExpression string passed as an attribute value to my taglib component?

My goal is to programmatically infer the missing attribute values ​​from it. In this case, I try to avoid repeating the attribute as a literal.

Now:

<a:columnText title="name" value="#{entity.name}" sortBy="entity.name" />

      

:

<a:columnText title="name" value="#{entity.name}" />

      

-taglib.xml

<tag>
    <tag-name>columnText</tag-name>
    <source>column-text.xhtml</source>
    <attribute>
        <name>value</name>
        <required>true</required>
    </attribute>
    <attribute>
        <name>title</name>
        <required>false</required>
    </attribute>
    <attribute>
        <name>sortBy</name>
        <required>false</required>
    </attribute>
</tag>

      

columnar text.xhtml

<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:p="http://primefaces.org/ui" xmlns:h="http://java.sun.com/jsf/html"
                xmlns:a="http://www.kwa.nl/jsf/app-legacy" xmlns:c="http://java.sun.com/jstl/core">

    <c:if test="#{empty sortBy}">
        <a:expressionAsLiteral var="#{sortBy}" value="#{value}" />
    </c:if>

    <p:column headerText="#{title}" sortable="true" sortBy="#{sortBy}">
        <h:outputText value="#{value}"/>
        <a:sortByUnwrapper/>
    </p:column>
</ui:composition>

      

<a:expressionAsLiteral />

is intended to expand ValueExpression '# {value}' to '# {entity.name}' and set '# {sortBy}' to the literal 'entity.name'. This example feeds the sort column sortBy.

public class ExpressionAsLiteral extends TagHandler {

    private final TagAttribute var;
    private final TagAttribute value;

    public ExpressionAsLiteral(TagConfig config) {
        super(config);
        var = getRequiredAttribute("var");
        value = getRequiredAttribute("value");
    }    

    @Override
    public void apply(FaceletContext ctx, UIComponent parent) throws IOException {
       // abstracted for example.
       setAsLiteral(ctx, var, unwrapFaceletAttributeValue(ctx,value));
    }
}

      

My debugger tells me that the required information is hidden in the ValueExpressionImpl value private VariableMapper varMapper

. My problem is unpacking the returned ValueExpressionImpl without resorting to code smells.

My google-fu is failing. I feel like my approach is wrong, any advice?

EDIT # 1: Tried to do the following. Results in #{value}

instead of the desired attribute value #{iRow.title}

.

valueExpressionString = value.getValueExpression(ctx, Object.class).getExpressionString();

      

Variables in trace

+3


source to share


1 answer


As for the specific question, you can access the ValueExpression

representing the value of the tag attribute as defined in the template client through FaceletContext#getVariableMapper()

and then VariableMapper#resolveVariable()

passing the tag attribute name. Then you can get the literal string of the expression via ValueExpression#getExpressionString()

.

<my:expressionAsLiteral tagAttributeName="value" />

      

String tagAttributeName = getRequiredAttribute("tagAttributeName").getValue();
ValueExpression ve = context.getVariableMapper().resolveVariable(tagAttributeName);
String expression = ve.getExpressionString(); // #{entity.name}
String literal = expression.substring(2, expression.length() - 1); // entity.name

      

However, after that it is not possible to put it in the EL scope through some "var", because PF 4.x ultimately interprets the value sortBy="#{sortBy}"

literally as #{sortBy}

, not as entity.name

. You'd better nest it inside <p:column>

and the tag handler explicitly set it.

<p:column>
    <my:expressionAsLiteral tagAttributeName="value" componentAttributeName="sortBy" />
    #{value}
</p:column>

      

String componentAttributeName = getRequiredAttribute("componentAttributeName").getValue();
parent.getAttributes().put(componentAttributeName, literal);

      




As far as the functional problem you're actually trying to solve, everything works fine for PF 5.2. Based on the source code, they fixed it in 5.0 and improved it in 5.1.

/WEB-INF/tags/column.xhtml

:

<ui:composition 
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:p="http://primefaces.org/ui"
>
    <p:column sortBy="#{empty sortBy ? value : sortBy}">#{value}</p:column>
</ui:composition>

      

Template client:

<p:dataTable value="#{bean.items}" var="item">
    <my:column value="#{item.id}" />
    <my:column value="#{item.name}" sortBy="#{item.value}" />
    <my:column value="#{item.value}" />
</p:dataTable>

      

+2


source







All Articles