Custom converter in JSF 2 with arguments

21,216

Solution 1

You may take a look at JSF2.0 sources. For example DateTimeConverter... JSF sources available here in svn reposotory: https://svn.java.net/svn/mojarra~svn/trunk

IMO creating such converter is not easy. It also required to create converter tag to register converter.

Other way to pass some data to converter is attibutes. So You can write

<h:outputText ...>
  <f:converter converterId="bla.blablabla.Truncate" />
  <f:attribute name="truncateIndex" value="150"/>
</h:outputText>

Than call to component.getAttributes().get("truncateIndex"); in converter code.

Solution 2

Based on @Maks solution: It's possible to combine the converter and the attribute in one tag:

<facelet-taglib version="2.2"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facelettaglibrary_2_2.xsd">
    <namespace>http://mycompany.com/some-identifier</namespace>
    <tag>
        <tag-name>truncate</tag-name>
        <converter>
            <converter-id>bla.blablabla.Truncate</converter-id>
        </converter>
        <attribute>
            <name>truncateIndex</name>
        </attribute>
    </tag>
</facelet-taglib>

Then you can use the converter like this:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:my="http://mycompany.com/some-identifier">

  <my:truncate truncateIndex="150" />

</ui:composition>

You also don't need to grab the parameter from the component-attributes. A bean-property with the same name will be populated automatically:

@FacesConverter("bla.blablabla.Truncate")
public class Truncate implements Converter {

    private String truncateIndex;

    // getters + setters

    ...

}

Solution 3

http://jerryorr.blogspot.nl/2011/10/creating-jsf-12-custom-converter-with.html is a good guide to set up your first custom converter with parameters

Solution 4

This is how I did it (in a separated jar file, and using maven's default directories):

1) Create the converter's class (inside src/main/java)

2) Create a .taglib.xml class (inside src/main/resources/META-INF)

3) Create a faces-config.xml (inside src/main/resources/META-INF)

Example:

Step 1)

package com.ocabit.jsf.converter;



import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;

import org.springframework.stereotype.Service;

import com.ocabit.utils.currency.CurrencyUtils;


/**
 * Converter para valores BigDecimal.
 * 
 * @author Carlos Eduardo Pauluk
 * 
 */
@FacesConverter("bigDecimalConverter")
@Service("bigDecimalConverter")
public class BigDecimalConverter implements Converter, Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public static final String CONVERTER_ID = "com.ocabit.jsf.converter.BigDecimalConverter";

    /**
     * Em caso de null, força a saída para 0.0;
     */
    private boolean nullToZero = false;

    /**
     * Em caso de zero, força a saída para null;.
     */
    private boolean zeroToNull = false;

    /**
     * Só retorna números positivos.
     */
    private boolean onlyAbs = false;

    /**
     * Só retorna números negativos.
     */
    private boolean onlyNeg = false;

    /**
     * Quantidade de casas decimais.
     */
    private int decimals = 2;

    @Override
    public Object getAsObject(final FacesContext context, final UIComponent component, final String value) {
        try {
            BigDecimal bd = new BigDecimal(value);
            bd = bd.setScale(getDecimals(), RoundingMode.HALF_DOWN);

            if (bd.equals(BigDecimal.ZERO) && isZeroToNull()) {
                return null;
            }

            if (isOnlyAbs()) {
                bd = bd.abs();
            }
            if (isOnlyNeg()) {
                bd = bd.abs();
                bd = bd.negate();
            }

            return bd;
        } catch (final Exception e) {
            BigDecimal bd = null;
            if (isNullToZero()) {
                bd = CurrencyUtils.getBigDecimalCurrency("0.0");
                bd = bd.setScale(getDecimals(), RoundingMode.HALF_DOWN);
            }
            return bd;
        }
    }

    @Override
    public String getAsString(final FacesContext context, final UIComponent component, final Object value) {
        if (!(value instanceof BigDecimal)) {
            throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erro ao converter o valor decimal.", ""));
        }

        final NumberFormat nf = NumberFormat.getInstance();
        // sempre terá 2 casas decimais
        nf.setMinimumFractionDigits(2);
        nf.setMaximumFractionDigits(2);
        return nf.format(((BigDecimal) value).doubleValue());
    }

    public boolean isNullToZero() {
        return nullToZero;
    }

    public void setNullToZero(boolean nullToZero) {
        this.nullToZero = nullToZero;
    }

    public boolean isZeroToNull() {
        return zeroToNull;
    }

    public void setZeroToNull(boolean zeroToNull) {
        this.zeroToNull = zeroToNull;
    }

    public boolean isOnlyAbs() {
        return onlyAbs;
    }

    public void setOnlyAbs(boolean onlyAbs) {
        this.onlyAbs = onlyAbs;
    }

    public boolean isOnlyNeg() {
        return onlyNeg;
    }

    public void setOnlyNeg(boolean onlyNeg) {
        this.onlyNeg = onlyNeg;
    }

    public int getDecimals() {
        return decimals;
    }

    public void setDecimals(int decimals) {
        this.decimals = decimals;
    }

}

