From 392accd910623ae7caf58673674fbc4f0cb1be8a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 7 Jun 2010 22:41:21 +0000 Subject: [PATCH] introduced EmbeddedValueResolverAware callback interface for convenient placeholder resolution --- .../context/EmbeddedValueResolverAware.java | 39 ++++++++++ .../ApplicationContextAwareProcessor.java | 21 ++++++ ...eTimeFormatAnnotationFormatterFactory.java | 36 ++++++--- ...umberFormatAnnotationFormatterFactory.java | 54 ++++++++------ .../support/FormattingConversionService.java | 44 +++++++---- .../ScheduledAnnotationBeanPostProcessor.java | 23 ++++-- .../joda/JodaTimeFormattingTests.java | 4 +- .../format/number/NumberFormattingTests.java | 17 ++++- .../FormattingConversionServiceTests.java | 73 ++++++++++++------- 9 files changed, 224 insertions(+), 87 deletions(-) create mode 100644 org.springframework.context/src/main/java/org/springframework/context/EmbeddedValueResolverAware.java diff --git a/org.springframework.context/src/main/java/org/springframework/context/EmbeddedValueResolverAware.java b/org.springframework.context/src/main/java/org/springframework/context/EmbeddedValueResolverAware.java new file mode 100644 index 00000000000..7749230f676 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/context/EmbeddedValueResolverAware.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2010 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.context; + +import org.springframework.util.StringValueResolver; + +/** + * Interface to be implemented by any object that wishes to be notified of a + * StringValueResolver for the resolution of embedded definition values. + * + *

This is an alternative to a full ConfigurableBeanFactory dependency via the + * ApplicationContextAware/BeanFactoryAware interfaces. + * + * @author Juergen Hoeller + * @since 3.0.3 + * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#resolveEmbeddedValue + */ +public interface EmbeddedValueResolverAware { + + /** + * Set the StringValueResolver to use for resolving embedded definition values. + */ + void setEmbeddedValueResolver(StringValueResolver resolver); + +} diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/ApplicationContextAwareProcessor.java b/org.springframework.context/src/main/java/org/springframework/context/support/ApplicationContextAwareProcessor.java index 7e1da6a5e56..d40ca72323d 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/support/ApplicationContextAwareProcessor.java +++ b/org.springframework.context/src/main/java/org/springframework/context/support/ApplicationContextAwareProcessor.java @@ -22,11 +22,14 @@ import java.security.PrivilegedAction; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.context.MessageSourceAware; import org.springframework.context.ResourceLoaderAware; +import org.springframework.util.StringValueResolver; /** * {@link org.springframework.beans.factory.config.BeanPostProcessor} @@ -86,6 +89,10 @@ class ApplicationContextAwareProcessor implements BeanPostProcessor { } private void invokeAwareInterfaces(Object bean) { + if (bean instanceof EmbeddedValueResolverAware) { + ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver( + new EmbeddedValueResolver(this.applicationContext.getBeanFactory())); + } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } @@ -104,4 +111,18 @@ class ApplicationContextAwareProcessor implements BeanPostProcessor { return bean; } + + private static class EmbeddedValueResolver implements StringValueResolver { + + private final ConfigurableBeanFactory beanFactory; + + public EmbeddedValueResolver(ConfigurableBeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + public String resolveStringValue(String strVal) { + return this.beanFactory.resolveEmbeddedValue(strVal); + } + } + } diff --git a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java index 51eeeddae2d..227729ad52b 100644 --- a/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java +++ b/org.springframework.context/src/main/java/org/springframework/format/datetime/joda/JodaDateTimeFormatAnnotationFormatterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -31,12 +31,14 @@ import org.joda.time.ReadableInstant; import org.joda.time.ReadablePartial; import org.joda.time.format.DateTimeFormatter; +import org.springframework.context.EmbeddedValueResolverAware; 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; import org.springframework.util.StringUtils; +import org.springframework.util.StringValueResolver; /** * Formats fields annotated with the {@link DateTimeFormat} annotation. @@ -46,9 +48,12 @@ import org.springframework.util.StringUtils; * @since 3.0 * @see DateTimeFormat */ -public final class JodaDateTimeFormatAnnotationFormatterFactory implements AnnotationFormatterFactory { +public class JodaDateTimeFormatAnnotationFormatterFactory + implements AnnotationFormatterFactory, EmbeddedValueResolverAware { private final Set> fieldTypes; + + private StringValueResolver embeddedValueResolver; public JodaDateTimeFormatAnnotationFormatterFactory() { @@ -64,11 +69,20 @@ public final class JodaDateTimeFormatAnnotationFormatterFactory implements Annot this.fieldTypes = Collections.unmodifiableSet(rawFieldTypes); } - public Set> getFieldTypes() { + public final Set> getFieldTypes() { return this.fieldTypes; } + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.embeddedValueResolver = resolver; + } + + protected String resolveEmbeddedValue(String value) { + return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value); + } + + public Printer getPrinter(DateTimeFormat annotation, Class fieldType) { DateTimeFormatter formatter = configureDateTimeFormatterFrom(annotation); if (ReadableInstant.class.isAssignableFrom(fieldType)) { @@ -92,24 +106,26 @@ public final class JodaDateTimeFormatAnnotationFormatterFactory implements Annot } - // internal helpers - private DateTimeFormatter configureDateTimeFormatterFrom(DateTimeFormat annotation) { if (StringUtils.hasLength(annotation.pattern())) { - return forPattern(annotation.pattern()); + return forPattern(resolveEmbeddedValue(annotation.pattern())); } else if (annotation.iso() != ISO.NONE) { return forIso(annotation.iso()); } else { - return forStyle(annotation.style()); + return forStyle(resolveEmbeddedValue(annotation.style())); } } private DateTimeFormatter forPattern(String pattern) { return org.joda.time.format.DateTimeFormat.forPattern(pattern); } - + + private DateTimeFormatter forStyle(String style) { + return org.joda.time.format.DateTimeFormat.forStyle(style); + } + private DateTimeFormatter forIso(ISO iso) { if (iso == ISO.DATE) { return org.joda.time.format.ISODateTimeFormat.date(); @@ -122,8 +138,4 @@ public final class JodaDateTimeFormatAnnotationFormatterFactory implements Annot } } - 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/number/NumberFormatAnnotationFormatterFactory.java b/org.springframework.context/src/main/java/org/springframework/format/number/NumberFormatAnnotationFormatterFactory.java index 09da26668b6..7ae4021945a 100644 --- a/org.springframework.context/src/main/java/org/springframework/format/number/NumberFormatAnnotationFormatterFactory.java +++ b/org.springframework.context/src/main/java/org/springframework/format/number/NumberFormatAnnotationFormatterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -22,6 +22,7 @@ import java.util.Collections; 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; @@ -29,6 +30,7 @@ import org.springframework.format.Printer; import org.springframework.format.annotation.NumberFormat; import org.springframework.format.annotation.NumberFormat.Style; import org.springframework.util.StringUtils; +import org.springframework.util.StringValueResolver; /** * Formats fields annotated with the {@link NumberFormat} annotation. @@ -37,46 +39,52 @@ import org.springframework.util.StringUtils; * @since 3.0 * @see NumberFormat */ -public final class NumberFormatAnnotationFormatterFactory implements AnnotationFormatterFactory { +public class NumberFormatAnnotationFormatterFactory + implements AnnotationFormatterFactory, EmbeddedValueResolverAware { private final Set> fieldTypes; + private StringValueResolver embeddedValueResolver; + public NumberFormatAnnotationFormatterFactory() { - this.fieldTypes = Collections.unmodifiableSet(createFieldTypes()); + Set> rawFieldTypes = new HashSet>(7); + rawFieldTypes.add(Short.class); + rawFieldTypes.add(Integer.class); + rawFieldTypes.add(Long.class); + rawFieldTypes.add(Float.class); + rawFieldTypes.add(Double.class); + rawFieldTypes.add(BigDecimal.class); + rawFieldTypes.add(BigInteger.class); + this.fieldTypes = Collections.unmodifiableSet(rawFieldTypes); } - - public Set> getFieldTypes() { + public final Set> getFieldTypes() { return this.fieldTypes; } + + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.embeddedValueResolver = resolver; + } + + protected String resolveEmbeddedValue(String value) { + return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value); + } + + public Printer getPrinter(NumberFormat annotation, Class fieldType) { - return configureFormatterFrom(annotation, fieldType); + return configureFormatterFrom(annotation); } public Parser getParser(NumberFormat annotation, Class fieldType) { - return configureFormatterFrom(annotation, fieldType); + return configureFormatterFrom(annotation); } - // internal helpers - - private Set> createFieldTypes() { - Set> fieldTypes = new HashSet>(7); - fieldTypes.add(Short.class); - fieldTypes.add(Integer.class); - fieldTypes.add(Long.class); - fieldTypes.add(Float.class); - fieldTypes.add(Double.class); - fieldTypes.add(BigDecimal.class); - fieldTypes.add(BigInteger.class); - return fieldTypes; - } - - private Formatter configureFormatterFrom(NumberFormat annotation, Class fieldType) { + private Formatter configureFormatterFrom(NumberFormat annotation) { if (StringUtils.hasLength(annotation.pattern())) { - return new NumberFormatter(annotation.pattern()); + return new NumberFormatter(resolveEmbeddedValue(annotation.pattern())); } else { Style style = annotation.style(); diff --git a/org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionService.java b/org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionService.java index 1830ef212e2..3e3ad01e3b8 100644 --- a/org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionService.java +++ b/org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.core.GenericTypeResolver; import org.springframework.core.convert.ConversionService; @@ -35,6 +36,7 @@ import org.springframework.format.Formatter; import org.springframework.format.FormatterRegistry; import org.springframework.format.Parser; import org.springframework.format.Printer; +import org.springframework.util.StringValueResolver; /** * A {@link org.springframework.core.convert.ConversionService} implementation @@ -44,7 +46,22 @@ import org.springframework.format.Printer; * @author Juergen Hoeller * @since 3.0 */ -public class FormattingConversionService extends GenericConversionService implements FormatterRegistry { +public class FormattingConversionService extends GenericConversionService + implements FormatterRegistry, EmbeddedValueResolverAware { + + private StringValueResolver embeddedValueResolver; + + private final Map cachedPrinters = + new ConcurrentHashMap(); + + private final Map cachedParsers = + new ConcurrentHashMap(); + + + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.embeddedValueResolver = resolver; + } + public void addFormatterForFieldType(Class fieldType, Formatter formatter) { addConverter(new PrinterConverter(fieldType, formatter, this)); @@ -61,16 +78,14 @@ public class FormattingConversionService extends GenericConversionService implem final Class annotationType = (Class) GenericTypeResolver.resolveTypeArgument(annotationFormatterFactory.getClass(), AnnotationFormatterFactory.class); if (annotationType == null) { - throw new IllegalArgumentException( - "Unable to extract parameterized Annotation type argument from AnnotationFormatterFactory [" - + annotationFormatterFactory.getClass().getName() - + "]; does the factory parameterize the generic type?"); - } - Set> fieldTypes = annotationFormatterFactory.getFieldTypes(); + throw new IllegalArgumentException("Unable to extract parameterized Annotation type argument from AnnotationFormatterFactory [" + + annotationFormatterFactory.getClass().getName() + "]; does the factory parameterize the generic type?"); + } + if (this.embeddedValueResolver != null && annotationFormatterFactory instanceof EmbeddedValueResolverAware) { + ((EmbeddedValueResolverAware) annotationFormatterFactory).setEmbeddedValueResolver(this.embeddedValueResolver); + } - final Map cachedPrinters = new ConcurrentHashMap(); - final Map cachedParsers = new ConcurrentHashMap(); - + Set> fieldTypes = annotationFormatterFactory.getFieldTypes(); for (final Class fieldType : fieldTypes) { addConverter(new ConditionalGenericConverter() { public Set getConvertibleTypes() { @@ -119,6 +134,7 @@ public class FormattingConversionService extends GenericConversionService implem } } + private static final class FieldFormatterKey { private final Annotation annotation; @@ -147,11 +163,11 @@ public class FormattingConversionService extends GenericConversionService implem } public int hashCode() { - return this.annotation.hashCode() + this.fieldType.hashCode(); + return this.annotation.hashCode() + 29 * this.fieldType.hashCode(); } - } + private static class PrinterConverter implements GenericConverter { private Class fieldType; @@ -191,6 +207,7 @@ public class FormattingConversionService extends GenericConversionService implem } } + private static class ParserConverter implements GenericConverter { private Class fieldType; @@ -230,7 +247,6 @@ public class FormattingConversionService extends GenericConversionService implem public String toString() { return String.class.getName() + " -> " + this.fieldType.getName() + ": " + this.parser; } - } } diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java index f76c9973515..74ced65a7ea 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java @@ -26,7 +26,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; -import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationUtils; @@ -35,6 +35,7 @@ import org.springframework.scheduling.support.MethodInvokingRunnable; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.MethodCallback; +import org.springframework.util.StringValueResolver; /** * Bean post-processor that registers methods annotated with {@link Scheduled @Scheduled} @@ -47,11 +48,14 @@ import org.springframework.util.ReflectionUtils.MethodCallback; * @see Scheduled * @see org.springframework.scheduling.TaskScheduler */ -public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered, - ApplicationContextAware, ApplicationListener, DisposableBean { +public class ScheduledAnnotationBeanPostProcessor + implements BeanPostProcessor, Ordered, EmbeddedValueResolverAware, ApplicationContextAware, + ApplicationListener, DisposableBean { private Object scheduler; + private StringValueResolver embeddedValueResolver; + private ApplicationContext applicationContext; private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar(); @@ -72,6 +76,10 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, this.scheduler = scheduler; } + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.embeddedValueResolver = resolver; + } + public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @@ -102,17 +110,16 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, try { runnable.prepare(); } - catch (Exception e) { - throw new IllegalStateException("failed to prepare task", e); + catch (Exception ex) { + throw new IllegalStateException("failed to prepare task", ex); } boolean processedSchedule = false; String errorMessage = "Exactly one of 'cron', 'fixedDelay', or 'fixedRate' is required."; String cron = annotation.cron(); if (!"".equals(cron)) { processedSchedule = true; - if (applicationContext instanceof ConfigurableApplicationContext) { - cron = ((ConfigurableApplicationContext) applicationContext) - .getBeanFactory().resolveEmbeddedValue(cron); + if (embeddedValueResolver != null) { + cron = embeddedValueResolver.resolveStringValue(cron); } cronTasks.put(runnable, cron); } 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 dd5610d0199..44d324e567a 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 @@ -335,7 +335,6 @@ public class JodaTimeFormattingTests { @DateTimeFormat private DateTime dateTimeAnnotatedDefault; - @DateTimeFormat(style="S-") private Long millisAnnotated; @DateTimeFormat(pattern="M/d/yy h:mm a") @@ -480,11 +479,12 @@ public class JodaTimeFormattingTests { this.millis = millis; } + @DateTimeFormat(style="S-") public Long getMillisAnnotated() { return millisAnnotated; } - public void setMillisAnnotated(Long millisAnnotated) { + public void setMillisAnnotated(@DateTimeFormat(style="S-") Long millisAnnotated) { this.millisAnnotated = millisAnnotated; } diff --git a/org.springframework.context/src/test/java/org/springframework/format/number/NumberFormattingTests.java b/org.springframework.context/src/test/java/org/springframework/format/number/NumberFormattingTests.java index 6fae27fd36c..ceeb262c074 100644 --- a/org.springframework.context/src/test/java/org/springframework/format/number/NumberFormattingTests.java +++ b/org.springframework.context/src/test/java/org/springframework/format/number/NumberFormattingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -17,8 +17,8 @@ package org.springframework.format.number; import java.math.BigDecimal; -import java.util.Locale; import java.util.List; +import java.util.Locale; import org.junit.After; import static org.junit.Assert.*; @@ -31,6 +31,7 @@ import org.springframework.core.convert.support.ConversionServiceFactory; import org.springframework.format.annotation.NumberFormat; import org.springframework.format.annotation.NumberFormat.Style; import org.springframework.format.support.FormattingConversionService; +import org.springframework.util.StringValueResolver; import org.springframework.validation.DataBinder; /** @@ -46,6 +47,16 @@ public class NumberFormattingTests { @Before public void setUp() { ConversionServiceFactory.addDefaultConverters(conversionService); + conversionService.setEmbeddedValueResolver(new StringValueResolver() { + public String resolveStringValue(String strVal) { + if ("${pattern}".equals(strVal)) { + return "#,##.00"; + } + else { + return strVal; + } + } + }); conversionService.addFormatterForFieldType(Number.class, new NumberFormatter()); conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory()); LocaleContextHolder.setLocale(Locale.US); @@ -154,7 +165,7 @@ public class NumberFormattingTests { @NumberFormat(style=Style.PERCENT) private BigDecimal percent; - @NumberFormat(pattern="#,##.00") + @NumberFormat(pattern="${pattern}") private BigDecimal pattern; @NumberFormat(pattern="#,##.00") diff --git a/org.springframework.context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java b/org.springframework.context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java index e187e7d2e06..144db168ff2 100644 --- a/org.springframework.context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java +++ b/org.springframework.context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -16,22 +16,24 @@ package org.springframework.format.support; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - import java.text.ParseException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; +import java.util.Properties; import org.joda.time.DateTime; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.junit.After; +import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; + +import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.support.ConversionServiceFactory; @@ -70,7 +72,7 @@ public class FormattingConversionServiceTests { } @Test - public void testFormatFieldForTypeWithPrinterParserWithCoersion() throws ParseException { + public void testFormatFieldForTypeWithPrinterParserWithCoercion() throws ParseException { formattingService.addConverter(new Converter() { public LocalDate convert(DateTime source) { return source.toLocalDate(); @@ -86,6 +88,24 @@ public class FormattingConversionServiceTests { @Test public void testFormatFieldForAnnotation() throws Exception { + doTestFormatFieldForAnnotation(Model.class); + } + + @Test + public void testFormatFieldForAnnotationWithPlaceholders() throws Exception { + GenericApplicationContext context = new GenericApplicationContext(); + PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer(); + Properties props = new Properties(); + props.setProperty("dateStyle", "S-"); + props.setProperty("datePattern", "M/d/yy"); + ppc.setProperties(props); + context.getBeanFactory().registerSingleton("ppc", ppc); + context.refresh(); + context.getBeanFactory().initializeBean(formattingService, "formattingService"); + doTestFormatFieldForAnnotation(ModelWithPlaceholders.class); + } + + private void doTestFormatFieldForAnnotation(Class modelClass) throws Exception { formattingService.addConverter(new Converter() { public Long convert(Date source) { return source.getTime(); @@ -97,34 +117,23 @@ public class FormattingConversionServiceTests { } }); formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory()); + String formatted = (String) formattingService.convert(new LocalDate(2009, 10, 31).toDateTimeAtCurrentTime() - .toDate(), new TypeDescriptor(Model.class.getField("date")), TypeDescriptor.valueOf(String.class)); + .toDate(), new TypeDescriptor(modelClass.getField("date")), TypeDescriptor.valueOf(String.class)); assertEquals("10/31/09", formatted); LocalDate date = new LocalDate(formattingService.convert("10/31/09", TypeDescriptor.valueOf(String.class), - new TypeDescriptor(Model.class.getField("date")))); + new TypeDescriptor(modelClass.getField("date")))); assertEquals(new LocalDate(2009, 10, 31), date); - } - - @Test - public void testFormatCollectionFieldForAnnotation() throws Exception { - formattingService.addConverter(new Converter() { - public Long convert(Date source) { - return source.getTime(); - } - }); - formattingService.addConverter(new Converter() { - public Date convert(DateTime source) { - return source.toDate(); - } - }); - formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory()); + List dates = new ArrayList(); dates.add(new LocalDate(2009, 10, 31).toDateTimeAtCurrentTime().toDate()); dates.add(new LocalDate(2009, 11, 1).toDateTimeAtCurrentTime().toDate()); dates.add(new LocalDate(2009, 11, 2).toDateTimeAtCurrentTime().toDate()); - String formatted = (String) formattingService.convert(dates, new TypeDescriptor(Model.class.getField("dates")), TypeDescriptor.valueOf(String.class)); + formatted = (String) formattingService.convert(dates, + new TypeDescriptor(modelClass.getField("dates")), TypeDescriptor.valueOf(String.class)); assertEquals("10/31/09,11/1/09,11/2/09", formatted); - dates = (List) formattingService.convert("10/31/09,11/1/09,11/2/09", TypeDescriptor.valueOf(String.class), new TypeDescriptor(Model.class.getField("dates"))); + dates = (List) formattingService.convert("10/31/09,11/1/09,11/2/09", + TypeDescriptor.valueOf(String.class), new TypeDescriptor(modelClass.getField("dates"))); assertEquals(new LocalDate(2009, 10, 31), new LocalDate(dates.get(0))); assertEquals(new LocalDate(2009, 11, 1), new LocalDate(dates.get(1))); assertEquals(new LocalDate(2009, 11, 2), new LocalDate(dates.get(2))); @@ -163,6 +172,7 @@ public class FormattingConversionServiceTests { assertNull(formattingService.convert("", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class))); } + private static class Model { @SuppressWarnings("unused") @@ -170,7 +180,20 @@ public class FormattingConversionServiceTests { public Date date; @SuppressWarnings("unused") - @org.springframework.format.annotation.DateTimeFormat(style="S-") + @org.springframework.format.annotation.DateTimeFormat(pattern="M/d/yy") + public List dates; + + } + + + private static class ModelWithPlaceholders { + + @SuppressWarnings("unused") + @org.springframework.format.annotation.DateTimeFormat(style="${dateStyle}") + public Date date; + + @SuppressWarnings("unused") + @org.springframework.format.annotation.DateTimeFormat(pattern="${datePattern}") public List dates; }