From 4b76cdd1a3c0ba635d7735eef59787d24f55056f Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Tue, 10 Nov 2009 20:34:59 +0000 Subject: [PATCH] date time format code review --- .../format/annotation/DateTimeFormat.java | 53 ++++++++-- .../format/annotation/ISODateTimeFormat.java | 58 ----------- ...actDateTimeAnnotationFormatterFactory.java | 97 ------------------- ...eTimeFormatAnnotationFormatterFactory.java | 82 +++++++++++++++- ...eTimeFormatAnnotationFormatterFactory.java | 41 -------- .../joda/JodaTimeFormattingConfigurer.java | 1 - .../joda/JodaTimeFormattingTests.java | 57 ++++++----- .../web/servlet/config/MvcNamespaceTests.java | 6 +- 8 files changed, 154 insertions(+), 241 deletions(-) delete mode 100644 org.springframework.context/src/main/java/org/springframework/format/annotation/ISODateTimeFormat.java delete mode 100644 org.springframework.context/src/main/java/org/springframework/format/datetime/joda/AbstractDateTimeAnnotationFormatterFactory.java delete mode 100644 org.springframework.context/src/main/java/org/springframework/format/datetime/joda/ISODateTimeFormatAnnotationFormatterFactory.java diff --git a/org.springframework.context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java b/org.springframework.context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java index 01280a07b5a..e216d82fceb 100644 --- a/org.springframework.context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java +++ b/org.springframework.context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java @@ -22,18 +22,22 @@ import java.lang.annotation.Target; /** * Declares that a field should be formatted as a date time. - * Supports formatting by style pattern or by format pattern string. + * Supports formatting by style pattern, custom format pattern string, or ISO date time pattern. * Can be applied to java.util.Date, java.util.Calendar, java.long.Long, or Joda Time fields. *

- * For style-based formatting, set the style attribute to be the style pattern. - * The first character is the date style, and the second character is the time style. + * For style-based formatting, set the {@link #style()} attribute to be the style pattern code. + * The first character of the code is the date style, and the second character is the time style. * Specify a character of 'S' for short style, 'M' for medium, 'L' for long, and 'F' for full. * A date or time may be omitted by specifying the style character '-'. *

- * For pattern-based formatting, set the pattern attribute to be the DateTime pattern, such as yyyy/mm/dd h:mm:ss a. - * If the pattern attribute is specified, it takes precedence over the style attribute. + * For pattern-based formatting, set the {@link #pattern()} attribute to be the DateTime pattern, such as yyyy/mm/dd h:mm:ss a. *

- * If no annotation attributes are specified, the default format applied is style-based with a style code of 'SS' (short date, short time). + * For ISO-based formatting, set the {@link #iso()} attribute to be the desired {@link ISO} format, such as {@link ISO#DATE}. + *

+ * Each attribute is mutually exclusive, so only set one attribute per annotation instance (the one most convenient one for your formatting needs). + * When the pattern attribute is specified, it takes precedence over both the style and ISO attribute. + * When the iso attribute is specified, if takes precedence over the style attribute. + * When no annotation attributes are specified, the default format applied is style-based with a style code of 'SS' (short date, short time). * * @author Keith Donald * @since 3.0 @@ -50,9 +54,44 @@ public @interface DateTimeFormat { String style() default "SS"; /** - * A pattern String to apply to format the field. * Set this attribute or the style attribute, not both. */ String pattern() default ""; + + /** + * A ISO pattern to apply to format the field. + * The possible ISO patterns are defined in the {@link ISO} enum. + * Defaults to ISO.NONE, indicating this attribute should be ignored. + */ + ISO iso() default ISO.NONE; + + /** + * Common ISO date time format patterns. + * @author Keith Donald + * @since 3.0 + */ + public enum ISO { + /** + * The most common ISO Date Format yyyy-MM-dd e.g. 2000-10-31. + */ + DATE, + + /** + * The most common ISO Time Format hh:mm:ss.SSSZ e.g. 01:30:00.000-05:00. + */ + TIME, + + /** + * The most common ISO DateTime Format yyyy-MM-dd'T'hh:mm:ss.SSSZ e.g. 2000-10-31 01:30:00.000-05:00. + * The default if no annotation value is specified. + */ + DATE_TIME, + + /** + * Indicates that no ISO-based format pattern should be applied. + */ + NONE + + } } diff --git a/org.springframework.context/src/main/java/org/springframework/format/annotation/ISODateTimeFormat.java b/org.springframework.context/src/main/java/org/springframework/format/annotation/ISODateTimeFormat.java deleted file mode 100644 index 02e92070181..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/format/annotation/ISODateTimeFormat.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2002-2009 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.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Declares that a field should be formatted as a ISO date time. - * Can be applied to java.util.Date, java.util.Calendar, java.long.Long, or Joda Time fields. - * @author Keith Donald - * @since 3.0 - */ -@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) -@Retention(RetentionPolicy.RUNTIME) -public @interface ISODateTimeFormat { - - /** - * The ISO style to use to format the date time. - * Defaults to {@link ISO#DATE_TIME}. - */ - ISO value() default ISO.DATE_TIME; - - public enum ISO { - - /** - * The most common ISO Date Format yyyy-MM-dd e.g. 2000-10-31. - */ - DATE, - - /** - * The most common ISO Time Format hh:mm:ss.SSSZ e.g. 01:30:00.000-05:00. - */ - TIME, - - /** - * The most common ISO DateTime Format yyyy-MM-dd'T'hh:mm:ss.SSSZ e.g. 2000-10-31 01:30:00.000-05:00. - * The default if no annotation value is specified. - */ - DATE_TIME - } - -} diff --git a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/AbstractDateTimeAnnotationFormatterFactory.java b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/AbstractDateTimeAnnotationFormatterFactory.java deleted file mode 100644 index e2f3ead18e2..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/AbstractDateTimeAnnotationFormatterFactory.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2002-2009 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.joda; - -import java.lang.annotation.Annotation; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; - -import org.joda.time.DateMidnight; -import org.joda.time.DateTime; -import org.joda.time.LocalDate; -import org.joda.time.LocalDateTime; -import org.joda.time.LocalTime; -import org.joda.time.ReadableInstant; -import org.joda.time.ReadablePartial; -import org.joda.time.format.DateTimeFormatter; -import org.springframework.format.AnnotationFormatterFactory; -import org.springframework.format.Parser; -import org.springframework.format.Printer; - -/** - * Base class for annotation-based Joda DateTime formatters. - * Can format any Joda {@link ReadableInstant} or {@link ReadablePartial} property, as well as standard {@link Date}, {@link Calendar}, and Long properties. - * @author Keith Donald - * @since 3.0 - * @param the format annotation parameter type to be declared by subclasses - */ -abstract class AbstractDateTimeAnnotationFormatterFactory implements AnnotationFormatterFactory { - - private final Set> fieldTypes; - - public AbstractDateTimeAnnotationFormatterFactory() { - this.fieldTypes = Collections.unmodifiableSet(createFieldTypes()); - } - - public Set> getFieldTypes() { - return this.fieldTypes; - } - - public Printer getPrinter(A annotation, Class fieldType) { - DateTimeFormatter formatter = configureDateTimeFormatterFrom(annotation); - if (ReadableInstant.class.isAssignableFrom(fieldType)) { - return new ReadableInstantPrinter(formatter); - } else if (ReadablePartial.class.isAssignableFrom(fieldType)) { - return new ReadablePartialPrinter(formatter); - } else if (Calendar.class.isAssignableFrom(fieldType)) { - // assumes Calendar->ReadableInstant converter is registered - return new ReadableInstantPrinter(formatter); - } else { - // assumes Date->Long converter is registered - return new MillisecondInstantPrinter(formatter); - } - } - - public Parser getParser(A annotation, Class propertyType) { - return new DateTimeParser(configureDateTimeFormatterFrom(annotation)); - } - - /** - * Hook method subclasses should override to configure the Joda {@link DateTimeFormatter} from the annotation values. - * @param annotation the annotation containing formatter configuration rules - * @return the configured date time formatter - */ - protected abstract DateTimeFormatter configureDateTimeFormatterFrom(A annotation); - - // internal helpers - - private Set> createFieldTypes() { - Set> fieldTypes = new HashSet>(8); - fieldTypes.add(LocalDate.class); - fieldTypes.add(LocalTime.class); - fieldTypes.add(LocalDateTime.class); - fieldTypes.add(DateTime.class); - fieldTypes.add(DateMidnight.class); - fieldTypes.add(Date.class); - fieldTypes.add(Calendar.class); - fieldTypes.add(Long.class); - return fieldTypes; - } - -} diff --git a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/DateTimeFormatAnnotationFormatterFactory.java b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/DateTimeFormatAnnotationFormatterFactory.java index 3e6b42b7384..4c12e8f7871 100644 --- a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/DateTimeFormatAnnotationFormatterFactory.java +++ b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/DateTimeFormatAnnotationFormatterFactory.java @@ -15,8 +15,25 @@ */ package org.springframework.format.datetime.joda; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import org.joda.time.DateMidnight; +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.LocalDateTime; +import org.joda.time.LocalTime; +import org.joda.time.ReadableInstant; +import org.joda.time.ReadablePartial; import org.joda.time.format.DateTimeFormatter; +import org.springframework.format.AnnotationFormatterFactory; +import org.springframework.format.Parser; +import org.springframework.format.Printer; import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.format.annotation.DateTimeFormat.ISO; /** * Formats fields annotated with the {@link DateTimeFormat} annotation. @@ -24,12 +41,57 @@ import org.springframework.format.annotation.DateTimeFormat; * @since 3.0 * @see DateTimeFormat */ -public final class DateTimeFormatAnnotationFormatterFactory extends AbstractDateTimeAnnotationFormatterFactory { +public final class DateTimeFormatAnnotationFormatterFactory implements AnnotationFormatterFactory { - protected DateTimeFormatter configureDateTimeFormatterFrom(DateTimeFormat annotation) { - String pattern = annotation.pattern(); - if (!pattern.isEmpty()) { - return forPattern(pattern); + private final Set> fieldTypes; + + public DateTimeFormatAnnotationFormatterFactory() { + this.fieldTypes = Collections.unmodifiableSet(createFieldTypes()); + } + + public Set> getFieldTypes() { + return this.fieldTypes; + } + + public Printer getPrinter(DateTimeFormat annotation, Class fieldType) { + DateTimeFormatter formatter = configureDateTimeFormatterFrom(annotation); + if (ReadableInstant.class.isAssignableFrom(fieldType)) { + return new ReadableInstantPrinter(formatter); + } else if (ReadablePartial.class.isAssignableFrom(fieldType)) { + return new ReadablePartialPrinter(formatter); + } else if (Calendar.class.isAssignableFrom(fieldType)) { + // assumes Calendar->ReadableInstant converter is registered + return new ReadableInstantPrinter(formatter); + } else { + // assumes Date->Long converter is registered + return new MillisecondInstantPrinter(formatter); + } + } + + public Parser getParser(DateTimeFormat annotation, Class fieldType) { + return new DateTimeParser(configureDateTimeFormatterFrom(annotation)); + } + + // internal helpers + + private Set> createFieldTypes() { + Set> fieldTypes = new HashSet>(8); + fieldTypes.add(LocalDate.class); + fieldTypes.add(LocalTime.class); + fieldTypes.add(LocalDateTime.class); + fieldTypes.add(DateTime.class); + fieldTypes.add(DateMidnight.class); + fieldTypes.add(Date.class); + fieldTypes.add(Calendar.class); + fieldTypes.add(Long.class); + return fieldTypes; + } + + private DateTimeFormatter configureDateTimeFormatterFrom(DateTimeFormat annotation) { + if (!annotation.pattern().isEmpty()) { + return forPattern(annotation.pattern()); + } else if (annotation.iso() != ISO.NONE) { + return forISO(annotation.iso()); } else { return forStyle(annotation.style()); } @@ -38,6 +100,16 @@ public final class DateTimeFormatAnnotationFormatterFactory extends AbstractDate private DateTimeFormatter forPattern(String pattern) { return org.joda.time.format.DateTimeFormat.forPattern(pattern); } + + private DateTimeFormatter forISO(ISO iso) { + if (iso == ISO.DATE) { + return org.joda.time.format.ISODateTimeFormat.date(); + } else if (iso == ISO.TIME) { + return org.joda.time.format.ISODateTimeFormat.time(); + } else { + return org.joda.time.format.ISODateTimeFormat.dateTime(); + } + } private DateTimeFormatter forStyle(String style) { return org.joda.time.format.DateTimeFormat.forStyle(style); diff --git a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/ISODateTimeFormatAnnotationFormatterFactory.java b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/ISODateTimeFormatAnnotationFormatterFactory.java deleted file mode 100644 index afc48045547..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/ISODateTimeFormatAnnotationFormatterFactory.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2009 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.joda; - -import org.joda.time.format.DateTimeFormatter; -import org.springframework.format.annotation.ISODateTimeFormat; -import org.springframework.format.annotation.ISODateTimeFormat.ISO; - -/** - * Formats fields annotated with the {@link ISODateTimeFormat} annotation. - * @author Keith Donald - * @since 3.0 - * @see ISODateTimeFormat - */ -public final class ISODateTimeFormatAnnotationFormatterFactory extends AbstractDateTimeAnnotationFormatterFactory { - - protected DateTimeFormatter configureDateTimeFormatterFrom(ISODateTimeFormat annotation) { - ISO format = annotation.value(); - if (format == ISO.DATE) { - return org.joda.time.format.ISODateTimeFormat.date(); - } else if (format == ISO.TIME) { - return org.joda.time.format.ISODateTimeFormat.time(); - } else { - return org.joda.time.format.ISODateTimeFormat.dateTime(); - } - } - -} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaTimeFormattingConfigurer.java b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaTimeFormattingConfigurer.java index be534611ebd..9e698e3cc88 100644 --- a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaTimeFormattingConfigurer.java +++ b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaTimeFormattingConfigurer.java @@ -109,7 +109,6 @@ public class JodaTimeFormattingConfigurer { formatterRegistry.addFormatterForFieldType(Date.class, new MillisecondInstantPrinter(jodaDateTimeFormatter), dateTimeParser); formatterRegistry.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory()); - formatterRegistry.addFormatterForFieldAnnotation(new ISODateTimeFormatAnnotationFormatterFactory()); } // internal helpers diff --git a/org.springframework.context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java b/org.springframework.context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java index 634995904b0..a72f825c607 100644 --- a/org.springframework.context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java +++ b/org.springframework.context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java @@ -17,8 +17,7 @@ import org.junit.Test; import org.springframework.beans.MutablePropertyValues; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.format.annotation.ISODateTimeFormat; -import org.springframework.format.annotation.ISODateTimeFormat.ISO; +import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.format.support.FormattingConversionService; import org.springframework.validation.DataBinder; @@ -137,6 +136,15 @@ public class JodaTimeFormattingTests { assertEquals("Oct 31, 2009 12:00 PM", binder.getBindingResult().getFieldValue("dateTimeAnnotated")); } + @Test + public void testBindDateTimeAnnotatedPattern() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.addPropertyValue("dateTimeAnnotatedPattern", "10/31/09 12:00 PM"); + binder.bind(propertyValues); + assertEquals(0, binder.getBindingResult().getErrorCount()); + assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("dateTimeAnnotatedPattern")); + } + @Test public void testBindDateTimeAnnotatedDefault() { MutablePropertyValues propertyValues = new MutablePropertyValues(); @@ -227,15 +235,6 @@ public class JodaTimeFormattingTests { assertEquals("2009-10-31T07:00:00.000-05:00", binder.getBindingResult().getFieldValue("isoDateTime")); } - @Test - public void testBindISODateTimeDefault() { - MutablePropertyValues propertyValues = new MutablePropertyValues(); - propertyValues.addPropertyValue("isoDateTimeDefault", "2009-10-31T12:00:00.000Z"); - binder.bind(propertyValues); - assertEquals(0, binder.getBindingResult().getErrorCount()); - assertEquals("2009-10-31T07:00:00.000-05:00", binder.getBindingResult().getFieldValue("isoDateTimeDefault")); - } - public static class JodaTimeBean { private LocalDate localDate; @@ -258,9 +257,6 @@ public class JodaTimeFormattingTests { @DateTimeFormat(style="MS") private DateTime dateTimeAnnotated; - @DateTimeFormat - private DateTime dateTimeAnnotatedDefault; - private Date date; @DateTimeFormat(style="S-") @@ -273,21 +269,24 @@ public class JodaTimeFormattingTests { private Long millis; + @DateTimeFormat + private DateTime dateTimeAnnotatedDefault; + @DateTimeFormat(style="S-") private Long millisAnnotated; - @ISODateTimeFormat(ISO.DATE) + @DateTimeFormat(pattern="M/d/yy h:mm a") + private DateTime dateTimeAnnotatedPattern; + + @DateTimeFormat(iso=ISO.DATE) private LocalDate isoDate; - @ISODateTimeFormat(ISO.TIME) + @DateTimeFormat(iso=ISO.TIME) private LocalTime isoTime; - @ISODateTimeFormat(ISO.DATE_TIME) + @DateTimeFormat(iso=ISO.DATE_TIME) private DateTime isoDateTime; - @ISODateTimeFormat - private DateTime isoDateTimeDefault; - public LocalDate getLocalDate() { return localDate; } @@ -352,6 +351,14 @@ public class JodaTimeFormattingTests { this.dateTimeAnnotated = dateTimeAnnotated; } + public DateTime getDateTimeAnnotatedPattern() { + return dateTimeAnnotatedPattern; + } + + public void setDateTimeAnnotatedPattern(DateTime dateTimeAnnotatedPattern) { + this.dateTimeAnnotatedPattern = dateTimeAnnotatedPattern; + } + public DateTime getDateTimeAnnotatedDefault() { return dateTimeAnnotatedDefault; } @@ -431,14 +438,6 @@ public class JodaTimeFormattingTests { public void setIsoDateTime(DateTime isoDateTime) { this.isoDateTime = isoDateTime; } - - public DateTime getIsoDateTimeDefault() { - return isoDateTimeDefault; - } - - public void setIsoDateTimeDefault(DateTime isoDateTimeDefault) { - this.isoDateTimeDefault = isoDateTimeDefault; - } - + } } \ No newline at end of file diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java index 0268f5f24ad..68db7132d2f 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java @@ -11,8 +11,8 @@ import org.junit.Test; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.core.io.ClassPathResource; -import org.springframework.format.annotation.ISODateTimeFormat; -import org.springframework.format.annotation.ISODateTimeFormat.ISO; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletContext; @@ -58,7 +58,7 @@ public class MvcNamespaceTests { public static class TestController { @RequestMapping - public void testBind(@RequestParam @ISODateTimeFormat(ISO.DATE) Date date) { + public void testBind(@RequestParam @DateTimeFormat(iso=ISO.DATE) Date date) { } }