2011-01-16 9 views
19

¿Hay alguna forma de comprobar declarativamente si una enumeración tiene un valor especificado? Por ejemplo:JSF 2: Uso de enumeraciones en el atributo representado

<h:graphicImage name="error.png" library="images" 
    rendered="#{viewController.current.status == Status.ERROR}" /> 

Es un poco tedioso para definir un método en el beand administrado que comprueba esto para cada valor de enumeración, por ejemplo

public boolean isStateIsError() { 
    return current.getStatus() == Status.ERROR; 
} 

¿Hay una manera más corta/mejor de hacer esto?

Respuesta

34

Until EL 3.0 no es posible importar enumeraciones en EL scope. Sin embargo, solo puede tratarlos y compararlos como cadenas, es decir, el valor constante enum debe citarse a continuación.

<h:graphicImage name="error.png" library="images" 
    rendered="#{viewController.current.status eq 'ERROR'}" /> 
+0

cadena es evaluada como llamar método equals() o como la comparación por referencia? – user3663882

7

Sé que esta pregunta es un poco más viejo ahora, pero que tenía el mismo problema y ha encontrado otra solución, que quiero compartir:

Crea una alfombrilla de EL-Resolver y enumeraciones de uso y Java constantes como objetos en el JSF:

<h:graphicImage name="error.png" library="images" 
     rendered="#{viewController.current.status == Status.ERROR}" /> 

Pero antes de poder utilizar las enumeraciones de esta manera usted tiene que hacer 3 pasos.

1. paso - Copia esta clase y reemplazar "MY_ENUM" a través de su enumClass (en el ejemplo anterior sería "Estado")

public class EnumCache { 
    private Map<String, Object> propertCache = new HashMap<String, Object>(); 
    private Map<String, Class> baseCache = new HashMap<String, Class>(); 
    private static EnumCache staticEnumCache = null; 

    public static EnumCache instance() { 
     if (staticEnumCache == null) { staticEnumCache = new EnumCache(); } 
     return staticEnumCache; 
    } 
    private EnumCache() { 
     List<Class<?>> classes = new ArrayList<Class<?>>(); 
     classes.add(MY_ENUM.class); 

     for(Class clazz : classes) { 
      try { 
       baseCache.put(clazz.getSimpleName(), clazz); 
       Method m = clazz.getMethod("values", (Class[]) null); 
       Enum<?>[] valueList = (Enum[]) m.invoke(null, (Object[]) null); 
       for (Enum<?> en : valueList) { 
        propertCache.put(clazz.getSimpleName() + "." + en.name(), en); 
       } 
      } catch (Exception e) { 
       System.err.println(clazz.getSimpleName(), e); 
      } 
     } 
    } 
    public Object getValueForKey(String key) { 
     return propertCache.get(key); 
    } 
    public Class getClassForKey(String key) { 
     return baseCache.get(key); 
    } 
} 

2. paso - añadir este EnumResolver - Este clase será el mapa de su expresión JSF para la enumeración en la memoria caché (paso 1)

public class MyEnumResolver extends ELResolver { 

    public Object getValue(ELContext context, Object base, Object property) { 
     Object result = null; 
     if (base == null) { 
      result = EnumCache.instance().getClassForKey(property + ""); 
     } else if (base instanceof Class) { 
      result = EnumCache.instance().getValueForKey(((Class) base).getSimpleName() + "." + property); 
     } 
     if (result != null) { 
      context.setPropertyResolved(true); 
     } 
     return result; 
    } 

    public Class<?> getCommonPropertyType(ELContext context, Object base) { 
     return null; 
    } 
    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { 
     return null; 
    } 
    public Class<?> getType(ELContext context, Object base, Object property) { 
     return null; 
    } 
    public boolean isReadOnly(ELContext context, Object base, Object property) { 
     return false; 
    } 
    public void setValue(ELContext context, Object base, Object property, Object arg3) { 
    } 
} 

3. paso - registrar el EnumResolver en faces-config.xml

<faces-config> 
    <application> 
     <el-resolver>com.asd.MyEnumResolver</el-resolver> 
    </application> 
</faces-config> 

NOTA: Si desea acceder a sus constantes java esta manera, sólo hay que extender el constructor de la clase enumCache. Este (untestet) ejemplo debería funcionar:

baseCache.put(CLASS_WITH_CONSTANTS.getSimpleName(), clazz); 
for (Field field : CLASS_WITH_CONSTANTS.getDeclaredFields()) { 
    try { 
     propertCache.put(CLASS_WITH_CONSTANTS.getSimpleName() + "." 
        + field.getName(), field.get(null)); 
    } catch (Exception e) { } 
} 

