I want to achieve this using dynamic parameters for value expressions:
<h:dataTable value="#{someBean.someValue}" var="field">
<h:column>#{anotherBean[field]}</h:column>
</h:dataTable>
where field
is 'user.name'
or 'location.address.zip'
or...
Is it possible?
Note that this is a simple example, I'm interested in ValueExpression
not in dataTable
component.
UPDATE
now the question is: how to replace standard BeanELResolver?
looking in ELUtils:
...
composite.addRootELResolver(IMPLICIT_RESOLVER);
composite.add(FLASH_RESOLVER);
composite.addPropertyELResolver(COMPOSITE_COMPONENT_ATTRIBUTES_EL_RESOLVER);
addELResolvers(composite, associate.getELResolversFromFacesConfig());
addVariableResolvers(composite, FacesCompositeELResolver.ELResolverChainType.Faces,
associate);
addPropertyResolvers(composite, associate);
composite.add(associate.getApplicationELResolvers());
composite.addRootELResolver(MANAGED_BEAN_RESOLVER);
composite.addPropertyELResolver(RESOURCE_RESOLVER);
composite.addPropertyELResolver(BUNDLE_RESOLVER);
...
but i don't fully understand resolver chain yet... so i'll go studying :)
UPDATE 2
this code works ;)
public class ExtendedBeanELResolver extends BeanELResolver
{
@Override
public Object getValue(ELContext context, Object base, Object property) throws NullPointerException, PropertyNotFoundException, ELException
{
try
{
return super.getValue(context, base, property);
}
catch(PropertyNotFoundException e)
{
try
{
Object value = base;
for(String part : property.toString().split("\\."))
{
value = super.getValue(context, value, part);
}
return value;
}
catch(PropertyNotFoundException e1)
{
context.setPropertyResolved(false);
}
}
return null;
}
}
This is by default not supported. You need a custom ELResolver
here. Easiest is to extend the existing BeanELResolver
.
Here's a kickoff example:
public class ExtendedBeanELResolver extends BeanELResolver {
@Override
public Object getValue(ELContext context, Object base, Object property)
throws NullPointerException, PropertyNotFoundException, ELException
{
if (property == null || base == null || base instanceof ResourceBundle || base instanceof Map || base instanceof Collection) {
return null;
}
String propertyString = property.toString();
if (propertyString.contains(".")) {
Object value = base;
for (String propertyPart : propertyString.split("\\.")) {
value = super.getValue(context, value, propertyPart);
}
return value;
}
else {
return super.getValue(context, base, property);
}
}
}
To get it to run, register it as follows in faces-config.xml
:
<application>
<el-resolver>com.example.ExtendedBeanELResolver</el-resolver>
</application>
The work made on this topic is very interesting, but not complete.
In case you pass value in a Composite Component, which is set in this component, it doesn't work.
Example :
<composite:interface>
<composite:attribute name="value" type="java.lang.Boolean" required="true" />
</composite:interface>
<composite:implementation>
<h:inputText id="inputValue" value="#{cc.attrs.value}"
</h:inputText>
</composite:implementation>
With the ExtendedBeanElResolver, it throws a PropertyNotFoundException when I set value.
So, I spend few hours to find a solution, and here is the working solution, to be able to use the ExtendedBeanElResolver with the ability to set values inside composite components :
public class ExtendedBeanELResolver extends BeanELResolver {
private static final String PRIMEFACES_RESOURCE_PREFIX = "primefaces:";
private static final String RESOURCES_HANDLER = "class org.omnifaces.resourcehandler.GraphicResourceHandler";
private static final String PRIMEFACES_EXT_RESOURCES_HANDLER = "class org.primefaces.extensions.application.PrimeFacesExtensionsResourceHandler";
@Override
public Object getValue(ELContext context, Object base, Object property) {
if (property == null || base == null || base instanceof ResourceBundle || base instanceof Map || base instanceof Collection
|| property.toString().startsWith(PRIMEFACES_RESOURCE_PREFIX) || base.getClass().toString().equals(RESOURCES_HANDLER)
|| base.getClass().toString().equals(PRIMEFACES_EXT_RESOURCES_HANDLER)) {
return null;
}
String propertyString = property.toString();
if (propertyString.contains(".")) {
Object value = base;
for (String propertyPart : propertyString.split("\\.")) {
value = super.getValue(context, value, propertyPart);
}
return value;
} else {
Object v = super.getValue(context, base, property);
return v;
}
}
@Override
public void setValue(ELContext context, Object base, Object property, Object val) {
if (base != null && !(base instanceof ResourceBundle) && !(base instanceof Map) && !(base instanceof Collection))
super.setValue(context, base, property, val);
}
}
(I added the "setValue" part).
And now it works. Don't hesitate to give me your feedback, as this is my first post in this website !