Sorting tree nodes in Primefaces - jsf

I am working with JSF 2.1 and Primefaces 3.3. I am using the primefaces tree component for crating the tree from Database. I wanted to sort the tree nodes in alphabetical order at all levels. Please help me on this.

we had problems with the sorting via Comparator and found out, that there is a handy PrimeFaces TreeUtils.sortNode( TreeNode, Comparator ) class already provided which works like a charm :)

You would have to sort Primefaces DefaultTreeNode objects at the ManagedBean using Collections.sort and a Comparator class.
public TreeNodeComparator() implements Comparator<TreeNode> {
public int compare(TreeNode n1, TreeNode n2) {
// This assumes the tree node data is a string
return n1.getData().compareTo(n2.getData());
}
}
In your managed bean you will need to assemble your child lists without adding their parents yet. That can come later. For right now build your child lists out for each level and set the parent to null;
TreeNode node1 = new DefaultTreeNode("node1", null);
TreeNode node2 = new DefaultTreeNode("node2", null);
TreeNode child1node1 = new DefaultTreeNode("zgnagn", null);
TreeNode child2node1 = new DefaultTreeNode("vvnieeianag", null);
TreeNode child1node2 = new DefaultTreeNode("cajkgnagair", null);
TreeNode child2node2 = new DefaultTreeNode("ajaavnagwokd", null);
rootNodeChildren.add(node1);
rootNodeChildren.add(node2);
node1Children.add(child1node1);
node1Children.add(child2node1);
node2Children.add(child1node2);
node2Children.add(child2node2);
The reason why we are setting everything to null is because when the parent is set on the DefaultTreeNode it is added to the parents child list. The order in which you set a nodes parents determines the order they will appear in the Tree component.
Knowing that we can use our comparator to sort each list individually.
Collections.sort(rootNodeChildren, new TreeNodeComparator());
Collections.sort(node1Children, new TreeNodeComparator());
Collections.sort(node2Children, new TreeNodeComparator());
Now all of the lists are sorted so we can loop through and the appropriate parents one at a time. You can probably write an algorithm to determine this or you can keep a separate data stucture that builds the tree hierarchy without adding to the list.
Another way, and probably easier overall, is to just override the DefaultTreeNode class and give it a sort method:
public SortableDefaultTreeNode extends DefaultTreeNode {
public void sort() {
TreeNodeComparator comparator = new TreeNodeComparator();
Collections.sort(this.children, comparator);
for (TreeNode child : children) {
child.sort();
}
}
}
Now you can just build your TreeNodes out and then call root.sort() and it will recursively sort all of its children at each level alphabetically.

You could also use a generic comparable TreeNode aproach such as:
Base was taken from primefaces DefaultTreeNode, unmodified changes are left out in below code.
If child's should not be restricted on T one can use TreeNodeComparable<T extends Comparable<?>> and cast to Comparable in compareTo() method.
public class TreeNodeComparable<T extends Comparable<T>> implements TreeNode, Serializable,
Comparable<TreeNodeComparable<T>>
{
private static final long serialVersionUID = ...;
private T data;
private List<TreeNodeComparable<T>> children;
public TreeNodeComparable(final String type, final T data, final TreeNodeComparable<T> parent)
{
this.type = type;
this.data = data;
this.children = (List) new TreeNodeChildren(this);
if (parent != null)
parent.getChildren().add(this);
}
/**
* Comparison only depends on the underlying data
*
* #see ObjectUtils#compare(Comparable, Comparable)
*/
#Override
public int compareTo(final TreeNodeComparable<T> node)
{
if (node == null)
throw new NullPointerException("node");
return ObjectUtils.compare((T) this.getData(), (T) node.getData());
}
/**
* Recursively sorts the complete tree.
*/
public void sort()
{
Collections.sort(this.children);
for (final TreeNodeComparable<T> child : this.children)
{
child.sort();
// must reset parent due to PF problems
// http://forum.primefaces.org/posting.php?mode=reply&f=3&t=39752
child.setParent(this);
}
}
#SuppressWarnings("unchecked")
#Override
public boolean equals(final Object obj)
{
if (this == obj)
return true;
if (obj == null || this.getClass() != obj.getClass())
return false;
final TreeNodeComparable<T> other = (TreeNodeComparable<T>) obj;
return ObjectUtils.equals(this.data, other.data);
}
#Override
public int hashCode()
{
return new HashCodeBuilder().append(this.data).toHashCode();
}
public void setData(final Object data)
{
if (data != null && !(data instanceof Comparable))
throw new IllegalArgumentException();
this.data = (T) data;
}
#SuppressWarnings(
{
"unchecked", "rawtypes"
})
public List<TreeNode> getChildren()
{
return (List) this.children;
}
}

