Support @NumberFormat as a meta-annotation
This commit ensures that @NumberFormat can be used as a meta-annotation, as was already the case for @DateTimeFormat. In addition, this commit polishes FormattingConversionServiceTests and MvcNamespaceTests. Issue: SPR-12743
This commit is contained in:
parent
6861d35ea0
commit
c746b10da9
|
@ -24,7 +24,8 @@ import java.lang.annotation.Target;
|
|||
|
||||
/**
|
||||
* Declares that a field should be formatted as a number.
|
||||
* Supports formatting by style or custom pattern string.
|
||||
*
|
||||
* <p>Supports formatting by style or custom pattern string.
|
||||
* Can be applied to any JDK {@code java.lang.Number} type.
|
||||
*
|
||||
* <p>For style-based formatting, set the {@link #style} attribute to be the desired {@link Style}.
|
||||
|
@ -41,7 +42,7 @@ import java.lang.annotation.Target;
|
|||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
|
||||
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
|
||||
public @interface NumberFormat {
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
@ -28,6 +28,7 @@ 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 org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -48,6 +49,7 @@ import org.springframework.core.convert.converter.Converter;
|
|||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.format.Printer;
|
||||
import org.springframework.format.annotation.NumberFormat;
|
||||
import org.springframework.format.datetime.joda.DateTimeParser;
|
||||
import org.springframework.format.datetime.joda.JodaDateTimeFormatAnnotationFormatterFactory;
|
||||
import org.springframework.format.datetime.joda.ReadablePartialPrinter;
|
||||
|
@ -58,6 +60,8 @@ import static org.junit.Assert.*;
|
|||
/**
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Kazuki Shimizu
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class FormattingConversionServiceTests {
|
||||
|
||||
|
@ -101,6 +105,7 @@ public class FormattingConversionServiceTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
public void testFormatFieldForValueInjection() {
|
||||
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
|
||||
ac.registerBeanDefinition("valueBean", new RootBeanDefinition(ValueBean.class));
|
||||
|
@ -111,6 +116,7 @@ public class FormattingConversionServiceTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
public void testFormatFieldForValueInjectionUsingMetaAnnotations() {
|
||||
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
|
||||
RootBeanDefinition bd = new RootBeanDefinition(MetaValueBean.class);
|
||||
|
@ -120,12 +126,15 @@ public class FormattingConversionServiceTests {
|
|||
ac.registerBeanDefinition("ppc", new RootBeanDefinition(PropertyPlaceholderConfigurer.class));
|
||||
ac.refresh();
|
||||
System.setProperty("myDate", "10-31-09");
|
||||
System.setProperty("myNumber", "99.99%");
|
||||
try {
|
||||
MetaValueBean valueBean = ac.getBean(MetaValueBean.class);
|
||||
assertEquals(new LocalDate(2009, 10, 31), new LocalDate(valueBean.date));
|
||||
assertEquals(Double.valueOf(0.9999), valueBean.number);
|
||||
}
|
||||
finally {
|
||||
System.clearProperty("myDate");
|
||||
System.clearProperty("myNumber");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,6 +151,7 @@ public class FormattingConversionServiceTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
public void testFormatFieldForAnnotationWithPlaceholders() throws Exception {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
|
||||
|
@ -157,6 +167,7 @@ public class FormattingConversionServiceTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
public void testFormatFieldForAnnotationWithPlaceholdersAndFactoryBean() throws Exception {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
|
||||
|
@ -336,13 +347,14 @@ public class FormattingConversionServiceTests {
|
|||
public Date date;
|
||||
}
|
||||
|
||||
|
||||
public static class MetaValueBean {
|
||||
|
||||
@MyDateAnn
|
||||
public Date date;
|
||||
}
|
||||
|
||||
@MyNumberAnn
|
||||
public Double number;
|
||||
}
|
||||
|
||||
@Value("${myDate}")
|
||||
@org.springframework.format.annotation.DateTimeFormat(pattern="MM-d-yy")
|
||||
|
@ -350,6 +362,11 @@ public class FormattingConversionServiceTests {
|
|||
public static @interface MyDateAnn {
|
||||
}
|
||||
|
||||
@Value("${myNumber}")
|
||||
@NumberFormat(style = NumberFormat.Style.PERCENT)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public static @interface MyNumberAnn {
|
||||
}
|
||||
|
||||
public static class Model {
|
||||
|
||||
|
@ -368,7 +385,6 @@ public class FormattingConversionServiceTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ModelWithPlaceholders {
|
||||
|
||||
@org.springframework.format.annotation.DateTimeFormat(style="${dateStyle}")
|
||||
|
@ -386,13 +402,11 @@ public class FormattingConversionServiceTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@org.springframework.format.annotation.DateTimeFormat(pattern="${datePattern}")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public static @interface MyDatePattern {
|
||||
}
|
||||
|
||||
|
||||
public static class NullReturningFormatter implements Formatter<Integer> {
|
||||
|
||||
@Override
|
||||
|
@ -407,12 +421,10 @@ public class FormattingConversionServiceTests {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class MyDate extends Date {
|
||||
}
|
||||
|
||||
|
||||
private static class ModelWithSubclassField {
|
||||
|
||||
@org.springframework.format.annotation.DateTimeFormat(style = "S-")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
@ -29,14 +29,14 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.MapperFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
import org.hamcrest.Matchers;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -52,6 +52,7 @@ import org.springframework.core.convert.ConversionService;
|
|||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||
import org.springframework.format.annotation.NumberFormat;
|
||||
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
|
@ -132,6 +133,12 @@ import org.springframework.web.servlet.view.velocity.VelocityConfigurer;
|
|||
import org.springframework.web.servlet.view.velocity.VelocityViewResolver;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.MapperFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
|
@ -142,11 +149,13 @@ import static org.junit.Assert.*;
|
|||
* @author Jeremy Grelle
|
||||
* @author Brian Clozel
|
||||
* @author Sebastien Deleuze
|
||||
* @author Kazuki Shimizu
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class MvcNamespaceTests {
|
||||
|
||||
public static final String VIEWCONTROLLER_BEAN_NAME = "org.springframework.web.servlet.config.viewControllerHandlerMapping";
|
||||
|
||||
|
||||
private GenericWebApplicationContext appContext;
|
||||
|
||||
private TestController handler;
|
||||
|
@ -165,7 +174,7 @@ public class MvcNamespaceTests {
|
|||
appContext.getServletContext().setAttribute(attributeName, appContext);
|
||||
|
||||
handler = new TestController();
|
||||
Method method = TestController.class.getMethod("testBind", Date.class, TestBean.class, BindingResult.class);
|
||||
Method method = TestController.class.getMethod("testBind", Date.class, Double.class, TestBean.class, BindingResult.class);
|
||||
handlerMethod = new InvocableHandlerMethod(handler, method);
|
||||
}
|
||||
|
||||
|
@ -211,6 +220,7 @@ public class MvcNamespaceTests {
|
|||
// default web binding initializer behavior test
|
||||
request = new MockHttpServletRequest("GET", "/");
|
||||
request.addParameter("date", "2009-10-31");
|
||||
request.addParameter("percent", "99.99%");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
HandlerExecutionChain chain = mapping.getHandler(request);
|
||||
|
@ -222,6 +232,8 @@ public class MvcNamespaceTests {
|
|||
|
||||
adapter.handle(request, response, handlerMethod);
|
||||
assertTrue(handler.recordedValidationError);
|
||||
assertEquals(LocalDate.parse("2009-10-31").toDate(), handler.date);
|
||||
assertEquals(Double.valueOf(0.9999),handler.percent);
|
||||
|
||||
CompositeUriComponentsContributor uriComponentsContributor = this.appContext.getBean(
|
||||
MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME,
|
||||
|
@ -718,7 +730,7 @@ public class MvcNamespaceTests {
|
|||
assertEquals(TilesViewResolver.class, resolvers.get(2).getClass());
|
||||
|
||||
resolver = resolvers.get(3);
|
||||
FreeMarkerViewResolver freeMarkerViewResolver = (FreeMarkerViewResolver) resolver;
|
||||
assertThat(resolver, instanceOf(FreeMarkerViewResolver.class));
|
||||
accessor = new DirectFieldAccessor(resolver);
|
||||
assertEquals("freemarker-", accessor.getPropertyValue("prefix"));
|
||||
assertEquals(".freemarker", accessor.getPropertyValue("suffix"));
|
||||
|
@ -726,14 +738,14 @@ public class MvcNamespaceTests {
|
|||
assertEquals(1024, accessor.getPropertyValue("cacheLimit"));
|
||||
|
||||
resolver = resolvers.get(4);
|
||||
VelocityViewResolver velocityViewResolver = (VelocityViewResolver) resolver;
|
||||
assertThat(resolver, instanceOf(VelocityViewResolver.class));
|
||||
accessor = new DirectFieldAccessor(resolver);
|
||||
assertEquals("", accessor.getPropertyValue("prefix"));
|
||||
assertEquals(".vm", accessor.getPropertyValue("suffix"));
|
||||
assertEquals(0, accessor.getPropertyValue("cacheLimit"));
|
||||
|
||||
resolver = resolvers.get(5);
|
||||
GroovyMarkupViewResolver groovyMarkupViewResolver = (GroovyMarkupViewResolver) resolver;
|
||||
assertThat(resolver, instanceOf(GroovyMarkupViewResolver.class));
|
||||
accessor = new DirectFieldAccessor(resolver);
|
||||
assertEquals("", accessor.getPropertyValue("prefix"));
|
||||
assertEquals(".tpl", accessor.getPropertyValue("suffix"));
|
||||
|
@ -742,7 +754,6 @@ public class MvcNamespaceTests {
|
|||
assertEquals(InternalResourceViewResolver.class, resolvers.get(6).getClass());
|
||||
assertEquals(InternalResourceViewResolver.class, resolvers.get(7).getClass());
|
||||
|
||||
|
||||
TilesConfigurer tilesConfigurer = appContext.getBean(TilesConfigurer.class);
|
||||
assertNotNull(tilesConfigurer);
|
||||
String[] definitions = {
|
||||
|
@ -841,6 +852,12 @@ public class MvcNamespaceTests {
|
|||
public @interface IsoDate {
|
||||
}
|
||||
|
||||
@NumberFormat(style = NumberFormat.Style.PERCENT)
|
||||
@Target({ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PercentNumber {
|
||||
}
|
||||
|
||||
@Validated(MyGroup.class)
|
||||
@Target({ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
@ -850,10 +867,14 @@ public class MvcNamespaceTests {
|
|||
@Controller
|
||||
public static class TestController {
|
||||
|
||||
private Date date;
|
||||
private Double percent;
|
||||
private boolean recordedValidationError;
|
||||
|
||||
@RequestMapping
|
||||
public void testBind(@RequestParam @IsoDate Date date, @MyValid TestBean bean, BindingResult result) {
|
||||
public void testBind(@RequestParam @IsoDate Date date, @RequestParam(required = false) @PercentNumber Double percent, @MyValid TestBean bean, BindingResult result) {
|
||||
this.date = date;
|
||||
this.percent = percent;
|
||||
this.recordedValidationError = (result.getErrorCount() == 1);
|
||||
}
|
||||
}
|
||||
|
@ -965,5 +986,4 @@ public class MvcNamespaceTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue