You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by Nick Williams <ni...@nicholaswilliams.net> on 2013/11/17 09:47:13 UTC

EL 3.0 Streams Edge Case - Am I wrong or is Tomcat wrong?

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 ...

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 ...

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?

Thanks,

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


Re: EL 3.0 Streams Edge Case - Am I wrong or is Tomcat wrong?

Posted by Nick Williams <ni...@nicholaswilliams.net>.
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 <ni...@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-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: EL 3.0 Streams Edge Case - Am I wrong or is Tomcat wrong?

Posted by Nick Williams <ni...@nicholaswilliams.net>.
On Nov 18, 2013, at 12:21 PM, Konstantin Kolinko wrote:

> 2013/11/18 Nick Williams <ni...@nicholaswilliams.net>:
>> 
> 
>>> 
>>> Regarding the list() example,
>>> "map(u -> [u.username, u.firstName, u.lastName])"  creates a
>>> List<Object>  with 3 elements and you are asking for "lastName"
>>> property on that list.
>> 
>> No, this is not correct. The lambda expression "u -> [u.username, u.firstName, u.lastName]" RETURNS a List<Object>. But "u" is a User. I am creating a List<Object> where the first element is the user's username, the second element is the user's first name, and the third element is the user's last name. That is completely valid. It is also essentially identical to Section 2.3.6.4's example "p->[p.name, p.unitPrice]."
>> 
>>> 
>>> It is no wonder that it results in NumberFormatException. (Whether
>>> there should be other handling for wrong property name here, I do not
>>> know. One should look into what resolvers are being used here).
>> 
>> It should not result in any exception. It should work, because the syntax is valid, as explained above. :-)
>> 
> 
> You apply map() which replaces each user with a list [u.username,
> u.firstName, u.lastName].
> 
> Then you try to apply sorted() to that list. That sorted() fails as it
> cannot evaluate "u1.lastName.compareTo(u2.lastName)".
> 
> As I said - provide a simple example.

Okay, I see where the confusion is. Indeed, when I moved "map" to after "sorted" the list-literal started working. So it's only the map-literal that is failing. My apologies; I'll revise the bug.

> 
>>> 
>>>>>> 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)
>>>>>>      ...
>>>>>> 
>>> 
>>> Best regards,
>>> Konstantin Kolinko
>> 
>> Thanks,
>> 
>> Nick

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


Re: EL 3.0 Streams Edge Case - Am I wrong or is Tomcat wrong?

Posted by Konstantin Kolinko <kn...@gmail.com>.
2013/11/18 Nick Williams <ni...@nicholaswilliams.net>:
>

>>
>> Regarding the list() example,
>> "map(u -> [u.username, u.firstName, u.lastName])"  creates a
>> List<Object>  with 3 elements and you are asking for "lastName"
>> property on that list.
>
> No, this is not correct. The lambda expression "u -> [u.username, u.firstName, u.lastName]" RETURNS a List<Object>. But "u" is a User. I am creating a List<Object> where the first element is the user's username, the second element is the user's first name, and the third element is the user's last name. That is completely valid. It is also essentially identical to Section 2.3.6.4's example "p->[p.name, p.unitPrice]."
>
>>
>> It is no wonder that it results in NumberFormatException. (Whether
>> there should be other handling for wrong property name here, I do not
>> know. One should look into what resolvers are being used here).
>
> It should not result in any exception. It should work, because the syntax is valid, as explained above. :-)
>

You apply map() which replaces each user with a list [u.username,
u.firstName, u.lastName].

Then you try to apply sorted() to that list. That sorted() fails as it
cannot evaluate "u1.lastName.compareTo(u2.lastName)".

As I said - provide a simple example.

>>
>>>>> 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)
>>>>>       ...
>>>>>
>>
>> Best regards,
>> Konstantin Kolinko
>
> Thanks,
>
> Nick
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: users-help@tomcat.apache.org
>

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


Re: EL 3.0 Streams Edge Case - Am I wrong or is Tomcat wrong?

Posted by Nick Williams <ni...@nicholaswilliams.net>.
On Nov 18, 2013, at 12:05 PM, Konstantin Kolinko wrote:

> 2013/11/17 Nick Williams <ni...@nicholaswilliams.net>:
>> 
>> On Nov 17, 2013, at 6:00 AM, Konstantin Kolinko wrote:
>> 
>>> 2013/11/17 Nick Williams <ni...@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()}
>> 
> 
> Tomcat is performing correctly here.
> 
> (For a reference: the priorities are specified by Ch.1.16 of EL 3.0).
> 
>>>> 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?
>> 
> 
> The early '}' terminating the EL expression looks like a bug. It is OK
> to file it. Try to provide a simple test case though.
> 
> 
> Regarding the list() example,
> "map(u -> [u.username, u.firstName, u.lastName])"  creates a
> List<Object>  with 3 elements and you are asking for "lastName"
> property on that list.

No, this is not correct. The lambda expression "u -> [u.username, u.firstName, u.lastName]" RETURNS a List<Object>. But "u" is a User. I am creating a List<Object> where the first element is the user's username, the second element is the user's first name, and the third element is the user's last name. That is completely valid. It is also essentially identical to Section 2.3.6.4's example "p->[p.name, p.unitPrice]."

> 
> It is no wonder that it results in NumberFormatException. (Whether
> there should be other handling for wrong property name here, I do not
> know. One should look into what resolvers are being used here).

It should not result in any exception. It should work, because the syntax is valid, as explained above. :-)

> 
>>>> 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)
>>>>       ...
>>>> 
> 
> Best regards,
> Konstantin Kolinko

Thanks,

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


Re: EL 3.0 Streams Edge Case - Am I wrong or is Tomcat wrong?

Posted by Konstantin Kolinko <kn...@gmail.com>.
2013/11/17 Nick Williams <ni...@nicholaswilliams.net>:
>
> On Nov 17, 2013, at 6:00 AM, Konstantin Kolinko wrote:
>
>> 2013/11/17 Nick Williams <ni...@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()}
>

Tomcat is performing correctly here.

(For a reference: the priorities are specified by Ch.1.16 of EL 3.0).

>>> 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?
>

The early '}' terminating the EL expression looks like a bug. It is OK
to file it. Try to provide a simple test case though.


Regarding the list() example,
"map(u -> [u.username, u.firstName, u.lastName])"  creates a
List<Object>  with 3 elements and you are asking for "lastName"
property on that list.

It is no wonder that it results in NumberFormatException. (Whether
there should be other handling for wrong property name here, I do not
know. One should look into what resolvers are being used here).

>>> 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)
>>>        ...
>>>

Best regards,
Konstantin Kolinko

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


Re: EL 3.0 Streams Edge Case - Am I wrong or is Tomcat wrong?

Posted by Nick Williams <ni...@nicholaswilliams.net>.
On Nov 17, 2013, at 6:00 AM, Konstantin Kolinko wrote:

> 2013/11/17 Nick Williams <ni...@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-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: EL 3.0 Streams Edge Case - Am I wrong or is Tomcat wrong?

Posted by Konstantin Kolinko <kn...@gmail.com>.
2013/11/17 Nick Williams <ni...@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 ";" ?


> 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?


> 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

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