py.una.pol.karaku.util.FormatProvider.java Source code

Java tutorial

Introduction

Here is the source code for py.una.pol.karaku.util.FormatProvider.java

Source

/*-
 * Copyright (c)
 *
 *       2012-2014, Facultad Politcnica, Universidad Nacional de Asuncin.
 *       2012-2014, Facultad de Ciencias Mdicas, Universidad Nacional de Asuncin.
 *       2012-2013, Centro Nacional de Computacin, Universidad Nacional de Asuncin.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */
package py.una.pol.karaku.util;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.annotation.Nullable;
import javax.validation.constraints.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import py.una.pol.karaku.math.Quantity;

/**
 * Singleton que se encarga de realizar los formatos de {@link Date} y
 * {@link BigDecimal} en el sistema.
 * <p>
 * Todas las conversiones de {@link String} a {@link Date} o {@link BigDecimal}
 * <b>deben</b> ser realizadas por esta clase.
 * </p>
 * <p>
 * Para acceder a este componente se pueden utilizar alguno de los siguientes
 * mecanismos:
 * <ol>
 * <li> {@link javax.el.Expression}: #{fp}</li>
 * <li> {@link Component}: {@literal @}Autowire {@link FormatProvider} fp;</li>
 * <li> {@link javax.faces.component.FacesComponent}:
 * Util.getSpringBeanByJSFContext(context, FormatProvider.class)</li>
 * 
 * </ol>
 * </p>
 * 
 * @author Arturo Volpe
 * @since 2.2
 * @version 1.0 Sep 10, 2013
 * @see Util#getSpringBeanByJSFContext(javax.faces.context.FacesContext, Class)
 */
@Component(value = FormatProvider.FORMAT_PROVIDER_NAME)
public class FormatProvider {

    private static final Logger LOG = LoggerFactory.getLogger(FormatProvider.class);
    private static final String CANT_PARSE_NULL_QUANTITY = "Cant parse null quantity";

    /**
     * Nombre de este bean, se reduce el nombre para disminuir la verbosidad
     * desde jsf.
     */
    public static final String FORMAT_PROVIDER_NAME = "fp";

    /**
     * 
     * Formato de las fechas en el sistema. Este formato incluye el ao
     * completo, y esta separado por guiones.
     * <p>
     * Si se desea que se formateen hora y minutos ver {@link #DATETIME_FORMAT}
     * </p>
     * 
     * @see #DATE_SHORT_FORMAT
     */
    public static final String DATE_FORMAT = "dd-MM-yyyy";

    /**
     * Formato de las fechas cortas, este formato incluye los dos ltimos
     * numeros de la fecha.
     * <p>
     * Si se desea que se formateen hora y los minutos, ver
     * {@link #DATETIME_SHORT_FORMAT}
     * </p>
     * 
     * @see #DATE_FORMAT
     */
    public static final String DATE_SHORT_FORMAT = "dd-MM-yy";

    /**
     * Formato de las fechas que representan horas y minutos, no incluye los
     * segundos.
     * 
     */
    public static final String TIME_FORMAT = "HH:mm";

    /**
     * Formato de las fechas con horas y minutos.
     * <p>
     * Este formato incluye el ao completo separado por guiones.
     * </p>
     * 
     * 
     * @see #DATE_SHORT_FORMAT
     */
    public static final String DATETIME_FORMAT = "dd-MM-yyyy HH:mm";

    /**
     * Formato de las fechas cortas con horas y minutos.
     * <p>
     * Este formato incluye el ao completo separado por guiones.
     * </p>
     */
    public static final String DATETIME_SHORT_FORMAT = "dd-MM-yy HH:mm";

    private static final String EMPTY_STRING = "";

    /**
     * Formato para los nmeros. Se compone de una cantidad variable de numeros
     * a la izquierda, y a lo sumo dos a la derecha.
     * <p>
     * La cadena mas pequea que puede generar este formato es <code>"0"</code>,
     * notar que el mtodo {@link #asNumber(BigDecimal)} puede retornar
     * <code>""</code> si se le pasa como parmetro <code>null</code>.
     * </p>
     */
    public static final String NUMBER_FORMAT = "###,###,###,###,###,###,###,###,###,###,##0.##";

