0

The data access layer behind my JSF app uses two different formats for date fields (sometimes ddmmyyyy, sometimes yyyymmdd); the displayed date is always dd/mm/yyyy.

Is there a way to use two different converters for a single field and decide which one to use dynamically? Like "if this command button is clicked, use this converter, else if this other command button is clicked, use that converter".

Virginie
  • 909
  • 3
  • 12
  • 32
  • You question needs further clarification. In case dates are **always** displayed in *one* format, then there is place for just *one converter*. Converter will produce `Date` object from that string. Next, in your managed bean you'll have date set and *later, in action method* you will decide in what format you will pass the data to your DAO, depending on what action method was called. In case my understanding of your setup doesn't correspond with yours, you could update the answer accordingly. – skuntsel May 06 '13 at 19:52

2 Answers2

4

BalusC explains why this is difficult here: https://stackoverflow.com/a/7123153/3284943

Based on his suggestions, I created this:

in the JSF file, I use my custom converter and its defined attributes, to select the converter and dynamic attributes at runtime. This is a snippet from a dynamic PrimeFaces DataTable:

<h:outputText rendered = "#{column.dateTime}" value="#{table[column.name]}">
    <f:converter converterId="runtimeConverterSelector" />
    <f:attribute name="converterId" value="#{column.converterName}" />
    <f:attribute name="pattern" value="#{column.converterPattern}" />
</h:outputText>

The custom converter:

@FacesConverter ("runtimeConverterSelector")
public class RuntimeConverterSelector implements Converter {
    Converter delegateConverter;
    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        retrieveAttributes(component);
        return delegateConverter.getAsObject(context, component, value);
    }
    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        retrieveAttributes(component);
        return delegateConverter.getAsString(context, component, value);
    }
    private void retrieveAttributes(UIComponent component) {
        Object attribute;
        attribute = component.getAttributes().get("converterId");
        if (attribute != null) {
            String converterName = (String)attribute;
            switch (converterName) {
                case "javax.faces.BigDecimal":
                    delegateConverter = new BigDecimalConverter();
                    break;
                case "javax.faces.BigInteger":
                    delegateConverter = new BigIntegerConverter();
                    break;
                case "javax.faces.Boolean":
                    delegateConverter = new BooleanConverter();
                    break;
                case "javax.faces.Byte":
                    delegateConverter = new ByteConverter();
                    break;
                case "javax.faces.Character":
                    delegateConverter = new CharacterConverter();
                    break;
                case "javax.faces.DateTimeConverter":
                    delegateConverter = new DateTimeConverter();
                    attribute = component.getAttributes().get("pattern");
                    if (attribute != null) ((DateTimeConverter)delegateConverter).setPattern((String)attribute);
                    attribute = component.getAttributes().get("timeZone");
                    if (attribute != null) ((DateTimeConverter)delegateConverter).setTimeZone(TimeZone.getTimeZone((String)attribute));
                    attribute = component.getAttributes().get("dateStyle");
                    if (attribute != null) ((DateTimeConverter)delegateConverter).setDateStyle((String)attribute);
                    attribute = component.getAttributes().get("timeStyle");
                    if (attribute != null) ((DateTimeConverter)delegateConverter).setDateStyle((String)attribute);
                    attribute = component.getAttributes().get("type");
                    if (attribute != null) ((DateTimeConverter)delegateConverter).setDateStyle((String)attribute);
                    break;
                case "javax.faces.Double":
                    delegateConverter = new DoubleConverter();
                    break;
                case "javax.faces.Enum":
                    delegateConverter = new EnumConverter();
                    break;
                case "javax.faces.Float":
                    delegateConverter = new FloatConverter();
                    break;
                case "javax.faces.Integer":
                    delegateConverter = new IntegerConverter();
                    break;
                case "javax.faces.Long":
                    delegateConverter = new LongConverter();
                    break;
                case "javax.faces.Number":
                    delegateConverter = new NumberConverter();
                    attribute = component.getAttributes().get("currencyCode");
                    if (attribute != null) ((NumberConverter)delegateConverter).setCurrencyCode((String)attribute);
                    attribute = component.getAttributes().get("currencySymbol");
                    if (attribute != null) ((NumberConverter)delegateConverter).setCurrencySymbol((String)attribute);
                    attribute = component.getAttributes().get("groupingUsed");
                    if (attribute != null) ((NumberConverter)delegateConverter).setGroupingUsed(Boolean.parseBoolean((String)attribute));
                    attribute = component.getAttributes().get("integerOnly");
                    if (attribute != null) ((NumberConverter)delegateConverter).setIntegerOnly(Boolean.parseBoolean((String)attribute));
                    attribute = component.getAttributes().get("locale");
                    if (attribute != null) ((NumberConverter)delegateConverter).setLocale(new Locale((String)attribute));
                    attribute = component.getAttributes().get("maxFractionDigits");
                    if (attribute != null) ((NumberConverter)delegateConverter).setMaxFractionDigits(Integer.parseInt((String)attribute));
                    attribute = component.getAttributes().get("maxIntegerDigits");
                    if (attribute != null) ((NumberConverter)delegateConverter).setMaxIntegerDigits(Integer.parseInt((String)attribute));
                    attribute = component.getAttributes().get("minFractionDigits");
                    if (attribute != null) ((NumberConverter)delegateConverter).setMinFractionDigits(Integer.parseInt((String)attribute));
                    attribute = component.getAttributes().get("minIntegerDigits");
                    if (attribute != null) ((NumberConverter)delegateConverter).setMinIntegerDigits(Integer.parseInt((String)attribute));
                    attribute = component.getAttributes().get("pattern");
                    if (attribute != null) ((NumberConverter)delegateConverter).setPattern((String)attribute);
                    attribute = component.getAttributes().get("type");
                    if (attribute != null) ((NumberConverter)delegateConverter).setType((String)attribute);
                    break;
                case "javax.faces.Short":
                    delegateConverter = new ShortConverter();
                    break;
                default:
                    System.err.println("ConverterId provided to runtimeConverterSelector, '" + converterName + "' is invalid. The default converter will be used");
                    break;
            }
        } else {
            System.err.println("No converterId was provided to runtimeConverterSelector.  The default converter will be used.");
        }
    }
}
Community
  • 1
  • 1