Step 2)

<!DOCTYPE facelet-taglib PUBLIC
    "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
    "facelet-taglib_1_0.dtd">
<facelet-taglib version="2.2"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facelettaglibrary_2_2.xsd">
    <namespace>http://ocabit.com.br/facelets</namespace>


    <tag>
        <tag-name>convertBigDecimal</tag-name>      
        <converter>
            <converter-id>com.ocabit.jsf.converter.BigDecimalConverter</converter-id>           
        </converter>    
    </tag>


</facelet-taglib>

Step 3)

<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
    version="2.2">

    <converter>
        <description></description>
        <converter-id>com.ocabit.jsf.converter.BigDecimalConverter</converter-id>
        <converter-class>com.ocabit.jsf.converter.BigDecimalConverter</converter-class>
        <property>
            <property-name>nullToZero</property-name>
            <property-class>boolean</property-class>
            <description>Ao invés de retornar 'null', retorna '0.0'</description>
        </property>
        <property>
            <property-name>zeroToNull</property-name>
            <property-class>boolean</property-class>
            <description>Ao invés de retornar '0.0', retorna 'null'</description>
        </property>
        <property>
            <property-name>onlyAbs</property-name>
            <property-class>boolean</property-class>
            <description>Somente retorna números positivos</description>
        </property>
        <property>
            <property-name>onlyNeg</property-name>
            <property-class>boolean</property-class>
            <description>Somente retorna números negativos</description>
        </property>
        <property>
            <property-name>decimals</property-name>
            <property-class>int</property-class>
            <description>Quantidade de casas decimais</description>
        </property>
    </converter>


</faces-config>

Voilà.

Share:
21,216
user871784
Author by

user871784

Updated on April 02, 2020

Comments

  • user871784
    user871784 about 4 years

    I'm trying to implement a custom truncate converter, which truncates a string at a given index and adds a continuation symbol. The converter works fine, only when i hard code the parameters, as they are not being passed to the backend. What am I doing wrong?

    The parameters are properties of the converter class:

    @FacesConverter(value = TruncateConverter.CONVERTER_ID)
    public class TruncateConverter implements Converter, StateHolder
    {
        public static final String CONVERTER_ID = "bla.blablabla.Truncate";
    
        private int truncateIndex;
        private String contSymbol;
    

    Here is how i'm using the converter (or trying to):

    <h:outputText id="news-text-left" value="#{newsListBean.newsList_teaser.text}">
        <f:converter converterId="bla.blablabla.Truncate" truncateIndex="150" contSymbol="..." />
    </h:outputText>
    

    I googled around for quite a bit and wasn't able to find a single example of a JSF2 converter with parameters... Thank you guys for your help, really appreciate it!

  • VeenarM
    VeenarM about 10 years
    Can you note that the f:attribute shouldn't be inside the converter block, it should be <f:outputText> <f:converter/> <f:attribute/> then end the output text. The attribute is bound to the component id, not to the converter itself.