> On 8 Jul 2015, at 23:21, Brett Ryan <[email protected]> wrote:
> 
>> 
>> On 8 Jul 2015, at 21:32, Brett Ryan <[email protected]> wrote:
>> 
>> Nicolas Le Bas <mail@...> writes:
>> 
>>> I've been looking at this pull request, and I like it. I understand its
>>> value, but it raises a number of interesting challenges and questions.
>>> 
>>> *Summary*
>>> 
>>> Brett wants to support evaluation of expressions in the attributes of
>>> <item>.
>> 
>> Thankyou. To be precise, I'm looking for evaluations of expressions on any
>> attribute that can contain multiple values. mck's solution that
>> `add-list-attribute` can be used, while verbose is sufficient, however;
>> presently an NPE is being thrown on the use of the `expression` attribute on
>> `add-list-attribute`.
> 
> I've tracked down the NPE to two things. First, if Attribute has a null 
> value, it returns the value null, which IMHO is something I discourage, 
> toString should always be a string representation of a value, not null, 
> alternatives would be "null" or "(null)".
> 
> While this is the cause of the NPE, I've now found that uses of 
> `<add-attribute expression="${some.val}"/>` within `<add-list-attribute>` are 
> presently not supported.
> 
> I shall attempt an enhancement to support this but due to the way tiles is 
> structured it would ultimately be returning nested copies of lists which is 
> similar to my original solution (with the added nesting for child properties).
> 
> I would like to complete this sooner but might not be able to provide 
> something before the weekend. Is there a preferred branch? I was going to 
> submit this on the 3_0_X branch.
> 
> Also, I've only created a few pull requests in the past for other projects, 
> what's the best practice for submitting an enhancement? I've created a new 
> branch rooted at commit cc11955, but I'm not sure how to create a pull 
> request from that, will need to test, sorry.
> 
> -Brett

So I've come up with a solution, though it kinda sucks. In what I'm starting to 
learn would I be correct in stating that tiles is somewhat inconsistent with 
how it presents attributes to the view? What I mean by this is that there is 
some discrepancy with how tiles presents attributes from `put-attribute` vs 
`put-list-attribute`.

With `put-attribute` the attribute itself is not exposed to the view, however 
with the `put-list-attribute` everything is. This makes things tricky as what I 
was originally intending to alter AbstractAttributeEvaluator#evaluate to 
process the list elements and recursively build up a new list for the view 
which contains an evaluated version of all attributes.

I've done this, and it works, but; I'm now faced with the dilemma that 
previously accessing a list-attribute would require accessing the value 
property of the attribute as it's exposed to the view.

So, what should I do to keep backwards compatible? I've had to clone Attributes 
and set the value to the evaluated expression (where value is null) and return 
the new mutated attribute, but I don't like the approach, it's unnecessary 
IMHO. I was required to do it though, it turns out that the following:

    <tiles:importAttribute name="main" toName="main" ignore="true"/>
    <c:forEach var="x" items="${main}">
      <tiles:insertAttribute value="${x}" flush="true" />
    </c:forEach>

Would break as for whatever known reason it's trying to cast "x" back to an 
attribute, which I couldn't see why and honestly couldn't be bothered figuring 
out right now.


If anyone can give me guidance on what I should do I can have a pull request 
completed tomorrow for allowing add-list-attribute's to evaluate expressions.

Following is the change toAbstractAttributeEvaluator#evaluate(Attribute, 
Request) that would be submitted if anyones interested:


    @Override
    public Object evaluate(Attribute attribute, Request request) {
        if (attribute == null) {
            throw new IllegalArgumentException("The attribute cannot be null");
        }

        Object retValue = attribute.getValue();

        if (retValue == null) {
            Expression expression = attribute.getExpressionObject();
            if (expression != null) {
                log.debug("Evaluating expression: 
[attribute={},expression={}]", attribute, expression);
                retValue = evaluate(attribute.getExpressionObject()
                        .getExpression(), request);
            }
        } else if (retValue instanceof List) {
            log.debug("Evaluating list for expressions: {}", retValue);
            retValue = evaluateList((List) retValue, request);
        }

        log.debug("Returning result: {}", retValue);
        return retValue;
    }

    private List evaluateList(List src, Request request) {
        List res = new ArrayList();
        for (Object val : src) {
            if (val instanceof ListAttribute) {
                log.debug("Evaluating list entry (ListAttribute): {}", val);
                res.add(evaluateList(((ListAttribute) val).getValue(), 
request));
            } else if (val instanceof Attribute) {
                log.debug("Evaluating list entry (Attribute): {}", val);
                Attribute att = (Attribute) val;
                if (att.getValue() != null) {
                    res.add(att.getValue() instanceof List
                            ? evaluateList((List) att.getValue(), request)
                            : att);
                } else {
                    Expression expression = att.getExpressionObject();
                    if (expression != null) {
                        log.debug("Evaluating list entry expression: {}", 
expression);
                        att = att.clone();
                        att.setValue(evaluate(expression.getExpression(), 
request));
                        res.add(att);
                    } else {
                        res.add(att);
                    }
                }
            } else {
                log.debug("Evaluating list entry ({}): {}", val.getClass(), 
val);
                res.add(val);
            }
        }
        return res;
    }


Reply via email to