I don’t really understand how variables work in javax.el:
// Implemented by the EL implementation:
ExpressionFactory factory = ExpressionFactory.newInstance();
// Implemented by the user:
ELContext context = ...;
Object result = factory.createValueExpression(context1, "${foo.bar}", Object.class).getValue1(context);
Why is it needed to pass the context twice. Is it possible to pass two different contexts? Which is used for what purpose? What is the expected result of:
ValueExpression expr = factory.createValueExpression(context1, "${foo.bar}", Object.class).getValue(context2);
The javadoc of ExpressionFactory#createValueExpression /JSR-245 explains that:
The FunctionMapper and VariableMapper stored in the ELContext are used to resolve
functions and variables found in the expression. They can be null, in which case
functions or variables are not supported for this expression. The object returned
must invoke the same functions and access the same variable mappings regardless
of whether the mappings in the provided FunctionMapper and VariableMapper
instances change between calling ExpressionFactory.createValueExpression()
and any method on ValueExpression.
Furthermore “JSR-245 2.0.7 EL Variables” explains:
An EL variable does not directly refer to a model object that can then be resolved
by an ELResolver. Instead, it refers to an EL expression. The evaluation of that
EL expression gives the EL variable its value.
[...]
[...] in this [...] example:
<c:forEach var=“item” items=“#{model.list}”>
<h:inputText value=“#{item.name}”/>
</c:forEach>
While creating the “#{item.name}” expression, the “item” variable is mapped (in the VariableMapper) to some ValueExpression instance and the expression is bound to this ValueExpression instance. How is this ValueExpression created and how is it bound to different elements of “model.list”? How should this be implemented? It is possible to
create the ValueExpression once and reuse it for each iteration:
<!-- Same as above but using immediate evaluation -->
<c:forEach var=“item” items=“${model.list}”>
<h:inputText value=“${item.name}”/>
</c:forEach>
ValueExpression e1 = factory.createExpression(context,"#{model.list}");
variableMapper.setVariable("item", ??);
ValueExpression e2 = factory.createExpression(context,"#{item.name}");
for(Object item : (Collection<?>) e1.getValue(context)) {
??
}
Or is it necessary to create a new ValueExpression for iteration:
ValueExpression e1 = factory.createExpression(context,"#{model.list}");
for(Object item : (Collection<?>) e1.getValue(context)) {
variableMapper.setVariable("item", factory.createValueExpression(item,Object.class));
ValueExpression e2 = factory.createExpression(context,"#{item.name}");
Object name = e2.getValue(context);
...
}
The
variableMapperandfunctionMapperare only used at parse time (factory.createMethod(…)) while theelResolveris used during evaluation (expr.getValue(…)). Using different contexts to parse and evaluate an expression is fine.First of all: forget about “variables”. As explained in the Javadoc, their values are expressions, provided at parse time. Think of EL variables as constants or macros. You cannot redefine them, they are “burned” into the expression.
What you need is EL’s mechanism to resolve properties. In the following working example, I’m using an
ELContextimplementation from JUEL, just to keep things simple and focus on the relevan EL usage: