Skip to content

Commit df1d90f

Browse files
committed
Merge pull request #741 from kazuki43zoo/SPR-12743
* SPR-12743: Support @numberformat as a meta-annotation
2 parents 6861d35 + c746b10 commit df1d90f

File tree

3 files changed

+56
-23
lines changed

3 files changed

+56
-23
lines changed

spring-context/src/main/java/org/springframework/format/annotation/NumberFormat.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424

2525
/**
2626
* Declares that a field should be formatted as a number.
27-
* Supports formatting by style or custom pattern string.
27+
*
28+
* <p>Supports formatting by style or custom pattern string.
2829
* Can be applied to any JDK {@code java.lang.Number} type.
2930
*
3031
* <p>For style-based formatting, set the {@link #style} attribute to be the desired {@link Style}.
@@ -41,7 +42,7 @@
4142
*/
4243
@Documented
4344
@Retention(RetentionPolicy.RUNTIME)
44-
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
45+
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
4546
public @interface NumberFormat {
4647

4748
/**

spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java

+20-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
2828
import org.joda.time.DateTime;
2929
import org.joda.time.LocalDate;
3030
import org.joda.time.format.DateTimeFormat;
31+
3132
import org.junit.After;
3233
import org.junit.Before;
3334
import org.junit.Test;
@@ -48,6 +49,7 @@
4849
import org.springframework.core.convert.support.DefaultConversionService;
4950
import org.springframework.format.Formatter;
5051
import org.springframework.format.Printer;
52+
import org.springframework.format.annotation.NumberFormat;
5153
import org.springframework.format.datetime.joda.DateTimeParser;
5254
import org.springframework.format.datetime.joda.JodaDateTimeFormatAnnotationFormatterFactory;
5355
import org.springframework.format.datetime.joda.ReadablePartialPrinter;
@@ -58,6 +60,8 @@
5860
/**
5961
* @author Keith Donald
6062
* @author Juergen Hoeller
63+
* @author Kazuki Shimizu
64+
* @author Sam Brannen
6165
*/
6266
public class FormattingConversionServiceTests {
6367

@@ -101,6 +105,7 @@ public LocalDate convert(DateTime source) {
101105
}
102106

103107
@Test
108+
@SuppressWarnings("resource")
104109
public void testFormatFieldForValueInjection() {
105110
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
106111
ac.registerBeanDefinition("valueBean", new RootBeanDefinition(ValueBean.class));
@@ -111,6 +116,7 @@ public void testFormatFieldForValueInjection() {
111116
}
112117

113118
@Test
119+
@SuppressWarnings("resource")
114120
public void testFormatFieldForValueInjectionUsingMetaAnnotations() {
115121
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
116122
RootBeanDefinition bd = new RootBeanDefinition(MetaValueBean.class);
@@ -120,12 +126,15 @@ public void testFormatFieldForValueInjectionUsingMetaAnnotations() {
120126
ac.registerBeanDefinition("ppc", new RootBeanDefinition(PropertyPlaceholderConfigurer.class));
121127
ac.refresh();
122128
System.setProperty("myDate", "10-31-09");
129+
System.setProperty("myNumber", "99.99%");
123130
try {
124131
MetaValueBean valueBean = ac.getBean(MetaValueBean.class);
125132
assertEquals(new LocalDate(2009, 10, 31), new LocalDate(valueBean.date));
133+
assertEquals(Double.valueOf(0.9999), valueBean.number);
126134
}
127135
finally {
128136
System.clearProperty("myDate");
137+
System.clearProperty("myNumber");
129138
}
130139
}
131140

@@ -142,6 +151,7 @@ public void testFormatFieldForAnnotationWithDirectFieldAccess() throws Exception
142151
}
143152

144153
@Test
154+
@SuppressWarnings("resource")
145155
public void testFormatFieldForAnnotationWithPlaceholders() throws Exception {
146156
GenericApplicationContext context = new GenericApplicationContext();
147157
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
@@ -157,6 +167,7 @@ public void testFormatFieldForAnnotationWithPlaceholders() throws Exception {
157167
}
158168

159169
@Test
170+
@SuppressWarnings("resource")
160171
public void testFormatFieldForAnnotationWithPlaceholdersAndFactoryBean() throws Exception {
161172
GenericApplicationContext context = new GenericApplicationContext();
162173
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
@@ -336,20 +347,26 @@ public static class ValueBean {
336347
public Date date;
337348
}
338349

339-
340350
public static class MetaValueBean {
341351

342352
@MyDateAnn
343353
public Date date;
344-
}
345354

355+
@MyNumberAnn
356+
public Double number;
357+
}
346358

347359
@Value("${myDate}")
348360
@org.springframework.format.annotation.DateTimeFormat(pattern="MM-d-yy")
349361
@Retention(RetentionPolicy.RUNTIME)
350362
public static @interface MyDateAnn {
351363
}
352364

365+
@Value("${myNumber}")
366+
@NumberFormat(style = NumberFormat.Style.PERCENT)
367+
@Retention(RetentionPolicy.RUNTIME)
368+
public static @interface MyNumberAnn {
369+
}
353370

354371
public static class Model {
355372

@@ -368,7 +385,6 @@ public void setDates(List<Date> dates) {
368385
}
369386
}
370387

371-
372388
public static class ModelWithPlaceholders {
373389

374390
@org.springframework.format.annotation.DateTimeFormat(style="${dateStyle}")
@@ -386,13 +402,11 @@ public void setDates(List<Date> dates) {
386402
}
387403
}
388404

389-
390405
@org.springframework.format.annotation.DateTimeFormat(pattern="${datePattern}")
391406
@Retention(RetentionPolicy.RUNTIME)
392407
public static @interface MyDatePattern {
393408
}
394409

395-
396410
public static class NullReturningFormatter implements Formatter<Integer> {
397411

398412
@Override
@@ -407,12 +421,10 @@ public Integer parse(String text, Locale locale) throws ParseException {
407421

408422
}
409423

410-
411424
@SuppressWarnings("serial")
412425
public static class MyDate extends Date {
413426
}
414427

415-
416428
private static class ModelWithSubclassField {
417429

418430
@org.springframework.format.annotation.DateTimeFormat(style = "S-")

spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java

+33-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,14 +29,14 @@
2929
import java.util.List;
3030
import java.util.Locale;
3131
import java.util.Map;
32+
3233
import javax.servlet.RequestDispatcher;
3334
import javax.validation.constraints.NotNull;
3435

35-
import com.fasterxml.jackson.databind.DeserializationFeature;
36-
import com.fasterxml.jackson.databind.MapperFeature;
37-
import com.fasterxml.jackson.databind.ObjectMapper;
38-
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
3936
import org.hamcrest.Matchers;
37+
38+
import org.joda.time.LocalDate;
39+
4040
import org.junit.Before;
4141
import org.junit.Test;
4242

@@ -52,6 +52,7 @@
5252
import org.springframework.core.io.ClassPathResource;
5353
import org.springframework.format.annotation.DateTimeFormat;
5454
import org.springframework.format.annotation.DateTimeFormat.ISO;
55+
import org.springframework.format.annotation.NumberFormat;
5556
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
5657
import org.springframework.http.MediaType;
5758
import org.springframework.http.converter.HttpMessageConverter;
@@ -132,6 +133,12 @@
132133
import org.springframework.web.servlet.view.velocity.VelocityViewResolver;
133134
import org.springframework.web.util.UrlPathHelper;
134135

136+
import com.fasterxml.jackson.databind.DeserializationFeature;
137+
import com.fasterxml.jackson.databind.MapperFeature;
138+
import com.fasterxml.jackson.databind.ObjectMapper;
139+
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
140+
141+
import static org.hamcrest.CoreMatchers.*;
135142
import static org.junit.Assert.*;
136143

137144
/**
@@ -142,11 +149,13 @@
142149
* @author Jeremy Grelle
143150
* @author Brian Clozel
144151
* @author Sebastien Deleuze
152+
* @author Kazuki Shimizu
153+
* @author Sam Brannen
145154
*/
146155
public class MvcNamespaceTests {
147156

148157
public static final String VIEWCONTROLLER_BEAN_NAME = "org.springframework.web.servlet.config.viewControllerHandlerMapping";
149-
158+
150159
private GenericWebApplicationContext appContext;
151160

152161
private TestController handler;
@@ -165,7 +174,7 @@ public void setUp() throws Exception {
165174
appContext.getServletContext().setAttribute(attributeName, appContext);
166175

167176
handler = new TestController();
168-
Method method = TestController.class.getMethod("testBind", Date.class, TestBean.class, BindingResult.class);
177+
Method method = TestController.class.getMethod("testBind", Date.class, Double.class, TestBean.class, BindingResult.class);
169178
handlerMethod = new InvocableHandlerMethod(handler, method);
170179
}
171180

@@ -211,6 +220,7 @@ public void testDefaultConfig() throws Exception {
211220
// default web binding initializer behavior test
212221
request = new MockHttpServletRequest("GET", "/");
213222
request.addParameter("date", "2009-10-31");
223+
request.addParameter("percent", "99.99%");
214224
MockHttpServletResponse response = new MockHttpServletResponse();
215225

216226
HandlerExecutionChain chain = mapping.getHandler(request);
@@ -222,6 +232,8 @@ public void testDefaultConfig() throws Exception {
222232

223233
adapter.handle(request, response, handlerMethod);
224234
assertTrue(handler.recordedValidationError);
235+
assertEquals(LocalDate.parse("2009-10-31").toDate(), handler.date);
236+
assertEquals(Double.valueOf(0.9999),handler.percent);
225237

226238
CompositeUriComponentsContributor uriComponentsContributor = this.appContext.getBean(
227239
MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME,
@@ -718,22 +730,22 @@ public void testViewResolution() throws Exception {
718730
assertEquals(TilesViewResolver.class, resolvers.get(2).getClass());
719731

720732
resolver = resolvers.get(3);
721-
FreeMarkerViewResolver freeMarkerViewResolver = (FreeMarkerViewResolver) resolver;
733+
assertThat(resolver, instanceOf(FreeMarkerViewResolver.class));
722734
accessor = new DirectFieldAccessor(resolver);
723735
assertEquals("freemarker-", accessor.getPropertyValue("prefix"));
724736
assertEquals(".freemarker", accessor.getPropertyValue("suffix"));
725737
assertArrayEquals(new String[] {"my*", "*Report"}, (String[]) accessor.getPropertyValue("viewNames"));
726738
assertEquals(1024, accessor.getPropertyValue("cacheLimit"));
727739

728740
resolver = resolvers.get(4);
729-
VelocityViewResolver velocityViewResolver = (VelocityViewResolver) resolver;
741+
assertThat(resolver, instanceOf(VelocityViewResolver.class));
730742
accessor = new DirectFieldAccessor(resolver);
731743
assertEquals("", accessor.getPropertyValue("prefix"));
732744
assertEquals(".vm", accessor.getPropertyValue("suffix"));
733745
assertEquals(0, accessor.getPropertyValue("cacheLimit"));
734746

735747
resolver = resolvers.get(5);
736-
GroovyMarkupViewResolver groovyMarkupViewResolver = (GroovyMarkupViewResolver) resolver;
748+
assertThat(resolver, instanceOf(GroovyMarkupViewResolver.class));
737749
accessor = new DirectFieldAccessor(resolver);
738750
assertEquals("", accessor.getPropertyValue("prefix"));
739751
assertEquals(".tpl", accessor.getPropertyValue("suffix"));
@@ -742,7 +754,6 @@ public void testViewResolution() throws Exception {
742754
assertEquals(InternalResourceViewResolver.class, resolvers.get(6).getClass());
743755
assertEquals(InternalResourceViewResolver.class, resolvers.get(7).getClass());
744756

745-
746757
TilesConfigurer tilesConfigurer = appContext.getBean(TilesConfigurer.class);
747758
assertNotNull(tilesConfigurer);
748759
String[] definitions = {
@@ -841,6 +852,12 @@ private void loadBeanDefinitions(String fileName, int expectedBeanCount) {
841852
public @interface IsoDate {
842853
}
843854

855+
@NumberFormat(style = NumberFormat.Style.PERCENT)
856+
@Target({ElementType.PARAMETER})
857+
@Retention(RetentionPolicy.RUNTIME)
858+
public @interface PercentNumber {
859+
}
860+
844861
@Validated(MyGroup.class)
845862
@Target({ElementType.PARAMETER})
846863
@Retention(RetentionPolicy.RUNTIME)
@@ -850,10 +867,14 @@ private void loadBeanDefinitions(String fileName, int expectedBeanCount) {
850867
@Controller
851868
public static class TestController {
852869

870+
private Date date;
871+
private Double percent;
853872
private boolean recordedValidationError;
854873

855874
@RequestMapping
856-
public void testBind(@RequestParam @IsoDate Date date, @MyValid TestBean bean, BindingResult result) {
875+
public void testBind(@RequestParam @IsoDate Date date, @RequestParam(required = false) @PercentNumber Double percent, @MyValid TestBean bean, BindingResult result) {
876+
this.date = date;
877+
this.percent = percent;
857878
this.recordedValidationError = (result.getErrorCount() == 1);
858879
}
859880
}
@@ -965,5 +986,4 @@ public Collection<String> getCacheNames() {
965986
}
966987
}
967988

968-
969989
}

0 commit comments

Comments
 (0)