package com.fasterxml.jackson.databind.deser.std;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.sql.Timestamp;
import java.text.*;
import java.util.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.StdDateFormat;
/**
* Container class for core JDK date/time type deserializers.
*/
@SuppressWarnings("serial")
public class DateDeserializers
{
private final static HashSet<String> _classNames = new HashSet<String>();
static {
Class<?>[] numberTypes = new Class<?>[] {
Calendar.class,
GregorianCalendar.class,
java.sql.Date.class,
java.util.Date.class,
Timestamp.class,
};
for (Class<?> cls : numberTypes) {
_classNames.add(cls.getName());
}
}
public static JsonDeserializer<?> find(Class<?> rawType, String clsName)
{
if (_classNames.contains(clsName)) {
// Start with the most common type
if (rawType == Calendar.class) {
return new CalendarDeserializer();
}
if (rawType == java.util.Date.class) {
return DateDeserializer.instance;
}
if (rawType == java.sql.Date.class) {
return new SqlDateDeserializer();
}
if (rawType == Timestamp.class) {
return new TimestampDeserializer();
}
if (rawType == GregorianCalendar.class) {
return new CalendarDeserializer(GregorianCalendar.class);
}
}
return null;
}
/*
/**********************************************************
/* Intermediate class for Date-based ones
/**********************************************************
*/
protected abstract static class DateBasedDeserializer<T>
extends StdScalarDeserializer<T>
implements ContextualDeserializer
{
/**
* Specific format to use, if non-null; if null will
* just use default format.
*/
protected final DateFormat _customFormat;
/**
* Let's also keep format String for reference, to use for error messages
*/
protected final String _formatString;
protected DateBasedDeserializer(Class<?> clz) {
super(clz);
_customFormat = null;
_formatString = null;
}
protected DateBasedDeserializer(DateBasedDeserializer<T> base,
DateFormat format, String formatStr) {
super(base._valueClass);
_customFormat = format;
_formatString = formatStr;
}
protected abstract DateBasedDeserializer<T> withDateFormat(DateFormat df, String formatStr);
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
BeanProperty property)
throws JsonMappingException
{
final JsonFormat.Value format = findFormatOverrides(ctxt, property,
handledType());
if (format != null) {
TimeZone tz = format.getTimeZone();
final Boolean lenient = format.getLenient();
// First: fully custom pattern?
if (format.hasPattern()) {
final String pattern = format.getPattern();
final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale();
SimpleDateFormat df = new SimpleDateFormat(pattern, loc);
if (tz == null) {
tz = ctxt.getTimeZone();
}
df.setTimeZone(tz);
if (lenient != null) {
df.setLenient(lenient);
}
return withDateFormat(df, pattern);
}
// But if not, can still override timezone
if (tz != null) {
DateFormat df = ctxt.getConfig().getDateFormat();
// one shortcut: with our custom format, can simplify handling a bit
if (df.getClass() == StdDateFormat.class) {
final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale();
StdDateFormat std = (StdDateFormat) df;
std = std.withTimeZone(tz);
std = std.withLocale(loc);
if (lenient != null) {
std = std.withLenient(lenient);
}
df = std;
} else {
// otherwise need to clone, re-set timezone:
df = (DateFormat) df.clone();
df.setTimeZone(tz);
if (lenient != null) {
df.setLenient(lenient);
}
}
return withDateFormat(df, _formatString);
}
// or maybe even just leniency?
if (lenient != null) {
DateFormat df = ctxt.getConfig().getDateFormat();
String pattern = _formatString;
// one shortcut: with our custom format, can simplify handling a bit
if (df.getClass() == StdDateFormat.class) {
StdDateFormat std = (StdDateFormat) df;
std = std.withLenient(lenient);
df = std;
pattern = std.toPattern();
} else {
// otherwise need to clone,
df = (DateFormat) df.clone();
df.setLenient(lenient);
if (df instanceof SimpleDateFormat) {
((SimpleDateFormat) df).toPattern();
}
}
if (pattern == null) {
pattern = "[unknown]";
}
return withDateFormat(df, pattern);
}
}
return this;
}
@Override
protected java.util.Date _parseDate(JsonParser p, DeserializationContext ctxt)
throws IOException
{
if (_customFormat != null) {
if (p.hasToken(JsonToken.VALUE_STRING)) {
String str = p.getText().trim();
if (str.length() == 0) {
return (Date) getEmptyValue(ctxt);
}
synchronized (_customFormat) {
try {
return _customFormat.parse(str);
} catch (ParseException e) {
return (java.util.Date) ctxt.handleWeirdStringValue(handledType(), str,
"expected format \"%s\"", _formatString);
}
}
}
}
return super._parseDate(p, ctxt);
}
}
/*
/**********************************************************
/* Deserializer implementations for Date types
/**********************************************************
*/
@JacksonStdImpl
public static class CalendarDeserializer extends DateBasedDeserializer<Calendar>
{
/**
* We may know actual expected type; if so, it will be
* used for instantiation.
*
* @since 2.9
*/
protected final Constructor<Calendar> _defaultCtor;
public CalendarDeserializer() {
super(Calendar.class);
_defaultCtor = null;
}
@SuppressWarnings("unchecked")
public CalendarDeserializer(Class<? extends Calendar> cc) {
super(cc);
_defaultCtor = (Constructor<Calendar>) ClassUtil.findConstructor(cc, false);
}
public CalendarDeserializer(CalendarDeserializer src, DateFormat df, String formatString) {
super(src, df, formatString);
_defaultCtor = src._defaultCtor;
}
@Override
protected CalendarDeserializer withDateFormat(DateFormat df, String formatString) {
return new CalendarDeserializer(this, df, formatString);
}
@Override
public Calendar deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
Date d = _parseDate(p, ctxt);
if (d == null) {
return null;
}
if (_defaultCtor == null) {
return ctxt.constructCalendar(d);
}
try {
Calendar c = _defaultCtor.newInstance();
c.setTimeInMillis(d.getTime());
TimeZone tz = ctxt.getTimeZone();
if (tz != null) {
c.setTimeZone(tz);
}
return c;
} catch (Exception e) {
return (Calendar) ctxt.handleInstantiationProblem(handledType(), d, e);
}
}
}
/**
* Simple deserializer for handling {@link java.util.Date} values.
*<p>
* One way to customize Date formats accepted is to override method
* {@link DeserializationContext#parseDate} that this basic
* deserializer calls.
*/
@JacksonStdImpl
public static class DateDeserializer extends DateBasedDeserializer<Date>
{
public final static DateDeserializer instance = new DateDeserializer();
public DateDeserializer() { super(Date.class); }
public DateDeserializer(DateDeserializer base, DateFormat df, String formatString) {
super(base, df, formatString);
}
@Override
protected DateDeserializer withDateFormat(DateFormat df, String formatString) {
return new DateDeserializer(this, df, formatString);
}
@Override
public java.util.Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return _parseDate(p, ctxt);
}
}
/**
* Compared to plain old {@link java.util.Date}, SQL version is easier
* to deal with: mostly because it is more limited.
*/
public static class SqlDateDeserializer
extends DateBasedDeserializer<java.sql.Date>
{
public SqlDateDeserializer() { super(java.sql.Date.class); }
public SqlDateDeserializer(SqlDateDeserializer src, DateFormat df, String formatString) {
super(src, df, formatString);
}
@Override
protected SqlDateDeserializer withDateFormat(DateFormat df, String formatString) {
return new SqlDateDeserializer(this, df, formatString);
}
@Override
public java.sql.Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
Date d = _parseDate(p, ctxt);
return (d == null) ? null : new java.sql.Date(d.getTime());
}
}
/**
* Simple deserializer for handling {@link java.sql.Timestamp} values.
*<p>
* One way to customize Timestamp formats accepted is to override method
* {@link DeserializationContext#parseDate} that this basic
* deserializer calls.
*/
public static class TimestampDeserializer extends DateBasedDeserializer<Timestamp>
{
public TimestampDeserializer() { super(Timestamp.class); }
public TimestampDeserializer(TimestampDeserializer src, DateFormat df, String formatString) {
super(src, df, formatString);
}
@Override
protected TimestampDeserializer withDateFormat(DateFormat df, String formatString) {
return new TimestampDeserializer(this, df, formatString);
}
@Override
public java.sql.Timestamp deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
Date d = _parseDate(p, ctxt);
return (d == null) ? null : new Timestamp(d.getTime());
}
}
}