Related

How to convert the cassandra row to my java class?

Here is [a Link](http://stackoverflow.com/questions/32448987/how-to-retrieve-a-very-big-cassandra-table-and-delete-some-unuse-data-from-it#comment52844466_32464409) of my question before.
After I get the cassandra data row by row in my program, I'm confused by the convert between cassandra row to java class. In java class the table of cassandra is convert to a ResultSet class,when I iterator it and get the row data,it returns a NPE. In fact,I can see the Object (or the data) while debuging the program. Here is My Iterator Code:
ResultSet rs=CassandraTools.getInstance().execute(cql);
Iterator<Row> iterator = rs.iterator();
while (iterator.hasNext()) {
Row row = iterator.next();
row.getString() ---->return NPE
The CassandraTools class is:
public class CassandraTools {
private static CassandraTools instance;
private CassandraTools() {
}
public static synchronized CassandraTools getInstance() {
if (instance == null) {
instance = new CassandraTools();
instance.init();
}
return instance;
}
Cluster cluster;
Session session;
public void init() {
if (cluster == null) {
cluster = new Cluster.Builder().addContactPoint("10.16.34.96")
.build();
if (session == null) {
session = cluster.connect("uc_passport");
}
}
}
public ResultSet execute(String cql) {
ResultSet rs = session.execute(cql);
// rs.forEach(n -> {
// System.out.println(n);
// });
return rs;
}
}
SO how could I convert the data in the row to A java Class?I have read the convert class in the API of spring data cassandra,but it is complicated to use for me. Who can help?
IMHO, If you want to map the rows of Cassandra to a java class, you should try to use an Object-Datastore mapper which does these things for you.
If you try to do this by yourself, you need to handle the java-cassandra datatype mappings, validations etc all by yourself which is very hectic job.
There are few (Kundera, Hibernate OGM, etc) opensource object-datastore mappers available and you can use them. I suggest you to try Kundera and check this for getting started with Cassandra.

JSF SelectOneMenu HashMap Converter has wrong instanceOf

In my Entity class I have a HashMap. Now I'm trying to create a Select of this Map to be able to select on of the objects. So I created following classes:
HorseConverter:
#Named
public class HorseConverter implements Converter{
#EJB
private HorseBean bean;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return bean.getHorse(Long.valueOf(value));
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if(!(value instanceof Horse)){
throw new ConverterException(new FacesMessage("Object is not a Horse"));
} else {
Horse h = (Horse) value;
return Long.toString(h.getId());
}
}
}
Race Entity:
public Map<Horse, Integer> getHorses() {
return horses;
}
public void setHorses(HashMap<Horse, Integer> horses) {
this.horses = horses;
}
And my view:
Horse:
<h:selectOneMenu value="#{betController.horse}" converter="#{horseConverter}">
<f:selectItems value="#{raceController.selectedRace.horses}" var="h" itemLabel="#{h.nickName}" itemValue="#{h}"/>
</h:selectOneMenu>
Seems like the value I'm getting isn't an instance of Horse. I checked the following link:
https://stackoverflow.com/tags/selectonemenu/info So it seems that the key is automaticly used as value. But even writing h.key doesn't make a difference.
EDIT:
Here is my hash and equals code from the Horse Entity:
#Override
public int hashCode() {
int hash = 7;
hash = 97 * hash + (int) (this.id ^ (this.id >>> 32));
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Horse other = (Horse) obj;
if (this.id != other.id) {
return false;
}
return true;
}
You can't use var on a Map value. This specific <f:selectItems> construct works only if you use List<Horse> instead of Map<Horse, Integer>.
public List<Horse> getHorses() {
return horses;
}
If you really want to use a Map, then you should be returning a Map<String, Horse>, where String is the nickname of the Horse.
public Map<String, Horse> getHorses() {
return horses;
}
In case of using a Map value, don't forget to remove the var:
<f:selectItems value="#{raceController.selectedRace.horses}" />
The map's key becomes the option label and the map's value becomes the option value.
Unrelated to the concrete problem, a HashMap is by nature unordered. If you want to show the dropdown items in insertion order, rather use LinkedHashMap.
Have you overriden hashcode() and equals() in your Horse() object?
Your Converter needs equals() overriden in order to work. If you don't do this, only two references to the same instance of Horse() will be equal, rather than two seperate instances that have exactly the same state. Collections create an implicit copy to compare, so you won't have a single instance on the heap in this case.
Don't forget that the argument in the equals() object is Object(), NOT Horse().
If you don't override hashcode(), the hashcode will be different for every instance of Horse. This means that you will struggle to find the right Horse for comparison, even if your Horses are logically equivalent, because again, you'll have more than one instance that you will be comparing in order to find your Horse in your HashMap.
For further information, see this chapter of Effective Java by Joshua Bloch.

JAX-RS: How to automatically serialize a collection when returning a Response object?

I have a JAXB-annotated employee class:
#XmlRootElement(name = "employee")
public class Employee {
private Integer id;
private String name;
...
#XmlElement(name = "id")
public int getId() {
return this.id;
}
... // setters and getters for name, equals, hashCode, toString
}
And a JAX-RS resource object (I'm using Jersey 1.12)
#GET
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/")
public List<Employee> findEmployees(
#QueryParam("name") String name,
#QueryParam("page") String pageNumber,
#QueryParam("pageSize") String pageSize) {
...
List<Employee> employees = employeeService.findEmployees(...);
return employees;
}
This endpoint works fine. I get
<employees>
<employee>
<id>2</id>
<name>Ana</name>
</employee>
</employees>
However, if I change the method to return a Response object, and put the employee list in the response body, like this:
#GET
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/")
public Response findEmployees(
#QueryParam("name") String name,
#QueryParam("page") String pageNumber,
#QueryParam("pageSize") String pageSize) {
...
List<Employee> employees = employeeService.findEmployees(...);
return Response.ok().entity(employees).build();
}
the endpoint results in an HTTP 500 due to the following exception:
javax.ws.rs.WebApplicationException: com.sun.jersey.api.MessageException: A message body writer for Java class java.util.ArrayList, and Java type class java.util.ArrayList, and MIME media type application/xml was not found
In the first case, JAX-RS has obviously arranged for the proper message writer to kick in when returning a collection. It seems somewhat inconsistent that this doesn't happen when the collection is placed in the entity body. What approach can I take to get the automatic JAXB serialization of the list to happen when returning a response?
I know that I can
Just return the list from the resource method
Create a separate EmployeeList class
but was wondering whether there is a nice way to use the Response object and get the list to serialize without creating my own wrapper class.
You can wrap the List<Employee> in an instance of GenericEntity to preserve the type information:
http://docs.oracle.com/javaee/6/api/javax/ws/rs/core/GenericEntity.html
You can use GenericEntity to send the collection in the Response. You must have included appropriate marshal/unmarshal library like moxy or jaxrs-jackson.
Below is the code :
#GET
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/")
public Response findEmployees(
#QueryParam("name") String name,
#QueryParam("page") String pageNumber,
#QueryParam("pageSize") String pageSize) {
...
List<Employee> employees = employeeService.findEmployees(...);
GenericEntity<List<Employee>> entity = new GenericEntity<List<Employee>>(Lists.newArrayList(employees))
return Response.ok().entity(entity).build();
}
I resolved this issue by extending the default JacksonJsonProvider class, in particular method writeTo.
Analyzing the source code of this class I found the block where the actual type is instantiated by reflection, so I've modified the source code as below:
public void writeTo(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String,Object> httpHeaders, OutputStream entityStream) throws IOException {
/* 27-Feb-2009, tatu: Where can we find desired encoding? Within
* HTTP headers?
*/
ObjectMapper mapper = locateMapper(type, mediaType);
JsonEncoding enc = findEncoding(mediaType, httpHeaders);
JsonGenerator jg = mapper.getJsonFactory().createJsonGenerator(entityStream, enc);
jg.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
// Want indentation?
if (mapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)) {
jg.useDefaultPrettyPrinter();
}
// 04-Mar-2010, tatu: How about type we were given? (if any)
JavaType rootType = null;
if (genericType != null && value != null) {
/* 10-Jan-2011, tatu: as per [JACKSON-456], it's not safe to just force root
* type since it prevents polymorphic type serialization. Since we really
* just need this for generics, let's only use generic type if it's truly
* generic.
*/
if (genericType.getClass() != Class.class) { // generic types are other impls of 'java.lang.reflect.Type'
/* This is still not exactly right; should root type be further
* specialized with 'value.getClass()'? Let's see how well this works before
* trying to come up with more complete solution.
*/
//**where the magic happens**
//if the type to instantiate implements collection interface (List, Set and so on...)
//Java applies Type erasure from Generic: e.g. List<BaseRealEstate> is seen as List<?> and so List<Object>, so Jackson cannot determine #JsonTypeInfo correctly
//so, in this case we must determine at runtime the right object type to set
if(Collection.class.isAssignableFrom(type))
{
Collection<?> converted = (Collection<?>) type.cast(value);
Class<?> elementClass = Object.class;
if(converted.size() > 0)
elementClass = converted.iterator().next().getClass();
//Tell the mapper to create a collection of type passed as parameter (List, Set and so on..), containing objects determined at runtime with the previous instruction
rootType = mapper.getTypeFactory().constructCollectionType((Class<? extends Collection<?>>)type, elementClass);
}
else
rootType = mapper.getTypeFactory().constructType(genericType);
/* 26-Feb-2011, tatu: To help with [JACKSON-518], we better recognize cases where
* type degenerates back into "Object.class" (as is the case with plain TypeVariable,
* for example), and not use that.
*/
if (rootType.getRawClass() == Object.class) {
rootType = null;
}
}
}
// [JACKSON-578]: Allow use of #JsonView in resource methods.
Class<?> viewToUse = null;
if (annotations != null && annotations.length > 0) {
viewToUse = _findView(mapper, annotations);
}
if (viewToUse != null) {
// TODO: change to use 'writerWithType' for 2.0 (1.9 could use, but let's defer)
ObjectWriter viewWriter = mapper.viewWriter(viewToUse);
// [JACKSON-245] Allow automatic JSONP wrapping
if (_jsonpFunctionName != null) {
viewWriter.writeValue(jg, new JSONPObject(this._jsonpFunctionName, value, rootType));
} else if (rootType != null) {
// TODO: change to use 'writerWithType' for 2.0 (1.9 could use, but let's defer)
mapper.typedWriter(rootType).withView(viewToUse).writeValue(jg, value);
} else {
viewWriter.writeValue(jg, value);
}
} else {
// [JACKSON-245] Allow automatic JSONP wrapping
if (_jsonpFunctionName != null) {
mapper.writeValue(jg, new JSONPObject(this._jsonpFunctionName, value, rootType));
} else if (rootType != null) {
// TODO: change to use 'writerWithType' for 2.0 (1.9 could use, but let's defer)
mapper.typedWriter(rootType).writeValue(jg, value);
} else {
mapper.writeValue(jg, value);
}
}
}

Model-Identifier for Node in JavaFX 2 TreeItem

Is there a way to store an identifier of a model object or the model object itself in a JavaFX 2 TreeItem<String>? There is just Value to store the text...
I'm populating a TreeView from a list of model objects, and need to find it when the user clicks a node. I'm used to work with Value and Text in .NET Windows Forms or HTML and I am afraid I cannot adapt this way of thinking to JavaFX...
You can use any objects with TreeView, they just have to override toString() for presenting or extend javafx.scene.Node
E.g. for next class:
private static class MyObject {
private final String value;
public MyObject(String st) { value = st; }
public String toString() { return "MyObject{" + "value=" + value + '}'; }
}
TreeView should be created next way:
TreeView<MyObject> treeView = new TreeView<MyObject>();
TreeItem<MyObject> treeRoot = new TreeItem<MyObject>(new MyObject("Root node"));
treeView.setRoot(treeRoot);
I have the same issue as the OP. In addition I want to bind the value displayed in the TreeItem to a property of the object. This isn't complete, but I'm experimenting with the following helper class, where I'm passing in the "user object" (or item) to be referenced in the TreeItem, and a valueProperty (which, in my case, is a property of the item) to be bound to the TreeItem.value.
final class BoundTreeItem<B, T> extends TreeItem<T> {
public BoundTreeItem(B item, Property<T> valueProperty) {
this(item, valueProperty, null);
}
public BoundTreeItem(B item, Property<T> valueProperty, Node graphic) {
super(null, graphic);
itemProperty.set(item);
this.valueProperty().bindBidirectional(valueProperty);
}
public ObjectProperty<B> itemProperty() {
return itemProperty;
}
public B getItem() {
return itemProperty.get();
}
private ObjectProperty<B> itemProperty = new SimpleObjectProperty<>();
}

JSF 2: Using enums in the rendered attribute

Is there any way to check declaratively whether an enum has a specified value. For example:
<h:graphicImage name="error.png" library="images"
rendered="#{viewController.current.status == Status.ERROR}" />
It's a little bit tedious to define a method in the managed beand that checks this for every enum value, e.g.
public boolean isStateIsError() {
return current.getStatus() == Status.ERROR;
}
Is there a shorter/better way of doing this?
Until EL 3.0 it's not possible to import enums in EL scope. You can however just treat and compare them like strings, i.e. the enum constant value must be quoted like below.
<h:graphicImage name="error.png" library="images"
rendered="#{viewController.current.status eq 'ERROR'}" />
I know this question is a bit older now, but i had the same problem and found another solution, which i want to share :
Create a Custom EL-Resolver and use enums and java constants as objects in jsf el:
<h:graphicImage name="error.png" library="images"
rendered="#{viewController.current.status == Status.ERROR}" />
But before you can use enums this way you have to do 3 steps.
1. step - Copy this Class and replace "MY_ENUM" through your enumClass (in the example above it would be "Status")
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. step - add this EnumResolver - This class will map your JSF expression to the enum in cache (step 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. step - register the EnumResolver in faces-config.xml
<faces-config>
<application>
<el-resolver>com.asd.MyEnumResolver</el-resolver>
</application>
</faces-config>
NOTE:
If you want to access your java constants this way, you just have to extend the constructor of the enumCache class.
This (untestet) example should work:
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) { }
}
Hope this reduced but working code can help anybody.
Update
I see this benefits:
If you use strings in jsf (viewController.current.status == 'ERROR_abcdefg'), you can misspell the value and wont recognise it so fast.
With my solution you would get an error while loading the jsf file, because the enum could not be resolved.
You can see in the sourcecode that "ERROR" is value of the enum "STATUS".
When you compare two values in el, the class of the enums will be compared too.
So for example PersonState.ACTIV is not the same like AccounState.ACTIV.
When i have to change my enum value from PersonState.ACTIV to PersonState.ACTIVATED i can search for the String "PersonState.ACTIV" in my sourcecode. searching for "ACTIV" would have much more matches.
I solved a similar problem by statically dumping all the enum keys (which are used in the rendered UI components) in a map and then I use a static getByKey method to convert the value from the UI into an actual native enum in the setter, throwing an Exception if the value provided is invalid:
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;
}
}
In the UI tier, the enum key is used as the value and the enum label is used as the label:
<f:selectItems var="rptTypeItem" value="#{reportController.allReportTypes}"
itemLabel="#{rptTypeItem.label}" itemValue="#{rptTypeItem.key}"/>
In the managed bean, I convert the enum into a renderable list, using the getValueList() from the enum:
public List<ReportType> getAllReportTypes() {
return ReportType.getValueList();
}
Finally, the [g|s]etters in the managed bean look as follows:
public String getReportType() {
return this.crtRptType.getKey();
}
public void setReportType(String _val) {
this.crtRptType = ReportType.getByKey(_val);
}
I think it could be done it the following way:
Create a method in you bean that would return the list of enums, for example
public Status[] getStatuses() {
Status.values();
}
then you can use the enum in EL like this
<h:graphicImage name="error.png" library="images"
rendered="#{viewController.current.status == someBean.statuses[0]}" />
assuming that the order of enum members is not going to be changed (for ex. here statuses[0] is ERROR). However, I would fix the positions like this:
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;
}
This is still not dynamic solution, but it's better than hard-coding in EL. Might be especially useful when you'r using localization for you statuses (enum values depending on locale/translation).

Resources