    /**
     * Formato para los nmeros. Se compone de una cantidad variable de numeros
     * a la izquierda, y a lo sumo dos a la derecha.
     * <p>
     * La cadena mas pequea que puede generar este formato es <code>"0"</code>,
     * notar que el mtodo {@link #asNumber(BigDecimal)} puede retornar
     * <code>""</code> si se le pasa como parmetro <code>null</code>.
     * </p>
     */
    public static final String LONG_NUMBER_FORMAT = NUMBER_FORMAT;

    /**
     * Formato para las monedas. Se compone de una cantidad variable de numeros
     * a la izquierda, y a lo sumo dos a la derecha.
     */
    public static final String MONEY_FORMAT = "###,###,###,###,###,###,###,###,###,###,##0";

    private Map<String, SimpleDateFormat> formats;

    private boolean initialized = false;

    private DecimalFormatSymbols dfs;

    private synchronized void init() {

        if (this.initialized) {
            return;
        }

        this.initialized = true;
        this.formats = new HashMap<String, SimpleDateFormat>();
        this.formats.put(DATE_FORMAT, new SimpleDateFormat(DATE_FORMAT));
        this.formats.put(DATE_SHORT_FORMAT, new SimpleDateFormat(DATE_SHORT_FORMAT));
        this.formats.put(TIME_FORMAT, new SimpleDateFormat(TIME_FORMAT));
        this.formats.put(DATETIME_FORMAT, new SimpleDateFormat(DATETIME_FORMAT));
        this.formats.put(DATETIME_SHORT_FORMAT, new SimpleDateFormat(DATETIME_SHORT_FORMAT));
        for (SimpleDateFormat sdf : this.formats.values()) {
            sdf.setLenient(false);
        }
    }

    /**
     * Formatea una fecha con el formato {@link #DATE_SHORT_FORMAT}
     * <p>
     * Esta fecha debe ser usada en grillas y en lugares donde el espacio es
     * reducido. Si es el caso en que se deben mostrar horas y minutos, se debe
     * usar {@link #asShortDateTime(Date)} o {@link #asTime(Date)}.
     * </p>
     * 
     * <pre>
     *    Dado:
     *       Date = 11-11-2013 22:15:00
     *    Retorna
     *       11-11-13
     * </pre>
     * 
     * @param date
     *            fecha que se desea parsear.
     * @return cadena con el formato {@value #DATE_SHORT_FORMAT}, retorna
     *         <code>""</code> si el parmetro es <code>null</code>
     */
    public String asShortDate(@Nullable Date date) {

        return this.dateToString(date, DATE_SHORT_FORMAT);
    }

    /**
     * Parsea una fecha con el formato {@link #DATE_SHORT_FORMAT}.
     * <p>
     * Ejemplo de uso:
     * 
     * <pre>
     *    fp.parseShortDate("11-11-13")
     * Retorna:
     *    Date con:
     *       Fecha:   11-11-2013
     *       Hora:   00:00:00:00
     * </pre>
     * 
     * </p>
     * 
     * @param string
     *            cadena con el formato {@value #DATE_SHORT_FORMAT}
     * @return {@link Date} correspondiente
     * @throws ParseException
     *             si <b>string</b> no es una fecha vlida.
     */
    public Date parseShortDate(@Nullable String string) throws ParseException {

        return this.stringToDate(string, DATE_SHORT_FORMAT);
    }

    /**
     * Formatea una fecha con el formato {@link #DATE_FORMAT}
     * <p>
     * Esta fecha debe ser usada en detalles y en lugares donde el espacio no es
     * reducido. Si es el caso en que se deben mostrar horas y minutos, se debe
     * usar {@link #asDateTime(Date)} o {@link #asTime(Date)}.
     * </p>
     * 
     * <pre>
     *    Dado:
     *       Date = 11-11-2013 22:15:00
     *    Retorna
     *       11-11-2013
     * </pre>
     * 
     * @param date
     *            fecha que se desea parsear.
     * @return cadena con el formato {@value #DATE_FORMAT}, retorna
     *         <code>""</code> si el parmetro es <code>null</code>
     */
    public String asDate(@Nullable Date date) {

        return this.dateToString(date, DATE_FORMAT);

    }

