The idea of this recipe originates in the following JSF concept: a converter with NULL values is bypassed.
The problem occurs when we want to render a special message for a NULL property, instead of returning an empty String
or a NULL value. At first view, a custom converter should fix the problem in an elegant manner, but at second view we notice that the NULL values never get called in the converter, which means that we can't control it before the render phase. This recipe proposes a solution to this problem.
We have developed this recipe with NetBeans 6.8, JSF 2.0, and GlassFish v3. The JSF 2.0 classes were obtained from the NetBeans JSF 2.0 bundled library.
The idea is to have a placebo object—an object that it is not NULL and which is passed to the converter instead of every NULL object. The converter can identify this object by a fixed property, for example its hash code, and every time it gets this object, it will return a custom message to be rendered. For example, if our objects are instances of java.util.Date
, then we can write a placebo class like the following one:
//placebo class for java.util.Date class Placebo extends java.util.Date { @Override public int hashCode() { return 0011001100; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Placebo other = (Placebo) obj; return true; } }
Notice that we have arbitrarily chosen a fixed hash code as 0011001100. This hash code will mark the NULL values in the converter's getAsString
method. However before that, we need to modify the getter method for our property as shown next (this is the entire bean, but we are focused on the getCurrentdate
method):
package nullconv; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import java.util.Date; @ManagedBean @SessionScoped public class NullBean { //valid date //private Date currentdate = new Date(); //null date private Date currentdate = null; //placebo date private Date nulldate = new Placebo(); public Date getCurrentdate() { if (currentdate == null) { return nulldate; } return this.currentdate; } public void setCurrentdate(Date currentdate) { this.currentdate = currentdate; } }
Now, the converter gets a real date, when the currentdate
property is not NULL, and it gets the placebo nulldate
, when the currentdate
property is NULL. Now, we know that the converter gets all the values, including the NULL ones. Next, the converter (getAsString method) will check the hash code of the objects, to see which one is NULL and which one is not. The following is the source code for this converter:
package nullconv; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.ConverterException; import javax.faces.convert.DateTimeConverter; import javax.faces.convert.FacesConverter; @FacesConverter(value = "nullConverter") public class NullConverter extends DateTimeConverter { @Override public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) { if (arg0 == null) {throw new NullPointerException("context");} if (arg1 == null) {throw new NullPointerException("component");} if (arg2 != null && !(arg2 instanceof java.util.Date)) { throw new ConverterException("Not valid date"); } if (arg2.hashCode() == 0011001100) { return ("Not available!"); } return super.getAsString(arg0, arg1, arg2); } @Override public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) { if (arg0 == null) {throw new NullPointerException("context");} if (arg1 == null) {throw new NullPointerException("component");} return super.getAsObject(arg0, arg1, arg2); } }
Now, the NULL values will be rendered with a "Not available!"
message!
Every time a NULL date is loaded into the bean it is replaced by the placebo date. This date has the particularity of having a well known hash code. When the placebo object gets into the converter, the getAsString
method checks for this hash code. When it finds a match it returns a custom message instead of the String
representation of the date, because it knows that the received value is actually a NULL one, which should not be rendered verbatim.