introduced EmbeddedValueResolverAware callback interface for convenient placeholder resolution

This commit is contained in:
Juergen Hoeller 2010-06-07 22:41:21 +00:00
parent 8446fd1b26
commit 392accd910
9 changed files with 224 additions and 87 deletions

View File

@ -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
* <b>StringValueResolver</b> for the <b> resolution of embedded definition values.
*
* <p>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);
}

View File

@ -22,11 +22,14 @@ import java.security.PrivilegedAction;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.MessageSourceAware; import org.springframework.context.MessageSourceAware;
import org.springframework.context.ResourceLoaderAware; import org.springframework.context.ResourceLoaderAware;
import org.springframework.util.StringValueResolver;
/** /**
* {@link org.springframework.beans.factory.config.BeanPostProcessor} * {@link org.springframework.beans.factory.config.BeanPostProcessor}
@ -86,6 +89,10 @@ class ApplicationContextAwareProcessor implements BeanPostProcessor {
} }
private void invokeAwareInterfaces(Object bean) { private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
}
if (bean instanceof ResourceLoaderAware) { if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
} }
@ -104,4 +111,18 @@ class ApplicationContextAwareProcessor implements BeanPostProcessor {
return bean; 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);
}
}
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.ReadablePartial;
import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatter;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.format.AnnotationFormatterFactory; import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Parser; import org.springframework.format.Parser;
import org.springframework.format.Printer; import org.springframework.format.Printer;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;
/** /**
* Formats fields annotated with the {@link DateTimeFormat} annotation. * Formats fields annotated with the {@link DateTimeFormat} annotation.
@ -46,10 +48,13 @@ import org.springframework.util.StringUtils;
* @since 3.0 * @since 3.0
* @see DateTimeFormat * @see DateTimeFormat
*/ */
public final class JodaDateTimeFormatAnnotationFormatterFactory implements AnnotationFormatterFactory<DateTimeFormat> { public class JodaDateTimeFormatAnnotationFormatterFactory
implements AnnotationFormatterFactory<DateTimeFormat>, EmbeddedValueResolverAware {
private final Set<Class<?>> fieldTypes; private final Set<Class<?>> fieldTypes;
private StringValueResolver embeddedValueResolver;
public JodaDateTimeFormatAnnotationFormatterFactory() { public JodaDateTimeFormatAnnotationFormatterFactory() {
Set<Class<?>> rawFieldTypes = new HashSet<Class<?>>(8); Set<Class<?>> rawFieldTypes = new HashSet<Class<?>>(8);
@ -64,11 +69,20 @@ public final class JodaDateTimeFormatAnnotationFormatterFactory implements Annot
this.fieldTypes = Collections.unmodifiableSet(rawFieldTypes); this.fieldTypes = Collections.unmodifiableSet(rawFieldTypes);
} }
public Set<Class<?>> getFieldTypes() { public final Set<Class<?>> getFieldTypes() {
return this.fieldTypes; 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) { public Printer<?> getPrinter(DateTimeFormat annotation, Class<?> fieldType) {
DateTimeFormatter formatter = configureDateTimeFormatterFrom(annotation); DateTimeFormatter formatter = configureDateTimeFormatterFrom(annotation);
if (ReadableInstant.class.isAssignableFrom(fieldType)) { if (ReadableInstant.class.isAssignableFrom(fieldType)) {
@ -92,17 +106,15 @@ public final class JodaDateTimeFormatAnnotationFormatterFactory implements Annot
} }
// internal helpers
private DateTimeFormatter configureDateTimeFormatterFrom(DateTimeFormat annotation) { private DateTimeFormatter configureDateTimeFormatterFrom(DateTimeFormat annotation) {
if (StringUtils.hasLength(annotation.pattern())) { if (StringUtils.hasLength(annotation.pattern())) {
return forPattern(annotation.pattern()); return forPattern(resolveEmbeddedValue(annotation.pattern()));
} }
else if (annotation.iso() != ISO.NONE) { else if (annotation.iso() != ISO.NONE) {
return forIso(annotation.iso()); return forIso(annotation.iso());
} }
else { else {
return forStyle(annotation.style()); return forStyle(resolveEmbeddedValue(annotation.style()));
} }
} }
@ -110,6 +122,10 @@ public final class JodaDateTimeFormatAnnotationFormatterFactory implements Annot
return org.joda.time.format.DateTimeFormat.forPattern(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) { private DateTimeFormatter forIso(ISO iso) {
if (iso == ISO.DATE) { if (iso == ISO.DATE) {
return org.joda.time.format.ISODateTimeFormat.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);
}
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.HashSet;
import java.util.Set; import java.util.Set;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.format.AnnotationFormatterFactory; import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Formatter; import org.springframework.format.Formatter;
import org.springframework.format.Parser; 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;
import org.springframework.format.annotation.NumberFormat.Style; import org.springframework.format.annotation.NumberFormat.Style;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;
/** /**
* Formats fields annotated with the {@link NumberFormat} annotation. * Formats fields annotated with the {@link NumberFormat} annotation.
@ -37,46 +39,52 @@ import org.springframework.util.StringUtils;
* @since 3.0 * @since 3.0
* @see NumberFormat * @see NumberFormat
*/ */
public final class NumberFormatAnnotationFormatterFactory implements AnnotationFormatterFactory<NumberFormat> { public class NumberFormatAnnotationFormatterFactory
implements AnnotationFormatterFactory<NumberFormat>, EmbeddedValueResolverAware {
private final Set<Class<?>> fieldTypes; private final Set<Class<?>> fieldTypes;
private StringValueResolver embeddedValueResolver;
public NumberFormatAnnotationFormatterFactory() { public NumberFormatAnnotationFormatterFactory() {
this.fieldTypes = Collections.unmodifiableSet(createFieldTypes()); Set<Class<?>> rawFieldTypes = new HashSet<Class<?>>(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 final Set<Class<?>> getFieldTypes() {
public Set<Class<?>> getFieldTypes() {
return this.fieldTypes; 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<Number> getPrinter(NumberFormat annotation, Class<?> fieldType) { public Printer<Number> getPrinter(NumberFormat annotation, Class<?> fieldType) {
return configureFormatterFrom(annotation, fieldType); return configureFormatterFrom(annotation);
} }
public Parser<Number> getParser(NumberFormat annotation, Class<?> fieldType) { public Parser<Number> getParser(NumberFormat annotation, Class<?> fieldType) {
return configureFormatterFrom(annotation, fieldType); return configureFormatterFrom(annotation);
} }
// internal helpers private Formatter<Number> configureFormatterFrom(NumberFormat annotation) {
private Set<Class<?>> createFieldTypes() {
Set<Class<?>> fieldTypes = new HashSet<Class<?>>(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<Number> configureFormatterFrom(NumberFormat annotation, Class<?> fieldType) {
if (StringUtils.hasLength(annotation.pattern())) { if (StringUtils.hasLength(annotation.pattern())) {
return new NumberFormatter(annotation.pattern()); return new NumberFormatter(resolveEmbeddedValue(annotation.pattern()));
} }
else { else {
Style style = annotation.style(); Style style = annotation.style();

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.GenericTypeResolver; import org.springframework.core.GenericTypeResolver;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
@ -35,6 +36,7 @@ import org.springframework.format.Formatter;
import org.springframework.format.FormatterRegistry; import org.springframework.format.FormatterRegistry;
import org.springframework.format.Parser; import org.springframework.format.Parser;
import org.springframework.format.Printer; import org.springframework.format.Printer;
import org.springframework.util.StringValueResolver;
/** /**
* A {@link org.springframework.core.convert.ConversionService} implementation * A {@link org.springframework.core.convert.ConversionService} implementation
@ -44,7 +46,22 @@ import org.springframework.format.Printer;
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 3.0 * @since 3.0
*/ */
public class FormattingConversionService extends GenericConversionService implements FormatterRegistry { public class FormattingConversionService extends GenericConversionService
implements FormatterRegistry, EmbeddedValueResolverAware {
private StringValueResolver embeddedValueResolver;
private final Map<FieldFormatterKey, GenericConverter> cachedPrinters =
new ConcurrentHashMap<FieldFormatterKey, GenericConverter>();
private final Map<FieldFormatterKey, GenericConverter> cachedParsers =
new ConcurrentHashMap<FieldFormatterKey, GenericConverter>();
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
public void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter) { public void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter) {
addConverter(new PrinterConverter(fieldType, formatter, this)); addConverter(new PrinterConverter(fieldType, formatter, this));
@ -61,16 +78,14 @@ public class FormattingConversionService extends GenericConversionService implem
final Class<? extends Annotation> annotationType = (Class<? extends Annotation>) final Class<? extends Annotation> annotationType = (Class<? extends Annotation>)
GenericTypeResolver.resolveTypeArgument(annotationFormatterFactory.getClass(), AnnotationFormatterFactory.class); GenericTypeResolver.resolveTypeArgument(annotationFormatterFactory.getClass(), AnnotationFormatterFactory.class);
if (annotationType == null) { if (annotationType == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException("Unable to extract parameterized Annotation type argument from AnnotationFormatterFactory [" +
"Unable to extract parameterized Annotation type argument from AnnotationFormatterFactory [" annotationFormatterFactory.getClass().getName() + "]; does the factory parameterize the <A extends Annotation> generic type?");
+ annotationFormatterFactory.getClass().getName()
+ "]; does the factory parameterize the <A extends Annotation> generic type?");
} }
if (this.embeddedValueResolver != null && annotationFormatterFactory instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) annotationFormatterFactory).setEmbeddedValueResolver(this.embeddedValueResolver);
}
Set<Class<?>> fieldTypes = annotationFormatterFactory.getFieldTypes(); Set<Class<?>> fieldTypes = annotationFormatterFactory.getFieldTypes();
final Map<FieldFormatterKey, GenericConverter> cachedPrinters = new ConcurrentHashMap<FieldFormatterKey, GenericConverter>();
final Map<FieldFormatterKey, GenericConverter> cachedParsers = new ConcurrentHashMap<FieldFormatterKey, GenericConverter>();
for (final Class<?> fieldType : fieldTypes) { for (final Class<?> fieldType : fieldTypes) {
addConverter(new ConditionalGenericConverter() { addConverter(new ConditionalGenericConverter() {
public Set<ConvertiblePair> getConvertibleTypes() { public Set<ConvertiblePair> getConvertibleTypes() {
@ -119,6 +134,7 @@ public class FormattingConversionService extends GenericConversionService implem
} }
} }
private static final class FieldFormatterKey { private static final class FieldFormatterKey {
private final Annotation annotation; private final Annotation annotation;
@ -147,10 +163,10 @@ public class FormattingConversionService extends GenericConversionService implem
} }
public int hashCode() { 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 static class PrinterConverter implements GenericConverter {
@ -191,6 +207,7 @@ public class FormattingConversionService extends GenericConversionService implem
} }
} }
private static class ParserConverter implements GenericConverter { private static class ParserConverter implements GenericConverter {
private Class<?> fieldType; private Class<?> fieldType;
@ -230,7 +247,6 @@ public class FormattingConversionService extends GenericConversionService implem
public String toString() { public String toString() {
return String.class.getName() + " -> " + this.fieldType.getName() + ": " + this.parser; return String.class.getName() + " -> " + this.fieldType.getName() + ": " + this.parser;
} }
} }
} }

View File

@ -26,7 +26,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils; 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.Assert;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback; import org.springframework.util.ReflectionUtils.MethodCallback;
import org.springframework.util.StringValueResolver;
/** /**
* Bean post-processor that registers methods annotated with {@link Scheduled @Scheduled} * Bean post-processor that registers methods annotated with {@link Scheduled @Scheduled}
@ -47,11 +48,14 @@ import org.springframework.util.ReflectionUtils.MethodCallback;
* @see Scheduled * @see Scheduled
* @see org.springframework.scheduling.TaskScheduler * @see org.springframework.scheduling.TaskScheduler
*/ */
public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered, public class ScheduledAnnotationBeanPostProcessor
ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, DisposableBean { implements BeanPostProcessor, Ordered, EmbeddedValueResolverAware, ApplicationContextAware,
ApplicationListener<ContextRefreshedEvent>, DisposableBean {
private Object scheduler; private Object scheduler;
private StringValueResolver embeddedValueResolver;
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar(); private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();
@ -72,6 +76,10 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
this.scheduler = scheduler; this.scheduler = scheduler;
} }
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
public void setApplicationContext(ApplicationContext applicationContext) { public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
} }
@ -102,17 +110,16 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
try { try {
runnable.prepare(); runnable.prepare();
} }
catch (Exception e) { catch (Exception ex) {
throw new IllegalStateException("failed to prepare task", e); throw new IllegalStateException("failed to prepare task", ex);
} }
boolean processedSchedule = false; boolean processedSchedule = false;
String errorMessage = "Exactly one of 'cron', 'fixedDelay', or 'fixedRate' is required."; String errorMessage = "Exactly one of 'cron', 'fixedDelay', or 'fixedRate' is required.";
String cron = annotation.cron(); String cron = annotation.cron();
if (!"".equals(cron)) { if (!"".equals(cron)) {
processedSchedule = true; processedSchedule = true;
if (applicationContext instanceof ConfigurableApplicationContext) { if (embeddedValueResolver != null) {
cron = ((ConfigurableApplicationContext) applicationContext) cron = embeddedValueResolver.resolveStringValue(cron);
.getBeanFactory().resolveEmbeddedValue(cron);
} }
cronTasks.put(runnable, cron); cronTasks.put(runnable, cron);
} }

View File

@ -335,7 +335,6 @@ public class JodaTimeFormattingTests {
@DateTimeFormat @DateTimeFormat
private DateTime dateTimeAnnotatedDefault; private DateTime dateTimeAnnotatedDefault;
@DateTimeFormat(style="S-")
private Long millisAnnotated; private Long millisAnnotated;
@DateTimeFormat(pattern="M/d/yy h:mm a") @DateTimeFormat(pattern="M/d/yy h:mm a")
@ -480,11 +479,12 @@ public class JodaTimeFormattingTests {
this.millis = millis; this.millis = millis;
} }
@DateTimeFormat(style="S-")
public Long getMillisAnnotated() { public Long getMillisAnnotated() {
return millisAnnotated; return millisAnnotated;
} }
public void setMillisAnnotated(Long millisAnnotated) { public void setMillisAnnotated(@DateTimeFormat(style="S-") Long millisAnnotated) {
this.millisAnnotated = millisAnnotated; this.millisAnnotated = millisAnnotated;
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,8 +17,8 @@
package org.springframework.format.number; package org.springframework.format.number;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Locale;
import java.util.List; import java.util.List;
import java.util.Locale;
import org.junit.After; import org.junit.After;
import static org.junit.Assert.*; 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;
import org.springframework.format.annotation.NumberFormat.Style; import org.springframework.format.annotation.NumberFormat.Style;
import org.springframework.format.support.FormattingConversionService; import org.springframework.format.support.FormattingConversionService;
import org.springframework.util.StringValueResolver;
import org.springframework.validation.DataBinder; import org.springframework.validation.DataBinder;
/** /**
@ -46,6 +47,16 @@ public class NumberFormattingTests {
@Before @Before
public void setUp() { public void setUp() {
ConversionServiceFactory.addDefaultConverters(conversionService); 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.addFormatterForFieldType(Number.class, new NumberFormatter());
conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory()); conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
LocaleContextHolder.setLocale(Locale.US); LocaleContextHolder.setLocale(Locale.US);
@ -154,7 +165,7 @@ public class NumberFormattingTests {
@NumberFormat(style=Style.PERCENT) @NumberFormat(style=Style.PERCENT)
private BigDecimal percent; private BigDecimal percent;
@NumberFormat(pattern="#,##.00") @NumberFormat(pattern="${pattern}")
private BigDecimal pattern; private BigDecimal pattern;
@NumberFormat(pattern="#,##.00") @NumberFormat(pattern="#,##.00")

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,22 +16,24 @@
package org.springframework.format.support; package org.springframework.format.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.text.ParseException; import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Properties;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormat;
import org.junit.After; import org.junit.After;
import static org.junit.Assert.*;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.ConversionServiceFactory; import org.springframework.core.convert.support.ConversionServiceFactory;
@ -70,7 +72,7 @@ public class FormattingConversionServiceTests {
} }
@Test @Test
public void testFormatFieldForTypeWithPrinterParserWithCoersion() throws ParseException { public void testFormatFieldForTypeWithPrinterParserWithCoercion() throws ParseException {
formattingService.addConverter(new Converter<DateTime, LocalDate>() { formattingService.addConverter(new Converter<DateTime, LocalDate>() {
public LocalDate convert(DateTime source) { public LocalDate convert(DateTime source) {
return source.toLocalDate(); return source.toLocalDate();
@ -86,27 +88,24 @@ public class FormattingConversionServiceTests {
@Test @Test
public void testFormatFieldForAnnotation() throws Exception { public void testFormatFieldForAnnotation() throws Exception {
formattingService.addConverter(new Converter<Date, Long>() { doTestFormatFieldForAnnotation(Model.class);
public Long convert(Date source) {
return source.getTime();
}
});
formattingService.addConverter(new Converter<DateTime, Date>() {
public Date convert(DateTime source) {
return source.toDate();
}
});
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));
assertEquals("10/31/09", formatted);
LocalDate date = new LocalDate(formattingService.convert("10/31/09", TypeDescriptor.valueOf(String.class),
new TypeDescriptor(Model.class.getField("date"))));
assertEquals(new LocalDate(2009, 10, 31), date);
} }
@Test @Test
public void testFormatCollectionFieldForAnnotation() throws Exception { 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<Date, Long>() { formattingService.addConverter(new Converter<Date, Long>() {
public Long convert(Date source) { public Long convert(Date source) {
return source.getTime(); return source.getTime();
@ -118,13 +117,23 @@ public class FormattingConversionServiceTests {
} }
}); });
formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory()); formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory());
String formatted = (String) formattingService.convert(new LocalDate(2009, 10, 31).toDateTimeAtCurrentTime()
.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(modelClass.getField("date"))));
assertEquals(new LocalDate(2009, 10, 31), date);
List<Date> dates = new ArrayList<Date>(); List<Date> dates = new ArrayList<Date>();
dates.add(new LocalDate(2009, 10, 31).toDateTimeAtCurrentTime().toDate()); dates.add(new LocalDate(2009, 10, 31).toDateTimeAtCurrentTime().toDate());
dates.add(new LocalDate(2009, 11, 1).toDateTimeAtCurrentTime().toDate()); dates.add(new LocalDate(2009, 11, 1).toDateTimeAtCurrentTime().toDate());
dates.add(new LocalDate(2009, 11, 2).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); assertEquals("10/31/09,11/1/09,11/2/09", formatted);
dates = (List<Date>) formattingService.convert("10/31/09,11/1/09,11/2/09", TypeDescriptor.valueOf(String.class), new TypeDescriptor(Model.class.getField("dates"))); dates = (List<Date>) 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, 10, 31), new LocalDate(dates.get(0)));
assertEquals(new LocalDate(2009, 11, 1), new LocalDate(dates.get(1))); assertEquals(new LocalDate(2009, 11, 1), new LocalDate(dates.get(1)));
assertEquals(new LocalDate(2009, 11, 2), new LocalDate(dates.get(2))); 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))); assertNull(formattingService.convert("", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
} }
private static class Model { private static class Model {
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -170,7 +180,20 @@ public class FormattingConversionServiceTests {
public Date date; public Date date;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@org.springframework.format.annotation.DateTimeFormat(style="S-") @org.springframework.format.annotation.DateTimeFormat(pattern="M/d/yy")
public List<Date> 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<Date> dates; public List<Date> dates;
} }