    /**
     * Parsea una fecha con el formato {@link #DATE_FORMAT}.
     * <p>
     * Ejemplo de uso:
     * 
     * <pre>
     *    fp.parseShortDate("11-11-2013")
     * Retorna:
     *    Date con:
     *       Fecha:   11-11-2013
     *       Hora:   00:00:00:00
     * </pre>
     * 
     * </p>
     * 
     * @param string
     *            cadena con el formato {@value #DATE_FORMAT}
     * @return {@link Date} correspondiente
     * @throws ParseException
     *             si <b>string</b> no es una fecha vlida.
     */
    public Date parseDate(@Nullable String string) throws ParseException {

        return this.stringToDate(string, DATE_FORMAT);
    }

    /**
     * Formatea una fecha con el formato {@link #TIME_FORMAT}
     * <p>
     * Este formato debe ser usado cuando al fecha no es importante, solo la
     * hora y los minutos.
     * </p>
     * 
     * <pre>
     *    Dado:
     *       Date = 11-11-2013 22:15:00
     *    Retorna
     *       22:15
     * </pre>
     * 
     * @param date
     *            fecha que se desea parsear.
     * @return cadena con el formato {@value #TIME_FORMAT}, retorna
     *         <code>""</code> si el parmetro es <code>null</code>
     */
    public String asTime(@Nullable Date date) {

        return this.dateToString(date, TIME_FORMAT);

    }

    /**
     * Parsea una fecha con el formato {@link #TIME_FORMAT}.
     * <p>
     * Ejemplo de uso:
     * 
     * <pre>
     *    fp.parseShortDate("15:30")
     * Retorna:
     *    Date con:
     *       Fecha:   01-01-1970
     *       Hora:   15:39:00:00
     * </pre>
     * 
     * </p>
     * 
     * @param string
     *            cadena con el formato {@value #DATE_FORMAT}
     * @return {@link Date} correspondiente
     * @throws ParseException
     *             si <b>string</b> no es una fecha vlida.
     */
    public Date parseTime(@Nullable String string) throws ParseException {

        return this.stringToDate(string, TIME_FORMAT);
    }

    /**
     * Formatea una fecha con el formato {@link #DATETIME_FORMAT}
     * <p>
     * Este formato debe ser usado cuando al fecha no es importante, solo la
     * hora y los minutos.
     * </p>
     * 
     * <pre>
     *    Dado:
     *       Date = 11-11-2013 22:15:00
     *    Retorna
     *       11-11-2013 22:15
     * </pre>
     * 
     * @param date
     *            fecha que se desea parsear.
     * @return cadena con el formato {@value #DATETIME_FORMAT}, retorna
     *         <code>""</code> si el parmetro es <code>null</code>
     */
    public String asDateTime(@Nullable Date date) {

        if (date != null) {
            return this.dateToString(date, DATE_FORMAT) + " " + this.dateToString(date, TIME_FORMAT);
        }
        return "";
    }

    /**
     * Parsea una fecha con el formato {@link #DATETIME_FORMAT}.
     * <p>
     * Ejemplo de uso:
     * 
     * <pre>
     *    fp.parseShortDate("2013-11-11 15:39")
     * Retorna:
     *    Date con:
     *       Fecha:   11-11-2013
     *       Hora:   15:39:00:00
     * </pre>
     * 
     * </p>
     * 
     * @param string
     *            cadena con el formato {@value #DATE_FORMAT}
     * @return {@link Date} correspondiente
     * @throws ParseException
     *             si <b>string</b> no es una fecha vlida.
     */
    public Date parseDateTime(@Nullable String string) throws ParseException {

        return this.stringToDate(string, DATETIME_FORMAT);
    }

