On Nov 18, 2013, at 12:05 PM, Konstantin Kolinko wrote: > 2013/11/17 Nick Williams <nicho...@nicholaswilliams.net>: >> >> 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()} >> > > 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-unsubscr...@tomcat.apache.org For additional commands, e-mail: users-h...@tomcat.apache.org