Skip to content

Commit

Permalink
Support DateTimeFormat annotation without Joda
Browse files Browse the repository at this point in the history
Dependency on Joda Time when using the @DateTimeFormat annotation is
now optional. If Joda Time is not present the JDK SimpleDateFormat
will be used to parse and print date patterns. If Joda time is
present it will always be used in preference to SimpleDateFormat.

Issue: SPR-6508
  • Loading branch information
philwebb committed Nov 3, 2012
1 parent ddddec8 commit a26059f
Show file tree
Hide file tree
Showing 9 changed files with 806 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,27 +19,49 @@
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import org.springframework.format.Formatter;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
* A formatter for {@link java.util.Date} types.
* Allows the configuration of an explicit date pattern and locale.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Phillip Webb
* @since 3.0
* @see SimpleDateFormat
* @see SimpleDateFormat
*/
public class DateFormatter implements Formatter<Date> {

private static final Map<ISO, String> ISO_PATTERNS;
static {
Map<ISO, String> formats = new HashMap<DateTimeFormat.ISO, String>();
formats.put(ISO.DATE, "yyyy-MM-dd");
formats.put(ISO.TIME, "HH:mm:ss.SSSZ");
formats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ss.SSSZ");
ISO_PATTERNS = Collections.unmodifiableMap(formats);
}


private String pattern;

private int style = DateFormat.DEFAULT;

private String stylePattern;

private ISO iso;

private TimeZone timeZone;

private boolean lenient = false;
Expand Down Expand Up @@ -80,6 +102,32 @@ public void setStyle(int style) {
this.style = style;
}

/**
* Set the two character to use to format date values. The first character used for
* the date style, the second is for the time style. Supported characters are
* <ul>
* <li>'S' = Small</li>
* <li>'M' = Medium</li>
* <li>'L' = Long</li>
* <li>'F' = Full</li>
* <li>'-' = Omitted</li>
* <ul>
* This method mimics the styles supported by Joda Time.
* @param stylePattern two characters from the set {"S", "M", "L", "F", "-"}
* @since 3.2
*/
public void setStylePattern(String stylePattern) {
this.stylePattern = stylePattern;
}

/**
* Set the ISO format used for this date.
* @param iso the {@link ISO} format
* @since 3.2
*/
public void setIso(ISO iso) {
this.iso = iso;
}
/**
* Set the TimeZone to normalize the date values into, if any.
*/
Expand Down Expand Up @@ -107,18 +155,53 @@ public Date parse(String text, Locale locale) throws ParseException {


protected DateFormat getDateFormat(Locale locale) {
DateFormat dateFormat;
if (this.pattern != null) {
dateFormat = new SimpleDateFormat(this.pattern, locale);
}
else {
dateFormat = DateFormat.getDateInstance(this.style, locale);
}
DateFormat dateFormat = createDateFormat(locale);
if (this.timeZone != null) {
dateFormat.setTimeZone(this.timeZone);
}
dateFormat.setLenient(this.lenient);
return dateFormat;
}

private DateFormat createDateFormat(Locale locale) {
if (StringUtils.hasLength(this.pattern)) {
return new SimpleDateFormat(this.pattern, locale);
}
if (iso != null && iso != ISO.NONE) {
String pattern = ISO_PATTERNS.get(iso);
Assert.state(pattern != null, "Unsupported ISO format " + iso);
SimpleDateFormat format = new SimpleDateFormat(pattern);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
return format;
}
if(StringUtils.hasLength(stylePattern)) {
int dateStyle = getStylePatternForChar(0);
int timeStyle = getStylePatternForChar(1);
if(dateStyle != -1 && timeStyle != -1) {
return DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
}
if(dateStyle != -1) {
return DateFormat.getDateInstance(dateStyle, locale);
}
if(timeStyle != -1) {
return DateFormat.getTimeInstance(timeStyle, locale);
}
throw new IllegalStateException("Unsupported style pattern '"+ stylePattern+ "'");

}
return DateFormat.getDateInstance(this.style, locale);
}

private int getStylePatternForChar(int index) {
if(stylePattern != null && stylePattern.length() > index) {
switch (stylePattern.charAt(index)) {
case 'S': return DateFormat.SHORT;
case 'M': return DateFormat.MEDIUM;
case 'L': return DateFormat.LONG;
case 'F': return DateFormat.FULL;
case '-': return -1;
}
}
throw new IllegalStateException("Unsupported style pattern '"+ stylePattern+ "'");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed 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 org.springframework.format.datetime;

import java.util.Calendar;
import java.util.Date;

import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.format.FormatterRegistrar;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar;
import org.springframework.util.Assert;

/**
* Configures Date formatting for use with Spring.
* <p>
* Designed for direct instantiation but also exposes the static
* {@link #addDateConverters(ConverterRegistry)} utility method for ad hoc use against any
* {@code ConverterRegistry} instance.
*
* @author Phillip Webb
* @since 3.2
* @see JodaTimeFormatterRegistrar
* @see FormatterRegistrar#registerFormatters
*/
public class DateFormatterRegistrar implements FormatterRegistrar {


private DateFormatter dateFormatter = new DateFormatter();


public void registerFormatters(FormatterRegistry registry) {
addDateConverters(registry);
registry.addFormatter(dateFormatter);
registry.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory());
}

/**
* Set the date formatter to register. If not specified default {@link DateFormatter}
* will be used. This method can be used if additional formatter configuration is
* required.
* @param dateFormatter the date formatter
*/
public void setFormatter(DateFormatter dateFormatter) {
Assert.notNull(dateFormatter,"DateFormatter must not be null");
this.dateFormatter = dateFormatter;
}

/**
* Add date converters to the specified registry.
* @param converterRegistry the registry of converters to add to
*/
public static void addDateConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverter(new DateToLongConverter());
converterRegistry.addConverter(new DateToCalendarConverter());
converterRegistry.addConverter(new CalendarToDateConverter());
converterRegistry.addConverter(new CalendarToLongConverter());
converterRegistry.addConverter(new LongToDateConverter());
converterRegistry.addConverter(new LongToCalendarConverter());
}


private static class DateToLongConverter implements Converter<Date, Long> {
public Long convert(Date source) {
return source.getTime();
}
}


private static class DateToCalendarConverter implements Converter<Date, Calendar> {
public Calendar convert(Date source) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(source);
return calendar;
}
}


private static class CalendarToDateConverter implements Converter<Calendar, Date> {
public Date convert(Calendar source) {
return source.getTime();
}
}


private static class CalendarToLongConverter implements Converter<Calendar, Long> {
public Long convert(Calendar source) {
return source.getTime().getTime();
}
}


private static class LongToDateConverter implements Converter<Long, Date> {
public Date convert(Long source) {
return new Date(source);
}
}


private static class LongToCalendarConverter implements Converter<Long, Calendar> {

private DateToCalendarConverter dateToCalendarConverter = new DateToCalendarConverter();

public Calendar convert(Long source) {
return dateToCalendarConverter.convert(new Date(source));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed 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 org.springframework.format.datetime;

import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Formatter;
import org.springframework.format.Parser;
import org.springframework.format.Printer;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.datetime.joda.JodaDateTimeFormatAnnotationFormatterFactory;
import org.springframework.util.StringValueResolver;

/**
* Formats fields annotated with the {@link DateTimeFormat} annotation.
*
* @author Phillip Webb
* @see JodaDateTimeFormatAnnotationFormatterFactory
* @since 3.2
*/
public class DateTimeFormatAnnotationFormatterFactory implements
AnnotationFormatterFactory<DateTimeFormat>, EmbeddedValueResolverAware {


private static final Set<Class<?>> FIELD_TYPES;
static {
Set<Class<?>> fieldTypes = new HashSet<Class<?>>();
fieldTypes.add(Date.class);
fieldTypes.add(Calendar.class);
fieldTypes.add(Long.class);
FIELD_TYPES = Collections.unmodifiableSet(fieldTypes);
}


private StringValueResolver embeddedValueResolver;


public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}

public Set<Class<?>> getFieldTypes() {
return FIELD_TYPES;
}

public Printer<?> getPrinter(DateTimeFormat annotation, Class<?> fieldType) {
return getFormatter(annotation, fieldType);
}

public Parser<?> getParser(DateTimeFormat annotation, Class<?> fieldType) {
return getFormatter(annotation, fieldType);
}

protected Formatter<Date> getFormatter(DateTimeFormat annotation, Class<?> fieldType) {
DateFormatter formatter = new DateFormatter();
formatter.setStylePattern(resolveEmbeddedValue(annotation.style()));
formatter.setIso(annotation.iso());
formatter.setPattern(resolveEmbeddedValue(annotation.pattern()));
return formatter;
}

protected String resolveEmbeddedValue(String value) {
return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -39,7 +39,7 @@
import org.springframework.util.StringValueResolver;

/**
* Formats fields annotated with the {@link DateTimeFormat} annotation.
* Formats fields annotated with the {@link DateTimeFormat} annotation using Joda time.
*
* @author Keith Donald
* @author Juergen Hoeller
Expand Down
Loading

0 comments on commit a26059f

Please sign in to comment.