All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jakarta.faces.convert.DateTimeConverter Maven / Gradle / Ivy

There is a newer version: 4.1.0-RC3
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package jakarta.faces.convert;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

import jakarta.faces.component.PartialStateHolder;
import jakarta.faces.component.UIComponent;
import jakarta.faces.context.FacesContext;

import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFConverter;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
import org.apache.myfaces.core.api.shared.MessageUtils;
import org.apache.myfaces.core.api.shared.lang.Assert;

/**
 * This tag associates a date time converter with the nearest parent UIComponent.
 * 
 * Unless otherwise specified, all attributes accept static values or EL expressions.
 * 
 * see Javadoc of Faces Specification
 */
@JSFConverter(name = "f:convertDateTime", bodyContent = "empty")
@JSFJspProperty(
    name="binding", 
    returnType = "jakarta.faces.convert.DateTimeConverter",
    longDesc = "A ValueExpression that evaluates to a DateTimeConverter.")
public class DateTimeConverter
        implements Converter, PartialStateHolder
{

    // API field
    public static final String CONVERTER_ID = "jakarta.faces.DateTime";
    public static final String DATE_ID = "jakarta.faces.converter.DateTimeConverter.DATE";
    public static final String DATETIME_ID = "jakarta.faces.converter.DateTimeConverter.DATETIME";
    public static final String STRING_ID = "jakarta.faces.converter.STRING";
    public static final String TIME_ID = "jakarta.faces.converter.DateTimeConverter.TIME";

    // internal constants
    private static final String TYPE_DATE = "date";
    private static final String TYPE_TIME = "time";
    private static final String TYPE_BOTH = "both";
    private static final String TYPE_LOCAL_DATE = "localDate";
    private static final String TYPE_LOCAL_TIME = "localTime";
    private static final String TYPE_LOCAL_DATE_TIME = "localDateTime";
    private static final String TYPE_OFFSET_TIME = "offsetTime";
    private static final String TYPE_OFFSET_DATE_TIME = "offsetDateTime";
    private static final String TYPE_ZONED_DATE_TIME = "zonedDateTime";
            
    private static final String STYLE_DEFAULT = "default";
    private static final String STYLE_MEDIUM = "medium";
    private static final String STYLE_SHORT = "short";
    private static final String STYLE_LONG = "long";
    private static final String STYLE_FULL = "full";
    private static final TimeZone TIMEZONE_DEFAULT = TimeZone.getTimeZone("GMT");

    private String _dateStyle;
    private Locale _locale;
    private String _pattern;
    private String _timeStyle;
    private TimeZone _timeZone;
    private String _type;
    private boolean _transient;

    // CONSTRUCTORS
    public DateTimeConverter()
    {
    }

    // METHODS
    @Override
    public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value)
    {
        Assert.notNull(facesContext, "facesContext");
        Assert.notNull(uiComponent, "uiComponent");

        if (value != null)
        {
            value = value.trim();
            if (value.length() > 0)
            {
                if (isJava8DateTimeFormatter())
                {
                    DateTimeFormatter format = getDateTimeFormatter();
                    try
                    {
                        TemporalQuery tq = getTemporalQuery();
                        if (tq != null)
                        {
                            return format.parse(value, tq);
                        }
                        else
                        {
                            return format.parse(value);
                        }
                    }
                    catch (Exception e)
                    {
                        String type = getType();
                        TemporalAccessor currentDate;
                        if (TYPE_LOCAL_DATE.equals(type) || TYPE_LOCAL_DATE_TIME.equals(type) 
                                || TYPE_LOCAL_TIME.equals(type))
                        {
                            currentDate = LocalDateTime.now();
                        }
                        else if (TYPE_OFFSET_TIME.equals(type) || TYPE_OFFSET_DATE_TIME.equals(type))
                        {
                            currentDate = OffsetDateTime.now();
                        }
                        else
                        {
                            currentDate = ZonedDateTime.now();
                        }
                        Object[] args = new Object[]{value,
                                format.format(currentDate),MessageUtils.getLabel(facesContext, uiComponent)};

                        if(type.equals(TYPE_LOCAL_DATE))
                        {
                            throw new ConverterException(MessageUtils.getErrorMessage(facesContext, DATE_ID, args));
                        }
                        else if (type.equals(TYPE_LOCAL_TIME) || type.equals(TYPE_OFFSET_TIME))
                        {
                            throw new ConverterException(MessageUtils.getErrorMessage(facesContext, TIME_ID, args));
                        }
                        else if (type.equals(TYPE_LOCAL_DATE_TIME) || type.equals(TYPE_OFFSET_DATE_TIME) 
                                || type.equals(TYPE_ZONED_DATE_TIME))
                        {
                            throw new ConverterException(
                                    MessageUtils.getErrorMessage(facesContext, DATETIME_ID, args));
                        }
                        else
                        {
                            throw new ConverterException("invalid type '" + _type + '\'');
                        }
                    }
                }
                else
                {
                    DateFormat format = getDateFormat();
                    TimeZone tz = getTimeZone();
                    if( tz != null )
                    {
                        format.setTimeZone(tz);
                    }
                    try
                    {
                        return format.parse(value);
                    }
                    catch (ParseException e)
                    {
                        String type = getType();
                        Object[] args = new Object[]{value,
                                format.format(new Date()),MessageUtils.getLabel(facesContext, uiComponent)};

                        if(type.equals(TYPE_DATE))
                        {
                            throw new ConverterException(MessageUtils.getErrorMessage(facesContext, DATE_ID, args));
                        }
                        else if (type.equals(TYPE_TIME))
                        {
                            throw new ConverterException(MessageUtils.getErrorMessage(facesContext, TIME_ID, args));
                        }
                        else if (type.equals(TYPE_BOTH))
                        {
                            throw new ConverterException(
                                    MessageUtils.getErrorMessage(facesContext, DATETIME_ID, args));
                        }
                        else
                        {
                            throw new ConverterException("invalid type '" + _type + '\'');
                        }
                    }
                }
            }
        }
        return null;
    }

    @Override
    public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value)
    {
        Assert.notNull(facesContext, "facesContext");
        Assert.notNull(uiComponent, "uiComponent");

        if (value == null)
        {
            return "";
        }
        if (value instanceof String)
        {
            return (String)value;
        }

        if (isJava8DateTimeFormatter())
        {
            DateTimeFormatter format = getDateTimeFormatter();
            
            if (value instanceof TemporalAccessor)
            {
                try
                {
                    return format.format((TemporalAccessor) value);
                }
                catch (Exception e)
                {
                    throw new ConverterException(MessageUtils.getErrorMessage(facesContext, STRING_ID,
                            new Object[]{value,MessageUtils.getLabel(facesContext, uiComponent)}),e);
                }
            }
            return null;
        }
        else
        {        
            DateFormat format = getDateFormat();
            TimeZone tz = getTimeZone(); 
            if (tz != null)
            {
                format.setTimeZone(tz);
            }
            try
            {
                return format.format(value);
            }
            catch (Exception e)
            {
                throw new ConverterException(MessageUtils.getErrorMessage(facesContext, STRING_ID,
                        new Object[]{value,MessageUtils.getLabel(facesContext, uiComponent)}),e);
            }
        }
    }

    private DateFormat getDateFormat()
    {
        String type = getType();
        DateFormat format;
        if (_pattern != null)
        {
            try 
            {
                format = new SimpleDateFormat(_pattern, getLocale());
            } 
                catch (IllegalArgumentException iae)
            {
                throw new ConverterException("Invalid pattern", iae);    
            }
        }
        else if (type.equals(TYPE_DATE))
        {
            format = DateFormat.getDateInstance(calcStyle(getDateStyle()), getLocale());
        }
        else if (type.equals(TYPE_TIME))
        {
            format = DateFormat.getTimeInstance(calcStyle(getTimeStyle()), getLocale());
        }
        else if (type.equals(TYPE_BOTH))
        {
            format = DateFormat.getDateTimeInstance(calcStyle(getDateStyle()),
                                                    calcStyle(getTimeStyle()),
                                                    getLocale());
        }
        else
        {
            throw new ConverterException("invalid type '" + _type + '\'');
        }
        
        // format cannot be lenient (JSR-127)
        format.setLenient(false);
        return format;
    }
    
    private DateTimeFormatter getDateTimeFormatter()
    {
        DateTimeFormatter formatter = null;
        String type = getType();
        String pattern = getPattern();
        if (pattern != null && pattern.length() > 0)
        {
            Locale locale = getLocale();
            if (locale == null)
            {
                formatter = DateTimeFormatter.ofPattern(pattern);
            }
            else
            {
                formatter = DateTimeFormatter.ofPattern(pattern, locale);
            }
        }
        else
        {
            if (TYPE_LOCAL_DATE.equals(type))
            {
                formatter = DateTimeFormatter.ofLocalizedDate(calcFormatStyle(getDateStyle()));
            }
            else if (TYPE_LOCAL_DATE_TIME.equals(type) )
            {
                String timeStyle = getTimeStyle();
                if (timeStyle != null && timeStyle.length() > 0)
                {
                    formatter = DateTimeFormatter.ofLocalizedDateTime(
                            calcFormatStyle(getDateStyle()), calcFormatStyle(timeStyle));
                }
                else
                {
                    formatter = DateTimeFormatter.ofLocalizedDateTime(
                            calcFormatStyle(getDateStyle()));
                }
            }
            else if (TYPE_LOCAL_TIME.equals(type) )
            {
                formatter = DateTimeFormatter.ofLocalizedTime(calcFormatStyle(getTimeStyle()));
            }
            else if (TYPE_OFFSET_TIME.equals(type))
            {
                formatter = DateTimeFormatter.ISO_OFFSET_TIME;
            }
            else if (TYPE_OFFSET_DATE_TIME.equals(type))
            {
                formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
            }
            else if (TYPE_ZONED_DATE_TIME.equals(type))
            {
                formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME;
            }
            
            Locale locale = getLocale();
            if (locale != null)
            {
                formatter = formatter.withLocale(locale);
            }
        }
        return formatter;
    }
    
    /**
     * According to java8 api, parse() also receives a TemporalQuery parameter that works as a qualifier to decide
     * how to parse and return the right type of value.
     * 
     * @return 
     */
    private TemporalQuery getTemporalQuery()
    {
        String type = getType();
        if (TYPE_LOCAL_DATE.equals(type))
        {
            return LocalDate::from;
        }
        else if (TYPE_LOCAL_DATE_TIME.equals(type) )
        {
            return LocalDateTime::from;            
        }
        else if (TYPE_LOCAL_TIME.equals(type) )
        {
            return LocalTime::from;
        }
        else if (TYPE_OFFSET_TIME.equals(type))
        {
            return OffsetTime::from;
        }
        else if (TYPE_OFFSET_DATE_TIME.equals(type))
        {
            return OffsetDateTime::from;
        }
        else if (TYPE_ZONED_DATE_TIME.equals(type))
        {
            return ZonedDateTime::from;
        }
        return null;
    }
    
    private FormatStyle calcFormatStyle(String name)
    {
        if (name.equals(STYLE_DEFAULT))
        {
            return FormatStyle.MEDIUM;
        }
        if (name.equals(STYLE_MEDIUM))
        {
            return FormatStyle.MEDIUM;
        }
        if (name.equals(STYLE_SHORT))
        {
            return FormatStyle.SHORT;
        }
        if (name.equals(STYLE_LONG))
        {
            return FormatStyle.LONG;
        }
        if (name.equals(STYLE_FULL))
        {
            return FormatStyle.FULL;
        }
        
        throw new ConverterException("invalid style '" + name + '\'');
    }

    private int calcStyle(String name)
    {
        if (name.equals(STYLE_DEFAULT))
        {
            return DateFormat.DEFAULT;
        }
        if (name.equals(STYLE_MEDIUM))
        {
            return DateFormat.MEDIUM;
        }
        if (name.equals(STYLE_SHORT))
        {
            return DateFormat.SHORT;
        }
        if (name.equals(STYLE_LONG))
        {
            return DateFormat.LONG;
        }
        if (name.equals(STYLE_FULL))
        {
            return DateFormat.FULL;
        }

        throw new ConverterException("invalid style '" + name + '\'');
    }
    
    private boolean isJava8DateTimeFormatter()
    {
        String type = getType();
        if (type != null)
        {
            return  TYPE_LOCAL_DATE.equals(type) ||
                    TYPE_LOCAL_TIME.equals(type) ||
                    TYPE_LOCAL_DATE_TIME.equals(type) ||
                    TYPE_OFFSET_TIME.equals(type) ||
                    TYPE_OFFSET_DATE_TIME.equals(type) ||
                    TYPE_ZONED_DATE_TIME.equals(type);
        }
        else
        {
            return false;
        }
    }

    // STATE SAVE/RESTORE
    @Override
    public void restoreState(FacesContext facesContext, Object state)
    {
        if (state != null)
        {
            Object[] values = (Object[])state;
            _dateStyle = (String)values[0];
            _locale = (Locale)values[1];
            _pattern = (String)values[2];
            _timeStyle = (String)values[3];
            _timeZone = (TimeZone)values[4];
            _type = (String)values[5];
        }
    }

    @Override
    public Object saveState(FacesContext facesContext)
    {
        if (!initialStateMarked())
        {
            Object[] values = new Object[6];
            values[0] = _dateStyle;
            values[1] = _locale;
            values[2] = _pattern;
            values[3] = _timeStyle;
            values[4] = _timeZone;
            values[5] = _type;
            return values;
        }
        return null;
    }

    // GETTER & SETTER
    
    /**
     * The style of the date.  Values include: default, short, medium, 
     * long, and full.
     * 
     */
    @JSFProperty
    public String getDateStyle()
    {
        return _dateStyle != null ? _dateStyle : STYLE_DEFAULT;
    }

    public void setDateStyle(String dateStyle)
    {
        _dateStyle = dateStyle;
        clearInitialState();
    }

    /**
     * The name of the locale to be used, instead of the default.
     * 
     */
    @JSFProperty
    public Locale getLocale()
    {
        if (_locale != null)
        {
            return _locale;
        }
        FacesContext context = FacesContext.getCurrentInstance();
        return context.getViewRoot().getLocale();
    }

    public void setLocale(Locale locale)
    {
        _locale = locale;
        clearInitialState();
    }

    /**
     * A custom Date formatting pattern, in the format used by java.text.SimpleDateFormat.
     * 
     */
    @JSFProperty
    public String getPattern()
    {
        return _pattern;
    }

    public void setPattern(String pattern)
    {
        _pattern = pattern;
        clearInitialState();
    }

    /**
     * The style of the time.  Values include:  default, short, medium, long, 
     * and full.
     * 
     */
    @JSFProperty
    public String getTimeStyle()
    {
        return _timeStyle != null ? _timeStyle : STYLE_DEFAULT;
    }

    public void setTimeStyle(String timeStyle)
    {
        _timeStyle = timeStyle;
        clearInitialState();
    }

    /**
     * The time zone to use instead of GMT (the default timezone). When
     * this value is a value-binding to a TimeZone instance, that
     * timezone is used. Otherwise this value is treated as a String
     * containing a timezone id, ie as the ID parameter of method
     * java.util.TimeZone.getTimeZone(String).
     * 
     */
    @JSFProperty
    public TimeZone getTimeZone()
    {
        return _timeZone != null ? _timeZone : TIMEZONE_DEFAULT;
    }

    public void setTimeZone(TimeZone timeZone)
    {
        _timeZone = timeZone;
        clearInitialState();
    }

    public boolean isTransient()
    {
        return _transient;
    }

    public void setTransient(boolean aTransient)
    {
        _transient = aTransient;
    }

    /**
     * Specifies whether the date, time, or both should be
     * parsed/formatted.
     * Valid values are: "date", "time", "both", "localDate", "localDateTime", "localTime", "offsetTime",
     * "offsetDateTime", and "zonedDateTime".
     * The prefixes "local", "offset", "zoned" are used, when the type of the value is
     * one of the corresponding Java 8 Date Time API classes.
     * Default is "date".
     */
    @JSFProperty
    public String getType()
    {
        return _type != null ? _type : TYPE_DATE;
    }

    public void setType(String type)
    {
        _type = type;
        clearInitialState();
    }
    
    private boolean _initialStateMarked = false;

    @Override
    public void clearInitialState()
    {
        _initialStateMarked = false;
    }

    @Override
    public boolean initialStateMarked()
    {
        return _initialStateMarked;
    }

    @Override
    public void markInitialState()
    {
        _initialStateMarked = true;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy