JSF 2:在呈现的属性中使用枚举

时间:2011-01-16 17:02:26

标签: jsf enums jsf-2 el

有没有办法以声明方式检查枚举是否具有指定值。例如:

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

在托管beand中定义一个方法,检查每个枚举值是否有点乏味,例如

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

有更短/更好的方法吗?

5 个答案:

答案 0 :(得分:35)

Until EL 3.0无法在EL范围内导入枚举。然而,您可以像对待字符串一样对待和比较它们,即必须引用枚举常量值,如下所示。

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

答案 1 :(得分:7)

我知道这个问题现在有点老了,但我遇到了同样的问题并找到了另一个解决方案,我想分享一下:

创建自定义EL解析器,使用枚举和java常量作为jsf中的对象 el:

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

但在你以这种方式使用枚举之前,你必须做3个步骤。

<强> 1。步骤 - 复制此类并通过enumClass替换“MY_ENUM”(在上面的示例中,它将是“状态”)

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。步骤 - 添加此EnumResolver - 此类将您的JSF表达式映射到缓存中的枚举(步骤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。步骤 - 在faces-config.xml中注册EnumResolver

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

注意: 如果要以这种方式访问​​java常量,则只需扩展enumCache类的构造函数即可。 这个(untestet)示例应该有效:

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) { }
}

希望这种情况有所减少,但正常工作的代码可以帮助任何人。


<强>更新

我看到了这个好处:

  1. 如果你在jsf中使用字符串(viewController.current.status =='ERROR_abcdefg'),你可以拼错该值并且不会如此快地识别它。 使用我的解决方案,您在加载jsf文件时会收到错误,因为枚举无法解析。

  2. 您可以在源代码中看到“ERROR”是枚举“STATUS”的值。

  3. 比较el中的两个值时,也会比较枚举的类别。 例如,PersonState.ACTIV与AccounState.ACTIV不同。

  4. 当我必须将我的枚举值从PersonState.ACTIV更改为PersonState.ACTIVATED时,我可以在源代码中搜索字符串“PersonState.ACTIV”。搜索“ACTIV”会有更多的匹配。

答案 2 :(得分:1)

我通过statically在地图中转储所有枚举键(在渲染的UI组件中使用)解决了类似的问题,然后我使用静态getByKey方法转换来自UI到setter中的实际本机枚举,如果提供的值无效,则抛出异常:

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;
    }
}

在UI层中,枚举键用作值,枚举标签用作标签:

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

managed bean中,我使用枚举中的getValueList()将枚举转换为可渲染列表:

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

最后,托管bean中的[g | s] etters如下所示:

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

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

答案 3 :(得分:1)

我认为可以通过以下方式完成:

在bean中创建一个返回枚举列表的方法,例如

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

那么你可以像这样使用EL中的枚举

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

假设枚举成员的顺序不会被更改(例如,此处状态[0]为ERROR)。但是,我会解决这样的立场:

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;
}

这仍然不是动态解决方案,但它比EL中的硬编码更好。当您使用本地化为您的状态(枚举值取决于区域设置/翻译)时,可能会特别有用。

答案 4 :(得分:-1)

我使用以下方法解决了类似问题:

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