Cómo implementar un ELResolver personalizado
El Expression Language de JSF nos permite acceder a propiedades de los objetos siempre que estas esten disponibles mediante getters.
Esto está muy bien, pero en ocasiones lo que queremos obtener es un objeto que solo está accesible a través de un método que no es un getter, como por ejemplo el tamaño de una java.util.Collection disponible a través de su método collection.size(), o los valores de un java.util.Map disponibles a través del método values().
Para ello podemos realizar nuestra propia implementación de la clase javax.el.ELResolver.
Esta clase es la encargada de evaluar las ELs (las expresiones que van entre #{}) y realizar las acciones correspondientes.
Sustiye a los antiguos PropertyResolver y VariableResolver, que están marcados como obsoletos (deprecated) en la especificación 1.2 de JSF (que es la usada en la versión 5 de Java EE).
Para usar nuestro propio ELResolver solo deboemos seguir tres sencillos pasos:
- 1) Implementar nuestra clase que extienda de javax.el.ELResolver
Puedes descargarla aquí: MyELResolver
Necesitarás añadir al classpath de compilación el archivo el-api.jar
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.el.ELContext;
public class MyELResolver extends javax.el.ELResolver {
@Override
public Class getCommonPropertyType(ELContext context, Object base) {
return null;
}
@Override
public Iterator getFeatureDescriptors(ELContext context, Object base) {
return null;
}
@Override
public Class getType(ELContext context, Object base, Object property) {
return null;
}
@Override
public Object getValue(ELContext context, Object base, Object property) {
if (base instanceof Collection) {
return resolveInCollection(context, (Collection)base, property);
} else if (base instanceof Map) {
return resolveInMap(context, (Map)base, property);
} else {
return null;
}
}
@Override
public boolean isReadOnly(ELContext arg0, Object arg1, Object arg2) {
return true;
}
@Override
public void setValue(ELContext arg0, Object arg1, Object arg2, Object arg3) {
}
private Object resolveInCollection(ELContext context, Collection base, Object property) {
if (property.equals("size")) {
context.setPropertyResolved(true);
return base.size();
} else if (property.equals("toMap")) {
context.setPropertyResolved(true);
return collectionToMap(base);
} else if (property.equals("toList")) {
context.setPropertyResolved(true);
return new ArrayList(base);
}else {
return null;
}
}
private Object resolveInMap(ELContext context, Map base, Object property) {
if (property.equals("size")) {
context.setPropertyResolved(true);
return base.size();
} else if (property.equals("values")) {
context.setPropertyResolved(true);
return base.values();
} else if (property.equals("keySet")) {
context.setPropertyResolved(true);
return base.keySet();
} else if (property.equals("entrySet")) {
context.setPropertyResolved(true);
return base.entrySet();
} else {
return null;
}
}
private Map<object> collectionToMap(Collection col) {
Map</object><object> map = new HashMap</object><object>();
for (Object obj : col) {
map.put(obj, obj);
}
return map;
}
}
- 2) Declarar el ELResolver en el archivo faces-config.xml
La declaración debe hacerse dentro de la seccion <application>.
<el-resolver>tes.jsf.MyELResolver</el-resolver>
- 3) Usar las nuevas posibilidades del Language Expression
- El tamaño de mi Collection es: #{myCollection.size}
- Puedes usarlo para rellenar tus combos:
<h:selectOneMenu> <f:selectItems value="#{myCollection.toMap}" /> <h:selectOneMenu> - Para asociar tu java.util.Set con un dataTable
<h:dataTable value="#{mySet.toList}" var="entry"> ... </h:dataTable> - Para iterar sobre las entradas de un Map
<ui:repeat value="#{myMap.entrySet.toList}" var="entry">
#{entry.key} : #{entry.value} <br />
</ui:repeat>





Joatham Said,
February 19, 2008 @ 8:06 am
Tengo una gran duda. Es posible inyectar un servicio a un conversor????
Juan Said,
April 1, 2008 @ 3:04 am
este post me ha impresionado, lo del size lo hacía de otra forma (taglib prefix='fn' uri='http://java.sun.com/jsp/jstl/functions' ) pero las posibilidades de extender ELResolver me parecen más potentes
gracias!
Cómo iterar sobre un Set en un dataTable | .:: Seam City ::. Said,
June 22, 2008 @ 7:39 am
[...] Una solución para poder usar nuestro Set con dataTables es convertirlo previamenta a un List. Para ello podemos usar un ELResolver. Ya expliqué cómo hacerlo en otro post [...]