diff --git a/spring-context/src/main/java/org/springframework/format/annotation/NumberFormat.java b/spring-context/src/main/java/org/springframework/format/annotation/NumberFormat.java index 944a276cb0..7418ec6b2f 100644 --- a/spring-context/src/main/java/org/springframework/format/annotation/NumberFormat.java +++ b/spring-context/src/main/java/org/springframework/format/annotation/NumberFormat.java @@ -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. + * + *

Supports formatting by style or custom pattern string. * Can be applied to any JDK {@code java.lang.Number} type. * *

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 { /** diff --git a/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java b/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java index a2930e3e7e..cc1b36ec27 100644 --- a/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java +++ b/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java @@ -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 { @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-") diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java index a7de588e6c..0967961bd5 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java @@ -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 { } } - }