Esperanza este código reducido, pero de trabajo puede ayudar a nadie.


actualización

veo estos beneficios:

  1. Si utiliza cadenas en JSF (viewController.current.status == 'ERROR_abcdefg'), se puede escribir mal el valor y no lo reconocerá tan rápido. Con mi solución, se obtendría un error al cargar el archivo jsf, porque la enumeración no se pudo resolver.

  2. Puede ver en el código fuente que "ERROR" es el valor de la enumeración "ESTADO".

  3. Al comparar dos valores en el, se comparará también la clase de las enumeraciones. Entonces, por ejemplo, PersonState.ACTIV no es lo mismo que AccounState.ACTIV.

  4. Cuando tengo que cambiar mi valor enum de PersonState.ACTIV a PersonState.ACTIVATED puedo buscar el String "PersonState.ACTIV" en mi código fuente. buscar "ACTIV" tendría muchas más coincidencias.

+5

¿Qué beneficios te proporciona exactamente este enfoque? En Java, la principal ventaja de utilizar referencias enum directas fue la compilación del tiempo de cumplimiento de valores válidos, pero esta ventaja no existe en EL, tampoco con esta resolución. – BalusC

+0

¡Esto es aún más tedioso que crear un método en su bean administrado! Aún así, es bueno saber que esto se puede hacer. –

1

I resuelto un problema similar por statically vertido todas las claves de enumeración (que se utilizan en los componentes de interfaz de usuario prestados) en un mapa y que utilizo un método estático getByKey para convertir el valor de la interfaz de usuario en un real enum nativo en el organismo, lanzar una excepción si el valor proporcionado no es válido:

public enum ReportType { 

    FILING("F", "Filings"), 
    RESOLUTION("R", "Resolutions"), 
    BASIS("B", "Bases"), 
    STAFF("T", "Staff Counts"), 
    COUNTS("I", "Counts"); 

    private String key; 
    private String label; 

    private static Map<String, ReportType> keyMap = new HashMap<String, ReportType>(); 

    static { 
     for(ReportType type : ReportType.values()) { 
      keyMap.put(type.getKey(), type); 
     } 
    } 

    private ReportType(String _key, String _label) { 
     this.key = _key; 
     this.label = _label; 

    } 

    public String getKey() { 
     return this.key; 
    } 

    public String getLabel() { 
     return this.label; 
    } 

    public static List<ReportType> getValueList() { 
     return Arrays.asList(ReportType.values()); 
    } 

    public static ReportType getByKey(String _key) { 
     ReportType result = keyMap.get(_key); 

     if(result == null) { 
      throw new IllegalArgumentException("Invalid report type key: " + _key); 
     } 

     return result; 
    } 
} 

en el nivel de la interfaz de usuario, la clave enum se utiliza como el valor y la etiqueta enum se utiliza como la etiqueta:

<f:selectItems var="rptTypeItem" value="#{reportController.allReportTypes}" 
    itemLabel="#{rptTypeItem.label}" itemValue="#{rptTypeItem.key}"/> 

En el managed bean, que convierten la enumeración en una lista renderable, utilizando el getValueList() de la enumeración:

public List<ReportType> getAllReportTypes() { 
    return ReportType.getValueList(); 
} 

Por último, el [g | s] Etters en el aspecto bean gestionado de la siguiente manera:

public String getReportType() { 
    return this.crtRptType.getKey(); 
} 

public void setReportType(String _val) { 
    this.crtRptType = ReportType.getByKey(_val); 
} 
1

creo que se podría hacer que la siguiente manera:

Crear un método en el que haba que devolvería la lista de enumeraciones, por ejemplo

public Status[] getStatuses() { 
    Status.values(); 
} 

continuación, puede utilizar la enumeración de EL como esto

<h:graphicImage name="error.png" library="images" 
    rendered="#{viewController.current.status == someBean.statuses[0]}" /> 

suponiendo que el orden de los miembros de enumeración no va a ser cambiado (por ej. aquí estados [0] es ERROR). Sin embargo, me gustaría arreglar las posiciones como esta:

public Status[] getStatuses() { 
    Status myStatuses = new Status [2]; // or whatever number of statuses you are going to use in UI 
    myStatuses [0] = Status.ERROR; 
    myStatuses [1] = Status.RUNNING; 
    return myStatuses; 
} 

Esto todavía no es solución dinámica, pero es mejor que la codificación dura en EL. Puede ser especialmente útil cuando usa la localización para sus estados (valores enum dependiendo de la localización/traducción).

-1

que resolver un problema similar usando: comparación

<p:graphicImage name="images/close.png" rendered="#{i.unityEnum.name().equals('DAY')}" /> 
Cuestiones relacionadas