juel
juel copied to clipboard
Not possible to transform String to enumeration value in special case
This small class reproduces an issue we had with JUEL and OpenJDK 1.7. The interesting thing of this case is that RALPH is internally a subclass of Actor, so it is not an enum and it is not possible to transform into a member of the enumeration and according to the EL specification it should work.
package com.juel;
import de.odysseus.el.ExpressionFactoryImpl;
import de.odysseus.el.TreeValueExpression;
import de.odysseus.el.util.SimpleContext;
public class App
{
public static enum Actor
{
HOMER, BART, RALPH
{
@Override
public String greeting()
{
return "I'm special";
}
};
public String greeting()
{
return "Hi, I'm " + name();
}
}
public static void main(String[] args) throws Exception
{
ExpressionFactoryImpl expressionFactory = new ExpressionFactoryImpl();
SimpleContext context = new SimpleContext();
context.setVariable("actor", expressionFactory.createValueExpression(Actor.HOMER, Actor.class));
context.setVariable("actor2", expressionFactory.createValueExpression(Actor.RALPH, Actor.class));
TreeValueExpression e = expressionFactory.createValueExpression(context, "${actor == 'HOMER'}", boolean.class);
System.out.println(e.getValue(context));
TreeValueExpression e2 = expressionFactory.createValueExpression(context, "${actor2 == 'HOMER'}", boolean.class);
System.out.println(e2.getValue(context)); // kaboom!
}
}
Output:
true
Exception in thread "main" javax.el.ELException: Cannot coerce 'HOMER' of class java.lang.String to class com.juel.App$Actor$1 (incompatible type)
at de.odysseus.el.misc.TypeConverterImpl.coerceStringToType(TypeConverterImpl.java:287)
at de.odysseus.el.misc.TypeConverterImpl.coerceToType(TypeConverterImpl.java:348)
at de.odysseus.el.misc.TypeConverterImpl.convert(TypeConverterImpl.java:365)
at de.odysseus.el.tree.Bindings.convert(Bindings.java:138)
at de.odysseus.el.misc.BooleanOperations.eq(BooleanOperations.java:161)
at de.odysseus.el.tree.impl.ast.AstBinary$4.apply(AstBinary.java:52)
at de.odysseus.el.tree.impl.ast.AstBinary$SimpleOperator.eval(AstBinary.java:31)
at de.odysseus.el.tree.impl.ast.AstBinary.eval(AstBinary.java:110)
at de.odysseus.el.tree.impl.ast.AstEval.eval(AstEval.java:51)
at de.odysseus.el.tree.impl.ast.AstNode.getValue(AstNode.java:31)
at de.odysseus.el.TreeValueExpression.getValue(TreeValueExpression.java:122)
at com.juel.App.main(App.java:37)
Hi!
RALPH is an enum. Please use something like ${actor2.toString() == 'HOMER'}.
Hi, Thanks for the response. Our current workaround is using the enum's method ".name()" to get its name.
The question is whether it should work correctly or not, as it's quite a big corner case and I couldn't find anybody with the same issue.
Also, when I mean that it is not an enum I mean that if you run System.out.println(Actor.RALPH.getClass().isEnum());
you get false on OpenJDK 1.7
Using name() is better than toString() of course. Whether it should work is a good question. And to be honest, it probably should...
In the "context", we know the "actor2" is a "com.juel.App$Actor", but when evaluate "${actor2 == 'HOMER'}", the eq function treat "actor2" as a Object, o1.getClass will get "com.juel.App$Actor$1". There is no way JUEL know how to convert 'HOMER' to a "com.juel.App$Actor$1". So special RALPH is indeed different from HOMER and BART. And I don't think this is a bug. see BooleanOperations.java 136~170
Implemented spec of JUEL is JSP 2.2 == EL 2.1:
Chapter 1.8.2 2 says:
1.8.2 A {==,!=,eq,ne} B ... ■ If A or B is an enum, coerce both A and B to enum, apply operatorr ...
Next spec EL 3.0:
The JSR-000341 Expression Language 3.0 Final Release specification from 2013 1 states in chapter 1.9.2:
1.9.2 A {==,!=,eq,ne} B ...
- If A or B is an enum, coerce both A and B to enum, apply operator ....
From my understanding this officially means since JUEL wants to implement EL2.1, it is a bug.