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