Tuesday, February 5, 2013

Custom Converters

To make custom converter I found 4 ways of doing it:
  1. using Map in Bean object
  2. parsing Object to String
  3. using static Map
  4. defining inner class in backing bean

Using Map in Bean

In Bean class you need to have Map<key, Object>.
In converters getAsObject method you read object value from this map.

public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException
{
if (context == null) throw new NullPointerException("context");
if (component == null) throw new NullPointerException("component");
if (value == null || value.isEmpty()) return null;
// value je oid recorda

FacesContext ctx = FacesContext.getCurrentInstance();
ValueExpression vex = ctx.getApplication().getExpressionFactory().createValueExpression(ctx.getELContext(), "#{billViewBean}", BillViewBean.class);
BillViewBean view = (BillViewBean) vex.getValue(ctx.getELContext());

TowRecord record;
record = view.getRecordsMap().get(value);
if (record == null)
{

FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unknown value", "The record is unknown!");
throw new ConverterException(message);
}

return record;
}


public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException
{
if (context == null) throw new NullPointerException("context");
if (component == null) throw new NullPointerException("component");
if (value == null) return null;
if (!(value instanceof TowRecord))
{
log.error("Error converting TowRecord to String: Object is not an instance of TowRecord.");
throw new ConverterException("Object is not an instance of TowRecord.");
}

TowRecord record = (TowRecord) value;
return record.getOid();
}
    Ref: JSF 2.0 Cookbook: Using custom converters for h:selectOneMenu


Parsing Object to String

If using this approach you could be in problems with JPA.
Also if object if composed with many minor objects it can be heavy to parse to and from object.

public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException
{
if (value == null || value.isEmpty()) return null;

String[] attributes = StringUtils.delimitedListToStringArray(value, DELIMITER);

TowRecord record = new TowRecord(attributes[0], attributes[1], attributes[2]);
log.debug("Converter to object: " + record.getOid() + ", " + record.getNumber() + ", " + record.getRegistration().getLicensePlate());
return record;
}

public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException
{
if (value == null)
return null;
else if (!(value instanceof TowRecord))
{
log.error("Error converting TowRecord to String: Object is not an instance of TowRecord.");
throw new ConverterException("Object is not an instance of TowRecord.");
}
else
{
TowRecord record = (TowRecord) value;
String recordString = record.getOid() + DELIMITER + record.getNumber() + DELIMITER + record.getRegistration().getLicensePlate();

log.debug("Converted record to String " + recordString);
return recordString;
}
}

   Ref: Core JavaServer Faces  3rd Edition: Implementing Custom Converter Classes


Using static Map

You create class that will hold static maps, and from converter class you can easily access it.
Drawback is that you need to be careful how many object you can put in map.. memory issue.

public class MapHolder

{
private static Map<String, AbstractDOM> recordsMap = Collections.synchronizedMap(new HashMap<String, AbstractDOM>());

public static Map<String, AbstractDOM> getRecordsMap()
{
return recordsMap;
}
}

public class TowRecordsSTATICConverter implements Converter :
public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException
{
if (value == null || value.isEmpty()) return null;

return MapHolder.getRecordsMap().get(value);
}

public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException
{
if (value == null)
return null;
else if (!(value instanceof TowRecord))
{
log.error("Error converting TowRecord to String: Object is not an instance of TowRecord.");
throw new ConverterException("Object is not an instance of TowRecord.");
}
else
{
TowRecord record = (TowRecord) value;
return record.getOid();
}
}


Inner class in backing bean

using this approach you have access to Collection defined and populated in backing bean.

In this example you define:
private List<ReasonDPT> reasonList = new ArrayList<ReasonDPT>();
In xhtml you register converter as converter="#{backingBean.reasonDPTConverter}"

private class ReasonDPTConverter implements Converter
{

@Override
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
for (ReasonDPT o : reasonList)
{
if (o.getOid().equals(value)) return o;
}
return null;
}

@Override
public String getAsString(FacesContext context, UIComponent component, Object value)
{
if (value == null) { return ""; }
return ((ReasonDPT) value).getOid();
}
}