brs5tettba
  • 124
  • 9
0

AFAIK it's not possible. But you can check for both formats within one converter. Of course, conversion to string will be done basing to one format. Alternatively, you can format date basing on the current Locale. Basic example:

@FacesConverter("doubleDateConverter")
public class DateConverter implements Converter {

    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        if(value == null || value.equals("")) {
            return null;
        }
        SimpleDateFormat format1 = new SimpleDateFormat("ddMMyyyy");
        SimpleDateFormat format2 = new SimpleDateFormat("yyyyMMdd");
        Date date = null;
        boolean valid = true;
        try {
            date = format1.parse(value);
        } catch (ParseException e) {
            valid = false;
        }
        if(!valid) {
            try {
                date = format2.parse(value);
            } catch (ParseException e) {
                valid = false;
            }
        }
        if((!valid) || (date == null)) {
            throw new ConverterException(new FacesMessage("Date is in wrong format: " + value));
        }
        return date;
    }

    public String getAsString(FacesContext context, UIComponent component, Object value) {
        if (!(value instanceof Date) || (value == null)) {
            return null;
        }
        SimpleDateFormat format = new SimpleDateFormat("ddMMyyyy");
        return format.format((Date)value);
    }

}
skuntsel
  • 11,624
  • 11
  • 44
  • 67
  • I edited my question to be clearer: the displayed dates all have the same format (dd/mm/yyyy). With this approach, I can use a getAsString method and two regex to choose how to display my date, but the getAsObject method doesn't "know" which format is expected by my data layer. – Virginie May 06 '13 at 11:52
  • Please test the code before posting it as an answer. You forgot to take the [lenientness](http://stackoverflow.com/questions/13057464/validating-a-date-using-dateformat) of SDF into account as well as the fact that e.g. `12121212` is valid in both formats. It's better to make it really action-dependent as explicitly asked by OP. – BalusC May 06 '13 at 12:16