Being convinced now that this is a Tomcat bug in violation of the spec--and not 
something I am doing wrong--I'm going to go ahead and file a bug now.

N

On Nov 17, 2013, at 9:32 AM, Nick Williams wrote:

> 
> On Nov 17, 2013, at 6:00 AM, Konstantin Kolinko wrote:
> 
>> 2013/11/17 Nick Williams <nicho...@nicholaswilliams.net>:
>>> I have an EL 3.0 edge case that I need help understanding. Am I doing 
>>> something wrong (I don't think so) or is the Tomcat 8.0 implementation 
>>> missing something?
>>> 
>>> Consider the following EL expression:
>>> 
>>>       ${users.stream()
>>>              .filter(u -> fn:contains(u.username, '1'))
>>>              .sorted((u1, u2) -> u1.lastName.compareTo(u2.lastName) == 0 ? 
>>> u1.firstName.compareTo(u2.firstName) : u1.lastName.compareTo(u2.lastName))
>>>              .toList()}
>>> 
>>> This works as expected. However, it results in potentially evaluating 
>>> u1.lastName.compareTo(u2.lastName) twice. My understanding is that the 
>>> right-hand side of a lambda expression can be any valid EL expression, so I 
>>> believe this should also work:
>>> 
>>>       ${users.stream()
>>>              .filter(u -> fn:contains(u.username, '1'))
>>>              .sorted((u1, u2) -> x = u1.lastName.compareTo(u2.lastName); x 
>>> == 0 ? u1.firstName.compareTo(u2.firstName) : x)
>>>              .toList()}
>>> 
>>> However, this doesn't evaluate. I get the following error instead:
>>> 
>>> org.apache.el.parser.ParseException: Encountered " "=" "= "" at line 3, 
>>> column 38.
>>> Was expecting one of:
>>>   "." ...
>>>   ")" ...
>>>   etc ...
>>> 
>> 
>> What if you add "(" ")" ?
>> What operator has higher priority, "->" or ";" ?
> 
> "->" has higher priority than both "=" and ";", according to the spec. In 
> this particular case, I'm not sure whether that means parenthesis are 
> absolutely required or not. However, I can confirm that adding parenthesis 
> here solves this problem, so perhaps that's what I was doing wrong for this 
> error. This expression now works:
> 
>        ${users.stream()
>               .filter(u -> fn:contains(u.username, '1'))
>               .sorted((u1, u2) -> (x = u1.lastName.compareTo(u2.lastName); x 
> == 0 ? u1.firstName.compareTo(u2.firstName) : x))
>               .toList()}
> 
>>> Next I tried to reduce the properties present in each user using the stream 
>>> "map" method. Once again, with the understanding that the right-hand side 
>>> of a lambda expression can be any valid EL expression, I use an EL Map 
>>> literal to construct a reduced set of properties:
>>> 
>>>       ${users.stream()
>>>              .filter(u -> fn:contains(u.username, '1'))
>>>              .map(u -> {'username':u.username, 'first':u.firstName, 
>>> 'last':u.lastName})
>>>              .sorted((u1, u2) -> u1.lastName.compareTo(u2.lastName) == 0 ? 
>>> u1.firstName.compareTo(u2.firstName) : u1.lastName.compareTo(u2.lastName))
>>>              .toList()}
>>> 
>>> However, that doesn't work and I get this error:
>>> 
>>> org.apache.el.parser.ParseException: Encountered "<EOF>" at line 3, column 
>>> 88.
>>> Was expecting one of:
>>>   "." ...
>>>   ")" ...
>>>   etc ...
>>> 
>> 
>> I do not understand the above.  Can you provide a simple test case?
>> Which one of the expressions does not work? Can you remove the others?
> 
> So, as mentioned above, the following expression works:
> 
>        ${users.stream()
>               .filter(u -> fn:contains(u.username, '1'))
>               .sorted((u1, u2) -> (x = u1.lastName.compareTo(u2.lastName); x 
> == 0 ? u1.firstName.compareTo(u2.firstName) : x))
>               .toList()}
> 
> If I now add the "map" operation to it, I get the EOF error. Nothing else 
> about the expression changed:
> 
>        ${users.stream()
>               .filter(u -> fn:contains(u.username, '1'))
>               .sorted((u1, u2) -> (x = u1.lastName.compareTo(u2.lastName);
>                    x == 0 ? u1.firstName.compareTo(u2.firstName) : x))
>               .map(u -> {'username':u.username, 'first':u.firstName, 
> 'last':u.lastName})
>               .toList()}
> 
> javax.el.ELException: Failed to parse the expression [${users.stream()
>               .filter(u -> fn:contains(u.username, '1'))
>               .sorted((u1, u2) -> (x = u1.lastName.compareTo(u2.lastName);
>                    x == 0 ? u1.firstName.compareTo(u2.firstName) : x))
>               .map(u -> {'username':u.username, 'first':u.firstName,
>                    'last':u.lastName}]
>    ...
> <root cause>
> org.apache.el.parser.ParseException: Encountered "<EOF>" at line 6, column 
> 38. Was expecting one of:
>    "." ...
>    ")" ...
>    etc...
> 
> Notice that it thinks the expression is ending after the closing } of the 
> map-literal. Now the example in 2.3.6.4 of the specification alludes to the 
> fact that my use of the map-literal here is correct. In that example they use 
> a list-literal instead (.map(p->[p.name, p.unitPrice])). I tried changing to 
> use a list-literal instead of a map-literal, and that's when I got the 
> NumberFormatException described earlier. I _believe_ both should be legal; 
> the specification clearly intends that at least using the list-literal should 
> be legal. Neither work in Tomcat.
> 
> Does this make sense?
> 
>>> Section 2.3.6.4 of the specification uses the following example, where a 
>>> LIST literal is used as the right-hand side of the mapping lambda 
>>> expression:
>>> 
>>> products.stream().filter(p->p.unitPrice >= 10).
>>>       .map(p->[p.name, p.unitPrice])
>>>       .toList()
>>> 
>>> I tried to use this exact syntax, as shown in the spec, with my example:
>>> 
>>>       ${users.stream()
>>>              .filter(u -> fn:contains(u.username, '1'))
>>>              .map(u -> [u.username, u.firstName, u.lastName])
>>>              .sorted((u1, u2) -> u1.lastName.compareTo(u2.lastName) == 0 ? 
>>> u1.firstName.compareTo(u2.firstName) : u1.lastName.compareTo(u2.lastName))
>>>              .toList()}
>>> 
>>> And now I get this lovely error:
>>> 
>>> javax.el.ELException: java.lang.NumberFormatException: For input string: 
>>> "lastName"
>>>       javax.el.BeanELResolver.invoke(BeanELResolver.java:185)
>>>       
>>> org.apache.jasper.el.JasperELResolver.invoke(JasperELResolver.java:147)
>>>       org.apache.el.parser.AstValue.getValue(AstValue.java:158)
>>>       ...
>>> 
>>> I'm sure I'm doing something wrong here, but I'm not exactly sure what. On 
>>> the other hand, it's possible that the Tomcat 8.0 implementation is just 
>>> wrong. Can someone shed some light on this?
>>> 
>> 
>> Best regards,
>> Konstantin Kolinko
> 
> Thanks,
> 
> Nick


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to