    /**
     * Formatea una fecha con el formato {@link #DATETIME_SHORT_FORMAT}
     * <p>
     * Este formato debe ser usado cuando al fecha no es importante, solo la
     * hora y los minutos.
     * </p>
     * 
     * <pre>
     *    Dado:
     *       Date = 11-11-13 22:15:00
     *    Retorna
     *       11-11-13 22:15
     * </pre>
     * 
     * @param date
     *            fecha que se desea parsear.
     * @return cadena con el formato {@value #DATETIME_SHORT_FORMAT}, retorna
     *         <code>""</code> si el parmetro es <code>null</code>
     */
    public String asShortDateTime(@Nullable Date date) {

        return this.dateToString(date, DATETIME_SHORT_FORMAT);
    }

    /**
     * Parsea una fecha con el formato {@link #DATETIME_SHORT_FORMAT}.
     * <p>
     * Ejemplo de uso:
     * 
     * <pre>
     *    fp.parseShortDate("13-11-11 15:39")
     * Retorna:
     *    Date con:
     *       Fecha:   11-11-2013
     *       Hora:   15:39:00:00
     * </pre>
     * 
     * </p>
     * 
     * @param string
     *            cadena con el formato {@value #DATETIME_SHORT_FORMAT}
     * @return {@link Date} correspondiente
     * @throws ParseException
     *             si <b>string</b> no es una fecha vlida.
     */
    public Date parseShortDateTime(@Nullable String string) throws ParseException {

        return this.stringToDate(string, DATETIME_SHORT_FORMAT);
    }

    /**
     * Parsea un nmero con el formato estandar.
     * 
     * <p>
     * 
     * <table>
     * <tr>
     * <td><b>Numero</b></td>
     * <td><b>Convertido</b></td>
     * </tr>
     * <tr>
     * <td>1000000000000</td>
     * <td>1.000.000.000.000</td>
     * </tr>
     * <tr>
     * <td>100000</td>
     * <td>100.000</td>
     * </tr>
     * <tr>
     * <td>100</td>
     * <td>100</td>
     * </tr>
     * <tr>
     * <td>1</td>
     * <td>1</td>
     * </tr>
     * <tr>
     * <td>0</td>
     * <td>0</td>
     * </tr>
     * <tr>
     * <td>0.111</td>
     * <td>0,11</td>
     * </tr>
     * <tr>
     * <td>0.119</td>
     * <td>0,12</td>
     * </tr>
     * <tr>
     * <td>0.0111</td>
     * <td>0,01</td>
     * </tr>
     * <tr>
     * <td>0.0159</td>
     * <td>0,02</td>
     * </tr>
     * </table>
     * </p>
     * 
     * @param bd
     *            {@link BigDecimal} a convertir.
     * @return cadena formateada, segun los ejemplos anteriores
     */
    public String asNumber(@NotNull BigDecimal bd) {

        if (bd == null) {
            throw new IllegalArgumentException(CANT_PARSE_NULL_QUANTITY);
        }
        return this.getNumberFormat(NUMBER_FORMAT).format(bd);

    }

    /**
     * Parsea un nmero con el formato estandar.
     * 
     * <p>
     * 
     * <table>
     * <tr>
     * <td><b>Numero</b></td>
     * <td><b>Convertido</b></td>
     * </tr>
     * <tr>
     * <td>1000000000000</td>
     * <td>1.000.000.000.000</td>
     * </tr>
     * <tr>
     * <td>100000</td>
     * <td>100.000</td>
     * </tr>
     * <tr>
     * <td>100</td>
     * <td>100</td>
     * </tr>
     * <tr>
     * <td>1</td>
     * <td>1</td>
     * </tr>
     * <tr>
     * <td>0</td>
     * <td>0</td>
     * </tr>
     * <tr>
     * <td>0.111</td>
     * <td>0,11</td>
     * </tr>
     * <tr>
     * <td>0.119</td>
     * <td>0,12</td>
     * </tr>
     * <tr>
     * <td>0.0111</td>
     * <td>0,01</td>
     * </tr>
     * <tr>
     * <td>0.0159</td>
     * <td>0,02</td>
     * </tr>
     * bd
     * </table>
     * </p>
     * 
     * @param bd
     *            {@link BigDecimal} a convertir.
     * @return cadena formateada, segun los ejemplos anteriores
     */
    public String asNumber(@NotNull Quantity bd) {

        if (bd == null) {
            throw new IllegalArgumentException(CANT_PARSE_NULL_QUANTITY);
        }
        return this.getNumberFormat(NUMBER_FORMAT).format(bd);

    }

