Seleccionar página

El Expression Language de JSF nos permite acceder a propiedades de los objetos siempre que estas estén 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.
Sustituye 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

package tes.jsf;

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 collectionToMap(Collection col) {
Map map = new HashMap();
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>
    • Y para todo lo que se te ocurra añadir

Pin It on Pinterest