I dynamically add components to PreRenderViewEvent using the following http://blog.kennardconsulting.com/2010/10/safely-manipulating-component-tree-with.html method.
It works fine for the component adding the part, but I have a problem when I try to dynamically create ValueExpression expressions.
To be more specific, I have a problem when I try to create a dynamic ValueExpression using a dynamically passed parameter.
Let's try to explain an example ...
At the top level, I use a tag component (the component described in the tag file, not a composite, not a custom component.
<my:topComponent param=#{toto}"/>
In mine: topComponent, I pass the parameter to the nested component.
<my:nestedComponent param2=#{param}/>
This nestedComponent uses a custom component (in my case, I get the component from Datatable primitives), passing param2 to it as another parameter ...
<my:customComponent finalParam=#{param2}/>
In customComponent, I dynamically add some child components to PreRenderViewEvent and set some ValueExpression expressions for customComponent.
Some of these expressions use finalParam. So, I expand the finalParam value and then create a new ValueExpression expression:
String varName = getValueExpression("finalParam").getExpressionString().replace("#{", "").replace("}", "");
Then I create a dynamic value expression using this helper function:
public static ValueExpression createValueExpression(String expression, Class clazz) { FacesContext fc = FacesContext.getCurrentInstance(); ELContext elContext = fc.getELContext(); ExpressionFactory expFactory = fc.getApplication().getExpressionFactory(); ValueExpression ret = expFactory.createValueExpression(elContext, expression, clazz); return ret; }
Example:
ValueExpression dynExpression = JSFUtils.createValueExpression("#{" + varName + ".code" + "}"), Object.class);
In this example, the expression value is "# {param2.code}"
Then I can set this Expression value for my component:
this.setValueExpression("rowKey", dynExpression);
All this code is in a custom component class. I am using base class rendering.
However, the programmatically created ValueExpression is not correctly evaluated during rendering. For example, when a primitive processed by a datatable tries to calculate rowKey, # {param2.code} evaluates to "null" because param2 seems to be unknown.
What should I do to fix this? During debugging, I noticed that getValueExpression ("finalParam") has a VariableMapper set, and dynExpression does not (null value)
If I understood correctly, this VariableMapper is used to translate param2 into a parameter.
How can I instantiate a dynamic expression to keep the VariableMapper (s) chain? The question is the same for FunctionMapper.
Thanks in advance.
UPDATE I agree with Richard Kennard's answer: this seems like a mistake.
Since I can't wait years to fix, I use the following kludge to recursively resolve variables. It works for simple cases with my file MyFaces 2.1.9 / CODI 1.0.5 / OWB 1.1.6 / Tomcat 7.
public static String getValueExpressionExpression(ValueExpression valueExpression) { return valueExpression.getExpressionString().replace("#{", "").replace("}", ""); } public static String getMappedValueExpression(ValueExpression valueExpression) { ContextAwareTagValueExpression ctxAware = (ContextAwareTagValueExpression)valueExpression; if(ctxAware != null) { return getMappedValueExpression((WrappedValueExpression)ctxAware.getWrapped()); } return getValueExpressionExpression(valueExpression); } public static String getMappedValueExpression(WrappedValueExpression wrappedExpression) { String exprString = wrappedExpression.getExpressionString().replace("#{", "").replace("}", ""); String ret = exprString; try { Field valueExpression = WrappedValueExpression.class.getDeclaredField("valueExpression"); valueExpression.setAccessible(true); ValueExpressionImpl vei = (ValueExpressionImpl) valueExpression.get(wrappedExpression); Field varMapper = ValueExpressionImpl.class.getDeclaredField("varMapper"); varMapper.setAccessible(true); VariableMapperImpl vmi = (VariableMapperImpl) varMapper.get(vei); if(vmi != null) { String[] components = exprString.split("\\."); components[0] = getMappedValueExpression(vmi.resolveVariable(components[0])); ret = ""; for(int i = 0 ; i < components.length ; i++) { if(i != 0) { ret += "."; } ret += components[i]; } } } catch (Exception ex) { logger.error("Exception lors du mapping de l'expression EL " + exprString, ex); } finally { return ret; } }
It would be great to have cleaner versions of this workaround in MyFaces or Mojarra ...