    public Quantity parseQuantity(String number) throws ParseException {

        if (number == null || EMPTY_STRING.equals(number)) {
            return null;
        }
        return new Quantity(getNumberFormat(NUMBER_FORMAT).parse(number).doubleValue());
    }

    public String asLongNumber(@NotNull Quantity bd) {

        if (bd == null) {
            throw new IllegalArgumentException(CANT_PARSE_NULL_QUANTITY);
        }
        return this.getNumberFormat(LONG_NUMBER_FORMAT).format(bd);

    }

    public Quantity parseLongQuantity(String number) throws ParseException {

        if (number == null || EMPTY_STRING.equals(number)) {
            return null;
        }

        return new Quantity(this.getNumberFormat(LONG_NUMBER_FORMAT).parse(number).toString());
    }

    /**
     * Parsea una cadena con el formato pasado.
     * 
     * @param date
     *            fecha a serializar
     * @param format
     *            formato deseado
     * @return {@link String} con el formato deseado.
     */
    protected synchronized String dateToString(Date date, String format) {

        if (date == null) {
            return EMPTY_STRING;
        }
        return this.getFormat(format).format(date);
    }

    /**
     * Formatea una fecha con el formato dado
     * 
     * @param string
     *            cadena a formatear
     * @param format
     *            formato deseado
     * @return {@link Date} con los demas parmetros a cero.
     * @throws ParseException
     *             si el formato no es adecuado.
     */
    protected synchronized Date stringToDate(String string, String format) throws ParseException {

        if (string == null || EMPTY_STRING.equals(string)) {
            return null;
        }
        SimpleDateFormat sdf = this.getFormat(format);
        ParsePosition psp = new ParsePosition(0);
        if (string.length() != format.length()) {
            this.throwException(string, format);
        }
        Date toRet = sdf.parse(string, psp);
        if (psp.getIndex() != string.length()) {
            this.throwException(string, format);
        }
        return toRet;
    }

    private void throwException(String string, String format) throws ParseException {

        throw new ParseException("Imposible parsear cadena: " + string + " con formato " + format, 0);
    }

    /**
     * Provee un formato
     * 
     * @param format
     * @return
     */
    private SimpleDateFormat getFormat(String format) {

        if (!this.initialized) {
            this.init();
        }
        if (!this.formats.containsKey(format)) {
            throw new IllegalArgumentException("Unknow format " + format);
        }
        return this.formats.get(format);
    }

    private NumberFormat getNumberFormat(String format) {

        DecimalFormat df = new DecimalFormat(format);
        df.setDecimalFormatSymbols(this.getDFS());
        return df;
    }

    /**
     * @return
     */
    private DecimalFormatSymbols getDFS() {

        if (this.dfs == null) {
            this.dfs = new DecimalFormatSymbols(Locale.getDefault());
            this.dfs.setDecimalSeparator(',');
            this.dfs.setGroupingSeparator('.');
        }

        return this.dfs;
    }

    /**
     * Verifica si una fecha tiene el formato correspondiente a
     * {@link #DATE_FORMAT}.
     * 
     * @param date
     * @return
     */
    public boolean isDate(String date) {

        if (date == null) {
            return false;
        }
        try {
            this.parseDate(date);
            return true;
        } catch (ParseException pe) {
            LOG.trace("Can' parse date:", pe);
            return false;
        }
    }

    public String getDateFormat() {

        return DATE_FORMAT;
    }

    public String getDateTimeFormat() {

        return DATETIME_FORMAT;
    }

    public String getTimeFormat() {

        return TIME_FORMAT;
    }

    /**
     * Formato de las monedas.
     * 
     * @see #MONEY_FORMAT
     * @return {@value #MONEY_FORMAT}
     */
    public String getMoneyFormat() {

        return MONEY_FORMAT;
    }
}