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

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

Go to download

Jakarta Server Faces defines an MVC framework for building user interfaces for web applications, including UI components, state management, event handing, input validation, page navigation, and support for internationalization and accessibility.

There is a newer version: 3.1.0.SP02
Show newest version
/*
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package javax.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.DateTimeParseException;
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 javax.faces.component.PartialStateHolder;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;


/**
 * 

{@link Converter} * implementation for java.util.Date values.

* *

The getAsObject() method parses a String into a * java.util.Date, according to the following algorithm:

*
    *
  • If the specified String is null, return * a null. Otherwise, trim leading and trailing * whitespace before proceeding.
  • *
  • If the specified String - after trimming - has a zero length, * return null.
  • *
  • If the locale property is not null, * use that Locale for managing parsing. Otherwise, use the * Locale from the UIViewRoot.
  • * *
  • If a pattern has been specified, its syntax must * conform the rules specified by * java.text.SimpleDateFormat or {@code * java.time.format.DateTimeFormatter}. Which of these two formatters * is used depends on the value of {@code type}. Such a pattern * will be used to parse, and the type, * dateStyle, and timeStyle properties will be * ignored, unless the value of {@code * type} is one of the {@code java.time} specific values listed in * {@link #setType}. In this case, {@code DateTimeFormatter.ofPattern(String, Locale)} * must be called, passing the value of {@code pattern} as the first argument and * the current {@code Locale} as the second argument, * and this formatter must be used to parse the incoming value.
  • * *
  • If a pattern has not been specified, parsing will be * based on the type property, which expects a date value, * a time value, both, or one of several * values specific to classes in {@code java.time} as listed in {@link * #setType}. Any date and time values included will be parsed in * accordance to the styles specified by dateStyle and * timeStyle, respectively.
  • If a * timezone has been specified, it must be passed to the * underlying DateFormat instance. Otherwise the "GMT" * timezone is used.
  • In all cases, parsing must be non-lenient; * the given string must strictly adhere to the parsing format.
  • *
* *

The getAsString() method expects a value of type * java.util.Date (or a subclass), and creates a formatted * String according to the following algorithm:

*
    *
  • If the specified value is null, return a zero-length String.
  • *
  • If the specified value is a String, return it unmodified.
  • *
  • If the locale property is not null, * use that Locale for managing formatting. Otherwise, use the * Locale from the UIViewRoot.
  • *
  • If a timezone has been specified, it must be passed * to the underlying DateFormat instance. Otherwise * the "GMT" timezone is used.
  • *
  • If a pattern has been specified, its syntax must * conform the rules specified by * java.text.SimpleDateFormat or {@code * java.time.format.DateTimeFormatter}. Which of these two formatters * is used depends on the value of {@code type}. Such a pattern * will be used to format, and the type, * dateStyle, and timeStyle properties will be * ignored, unless the value of {@code * type} is one of the {@code java.time} specific values listed in * {@link #setType}. In this case, {@code * DateTimeFormatter.ofPattern(String, Locale)} must be called, passing * the value of {@code pattern} as the first argument and the current * {@code Locale} as the second argument, and this formatter must be * used to format the outgoing value.
  • *
  • If a pattern has not been specified, formatting will be * based on the type property, which includes a date value, * a time value, both or into the formatted String. Any date and time * values included will be formatted in accordance to the styles specified * by dateStyle and timeStyle, respectively.
  • *
*/ public class DateTimeConverter implements Converter, PartialStateHolder { // ------------------------------------------------------ Manifest Constants /** *

The standard converter id for this converter.

*/ public static final String CONVERTER_ID = "javax.faces.DateTime"; /** *

The message identifier of the {@link javax.faces.application.FacesMessage} to be created if * the conversion to Date fails. The message format * string for this message may optionally include the following * placeholders: *

    *
  • {0} replaced by the unconverted value.
  • *
  • {1} replaced by an example value.
  • *
  • {2} replaced by a String whose value * is the label of the input component that produced this message.
  • *
*/ public static final String DATE_ID = "javax.faces.converter.DateTimeConverter.DATE"; /** *

The message identifier of the {@link javax.faces.application.FacesMessage} to be created if * the conversion to Time fails. The message format * string for this message may optionally include the following * placeholders: *

    *
  • {0} replaced by the unconverted value.
  • *
  • {1} replaced by an example value.
  • *
  • {2} replaced by a String whose value * is the label of the input component that produced this message.
  • *
*/ public static final String TIME_ID = "javax.faces.converter.DateTimeConverter.TIME"; /** *

The message identifier of the {@link javax.faces.application.FacesMessage} to be created if * the conversion to DateTime fails. The message format * string for this message may optionally include the following * placeholders: *

    *
  • {0} replaced by the unconverted value.
  • *
  • {1} replaced by an example value.
  • *
  • {2} replaced by a String whose value * is the label of the input component that produced this message.
  • *
*/ public static final String DATETIME_ID = "javax.faces.converter.DateTimeConverter.DATETIME"; /** *

The message identifier of the {@link javax.faces.application.FacesMessage} to be created if * the conversion of the DateTime value to * String fails. The message format string for this message * may optionally include the following placeholders: *

    *
  • {0} relaced by the unconverted value.
  • *
  • {1} replaced by a String whose value * is the label of the input component that produced this message.
  • *
*/ public static final String STRING_ID = "javax.faces.converter.STRING"; private static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getTimeZone("GMT"); // ------------------------------------------------------ Instance Variables private String dateStyle = "default"; private Locale locale = null; private String pattern = null; private String timeStyle = "default"; private TimeZone timeZone = DEFAULT_TIME_ZONE; private String type = "date"; // -------------------------------------------------------------- Properties /** *

Return the style to be used to format or parse dates. If not set, * the default value, default, is returned.

* * @return the style */ public String getDateStyle() { return (this.dateStyle); } /** *

Set the style to be used to format or parse dates. Valid values * are default, short, medium, * long, and full. * An invalid value will cause a {@link ConverterException} when * getAsObject() or getAsString() is called.

* * @param dateStyle The new style code */ public void setDateStyle(String dateStyle) { clearInitialState(); this.dateStyle = dateStyle; } /** *

Return the Locale to be used when parsing or formatting * dates and times. If not explicitly set, the Locale stored * in the {@link javax.faces.component.UIViewRoot} for the current * request is returned.

* * @return the {@code Locale} */ public Locale getLocale() { if (this.locale == null) { this.locale = getLocale(FacesContext.getCurrentInstance()); } return (this.locale); } /** *

Set the Locale to be used when parsing or formatting * dates and times. If set to null, the Locale * stored in the {@link javax.faces.component.UIViewRoot} for the current * request will be utilized.

* * @param locale The new Locale (or null) */ public void setLocale(Locale locale) { clearInitialState(); this.locale = locale; } /** *

Return the format pattern to be used when formatting and * parsing dates and times.

* * @return the pattern */ public String getPattern() { return (this.pattern); } /** *

Set the format pattern to be used when formatting and parsing * dates and times. Valid values are those supported by * java.text.SimpleDateFormat. * An invalid value will cause a {@link ConverterException} when * getAsObject() or getAsString() is called.

* * @param pattern The new format pattern */ public void setPattern(String pattern) { clearInitialState(); this.pattern = pattern; } /** *

Return the style to be used to format or parse times. If not set, * the default value, default, is returned.

* * @return the time style */ public String getTimeStyle() { return (this.timeStyle); } /** *

Set the style to be used to format or parse times. Valid values * are default, short, medium, * long, and full. * An invalid value will cause a {@link ConverterException} when * getAsObject() or getAsString() is called.

* * @param timeStyle The new style code */ public void setTimeStyle(String timeStyle) { clearInitialState(); this.timeStyle = timeStyle; } /** *

Return the TimeZone used to interpret a time value. * If not explicitly set, the default time zone of GMT * returned.

* * @return the {@code TimeZone} */ public TimeZone getTimeZone() { return (this.timeZone); } /** *

Set the TimeZone used to interpret a time value.

* * @param timeZone The new time zone */ public void setTimeZone(TimeZone timeZone) { clearInitialState(); this.timeZone = timeZone; } /** *

Return the type of value to be formatted or parsed. * If not explicitly set, the default type, date * is returned.

* * @return the type */ public String getType() { return (this.type); } /** *

Set the type of * value to be formatted or parsed. Valid values are * both, date, time {@code localDate}, {@code * localDateTime}, {@code localTime}, {@code offsetTime}, {@code * offsetDateTime}, or {@code zonedDateTime}. The values starting * with "local", "offset" and "zoned" correspond to Java SE 8 Date * Time API classes in package java.time with the name * derived by upper casing the first letter. For example, * java.time.LocalDate for the value * "localDate". An invalid value will cause a {@link * ConverterException} when getAsObject() or * getAsString() is called.

* * @param type The new date style */ public void setType(String type) { clearInitialState(); this.type = type; } // ------------------------------------------------------- Converter Methods /** * @throws ConverterException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { if (context == null || component == null) { throw new NullPointerException(); } Object returnValue = null; FormatWrapper parser = null; try { // If the specified value is null or zero-length, return null if (value == null) { return (null); } value = value.trim(); if (value.length() < 1) { return (null); } // Identify the Locale to use for parsing Locale locale = getLocale(context); // Create and configure the parser to be used parser = getDateFormat(locale); if (null != timeZone) { parser.setTimeZone(timeZone); } // Perform the requested parsing returnValue = parser.parse(value); } catch (ParseException | DateTimeParseException e) { if (null != type) { switch (type) { case "date": case "localDate": throw new ConverterException(MessageFactory.getMessage( context, DATE_ID, value, parser.formatNow(), MessageFactory.getLabel(context, component)), e); case "time": case "localTime": case "offsetTime": throw new ConverterException(MessageFactory.getMessage( context, TIME_ID, value, parser.formatNow(), MessageFactory.getLabel(context, component)), e); case "both": case "localDateTime": case "offsetDateTime": case "zonedDateTime": throw new ConverterException(MessageFactory.getMessage( context, DATETIME_ID, value, parser.formatNow(), MessageFactory.getLabel(context, component)), e); } } } catch (Exception e) { throw new ConverterException(e); } return returnValue; } private static class FormatWrapper { private final DateFormat df; private final DateTimeFormatter dtf; private final TemporalQuery from; private FormatWrapper(DateFormat wrapped) { this.df = wrapped; this.dtf = null; this.from = null; } private FormatWrapper(DateTimeFormatter dtf, TemporalQuery from) { this.df = null; this.dtf = dtf; this.from = from; } private Object parse(CharSequence text) throws ParseException { Object result = (null != df) ? df.parse((String) text) : dtf.parse(text, from); return result; } private String format(Object obj) { return (null != df) ? df.format(obj) : dtf.format((TemporalAccessor) obj); } private String formatNow() { return (null != df) ? df.format(new Date()) : dtf.format(ZonedDateTime.now()); } private void setTimeZone(TimeZone zone) { if (null != df) { df.setTimeZone(zone); } } } /** * @throws ConverterException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ @Override public String getAsString(FacesContext context, UIComponent component, Object value) { if (context == null || component == null) { throw new NullPointerException(); } try { // If the specified value is null, return a zero-length String if (value == null) { return ""; } // If the incoming value is still a string, play nice // and return the value unmodified if (value instanceof String) { return (String) value; } // Identify the Locale to use for formatting Locale locale = getLocale(context); // Create and configure the formatter to be used FormatWrapper formatter = getDateFormat(locale); if (null != timeZone) { formatter.setTimeZone(timeZone); } // Perform the requested formatting return (formatter.format(value)); } catch (ConverterException e) { throw new ConverterException(MessageFactory.getMessage( context, STRING_ID, value, MessageFactory.getLabel(context, component)), e); } catch (Exception e) { throw new ConverterException(MessageFactory.getMessage( context, STRING_ID, value, MessageFactory.getLabel(context, component)), e); } } // --------------------------------------------------------- Private Methods /** *

Return a DateFormat instance to use for formatting * and parsing in this {@link Converter}.

* * @param locale The Locale used to select formatting * and parsing conventions * @throws ConverterException if no instance can be created */ private FormatWrapper getDateFormat(Locale locale) { // PENDING(craigmcc) - Implement pooling if needed for performance? if (pattern == null && type == null) { throw new IllegalArgumentException("Either pattern or type must" + " be specified."); } DateFormat df = null; DateTimeFormatter dtf = null; TemporalQuery from = null; if (pattern != null && !isJavaTimeType(type)) { df = new SimpleDateFormat(pattern, locale); } else if (type.equals("both")) { df = DateFormat.getDateTimeInstance (getStyle(dateStyle), getStyle(timeStyle), locale); } else if (type.equals("date")) { df = DateFormat.getDateInstance(getStyle(dateStyle), locale); } else if (type.equals("time")) { df = DateFormat.getTimeInstance(getStyle(timeStyle), locale); } else if (type.equals("localDate")) { if (null != pattern){ dtf = DateTimeFormatter.ofPattern(pattern, locale); } else { dtf = DateTimeFormatter.ofLocalizedDate(getFormatStyle(dateStyle)).withLocale(locale); } from = LocalDate::from; } else if (type.equals("localDateTime")) { if (null != pattern){ dtf = DateTimeFormatter.ofPattern(pattern, locale); } else { dtf = DateTimeFormatter.ofLocalizedDateTime(getFormatStyle(dateStyle), getFormatStyle(timeStyle)).withLocale(locale); } from = LocalDateTime::from; } else if (type.equals("localTime")) { if (null != pattern){ dtf = DateTimeFormatter.ofPattern(pattern, locale); } else { dtf = DateTimeFormatter.ofLocalizedTime(getFormatStyle(timeStyle)).withLocale(locale); } from = LocalTime::from; } else if (type.equals("offsetTime")) { if (null != pattern){ dtf = DateTimeFormatter.ofPattern(pattern, locale); } else { dtf = DateTimeFormatter.ISO_OFFSET_TIME.withLocale(locale); } from = OffsetTime::from; } else if (type.equals("offsetDateTime")) { if (null != pattern){ dtf = DateTimeFormatter.ofPattern(pattern, locale); } else { dtf = DateTimeFormatter.ISO_OFFSET_DATE_TIME.withLocale(locale); } from = OffsetDateTime::from; } else if (type.equals("zonedDateTime")) { if (null != pattern){ dtf = DateTimeFormatter.ofPattern(pattern, locale); } else { dtf = DateTimeFormatter.ISO_ZONED_DATE_TIME.withLocale(locale); } from = ZonedDateTime::from; } else { // PENDING(craigmcc) - i18n throw new IllegalArgumentException("Invalid type: " + type); } if (null != df) { df.setLenient(false); return new FormatWrapper(df); } else if (null != dtf) { return new FormatWrapper(dtf, from); } // PENDING(craigmcc) - i18n throw new IllegalArgumentException("Invalid type: " + type); } private static boolean isJavaTimeType(String type) { boolean result = false; if (null != type && type.length() > 1) { char c = type.charAt(0); result = c == 'l' || c == 'o' || c == 'z'; } return result; } /** *

Return the Locale we will use for localizing our * formatting and parsing processing.

* * @param context The {@link FacesContext} for the current request */ private Locale getLocale(FacesContext context) { // PENDING(craigmcc) - JSTL localization context? Locale locale = this.locale; if (locale == null) { locale = context.getViewRoot().getLocale(); } return (locale); } /** *

Return the style constant for the specified style name.

* * @param name Name of the style for which to return a constant * @throws ConverterException if the style name is not valid */ private static int getStyle(String name) { if (null != name) { switch (name) { case "default": return (DateFormat.DEFAULT); case "short": return (DateFormat.SHORT); case "medium": return (DateFormat.MEDIUM); case "long": return (DateFormat.LONG); case "full": return (DateFormat.FULL); } } // PENDING(craigmcc) - i18n throw new ConverterException("Invalid style '" + name + '\''); } private static FormatStyle getFormatStyle(String name) { if (null != name) { switch (name) { case "default": case "medium": return (FormatStyle.MEDIUM); case "short": return (FormatStyle.SHORT); case "long": return (FormatStyle.LONG); case "full": return (FormatStyle.FULL); } } // PENDING(craigmcc) - i18n throw new ConverterException("Invalid style '" + name + '\''); } // ----------------------------------------------------- StateHolder Methods @Override public Object saveState(FacesContext context) { if (context == null) { throw new NullPointerException(); } 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; } @Override public void restoreState(FacesContext context, Object state) { if (context == null) { throw new NullPointerException(); } 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]; } } private boolean transientFlag; @Override public boolean isTransient() { return (transientFlag); } @Override public void setTransient(boolean transientFlag) { this.transientFlag = transientFlag; } private boolean initialState; @Override public void markInitialState() { initialState = true; } @Override public boolean initialStateMarked() { return initialState; } @Override public void clearInitialState() { initialState = false; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy