Skip to content

Commit 7e5106d

Browse files
committed
FormatterRegistry extends ConverterRegistry now; FormattingConversionService extends GenericConversionService
1 parent a1916ca commit 7e5106d

File tree

6 files changed

+118
-101
lines changed

6 files changed

+118
-101
lines changed

org.springframework.context/src/main/java/org/springframework/format/FormatterRegistry.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
package org.springframework.format;
1718

1819
import java.lang.annotation.Annotation;
@@ -23,9 +24,10 @@
2324
* A registry of field formatting logic.
2425
*
2526
* @author Keith Donald
27+
* @author Juergen Hoeller
2628
* @since 3.0
2729
*/
28-
public interface FormatterRegistry {
30+
public interface FormatterRegistry extends ConverterRegistry {
2931

3032
/**
3133
* Adds a Formatter to format fields of a specific type.
@@ -57,14 +59,5 @@ public interface FormatterRegistry {
5759
* @param annotationFormatterFactory the annotation formatter factory to add
5860
*/
5961
void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory);
60-
61-
/**
62-
* Returns the registry of Converters that coerse field values to types required by Formatters.
63-
* Allows clients to register their own custom converters directly.
64-
* For example, a date/time formatting configuration might expect a java.util.Date field value to be coersed to a Long for formatting.
65-
* Registering a simpler DateToLongConverter allievates the need to register multiple formatters for closely related types.
66-
* @return the converter registry, allowing new Converters to be registered
67-
*/
68-
ConverterRegistry getConverterRegistry();
6962

7063
}

org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionService.java

Lines changed: 36 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
package org.springframework.format.support;
1718

1819
import java.lang.annotation.Annotation;
@@ -21,118 +22,89 @@
2122

2223
import org.springframework.context.i18n.LocaleContextHolder;
2324
import org.springframework.core.GenericTypeResolver;
25+
import org.springframework.core.convert.ConversionException;
2426
import org.springframework.core.convert.ConversionFailedException;
2527
import org.springframework.core.convert.ConversionService;
2628
import org.springframework.core.convert.TypeDescriptor;
2729
import org.springframework.core.convert.converter.ConditionalGenericConverter;
28-
import org.springframework.core.convert.converter.ConverterRegistry;
2930
import org.springframework.core.convert.converter.GenericConverter;
30-
import org.springframework.core.convert.support.ConversionServiceFactory;
31+
import org.springframework.core.convert.support.GenericConversionService;
3132
import org.springframework.format.AnnotationFormatterFactory;
3233
import org.springframework.format.Formatter;
3334
import org.springframework.format.FormatterRegistry;
3435
import org.springframework.format.Parser;
3536
import org.springframework.format.Printer;
3637

3738
/**
38-
* A ConversionService implementation designed to be configured as a {@link FormatterRegistry}..
39+
* A {@link org.springframework.core.convert.ConversionService} implementation
40+
* designed to be configured as a {@link FormatterRegistry}.
41+
*
3942
* @author Keith Donald
43+
* @author Juergen Hoeller
4044
* @since 3.0
4145
*/
42-
public class FormattingConversionService implements FormatterRegistry, ConversionService {
46+
public class FormattingConversionService extends GenericConversionService
47+
implements FormatterRegistry {
4348

44-
private ConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
45-
46-
// implementing FormattingRegistry
47-
4849
public void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser) {
49-
getConverterRegistry().addGenericConverter(new PrinterConverter(fieldType, printer, this.conversionService));
50-
getConverterRegistry().addGenericConverter(new ParserConverter(fieldType, parser, this.conversionService));
50+
addGenericConverter(new PrinterConverter(fieldType, printer, this));
51+
addGenericConverter(new ParserConverter(fieldType, parser, this));
5152
}
5253

5354
public void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter) {
54-
getConverterRegistry().addGenericConverter(new PrinterConverter(fieldType, formatter, this.conversionService));
55-
getConverterRegistry().addGenericConverter(new ParserConverter(fieldType, formatter, this.conversionService));
55+
addGenericConverter(new PrinterConverter(fieldType, formatter, this));
56+
addGenericConverter(new ParserConverter(fieldType, formatter, this));
5657
}
5758

5859
@SuppressWarnings("unchecked")
5960
public void addFormatterForFieldAnnotation(final AnnotationFormatterFactory annotationFormatterFactory) {
60-
final Class<? extends Annotation> annotationType = resolveAnnotationType(annotationFormatterFactory);
61+
final Class<? extends Annotation> annotationType = (Class<? extends Annotation>)
62+
GenericTypeResolver.resolveTypeArgument(annotationFormatterFactory.getClass(), AnnotationFormatterFactory.class);
6163
if (annotationType == null) {
6264
throw new IllegalArgumentException(
6365
"Unable to extract parameterized Annotation type argument from AnnotationFormatterFactory ["
6466
+ annotationFormatterFactory.getClass().getName()
6567
+ "]; does the factory parameterize the <A extends Annotation> generic type?");
6668
}
6769
Set<Class<?>> fieldTypes = annotationFormatterFactory.getFieldTypes();
70+
6871
for (final Class<?> fieldType : fieldTypes) {
69-
getConverterRegistry().addGenericConverter(new ConditionalGenericConverter() {
72+
addGenericConverter(new ConditionalGenericConverter() {
7073
public Class<?>[][] getConvertibleTypes() {
71-
return new Class<?>[][] { { fieldType, String.class } };
74+
return new Class<?>[][] {{fieldType, String.class}};
7275
}
7376
public boolean matches(TypeDescriptor sourceFieldType, TypeDescriptor targetFieldType) {
74-
return sourceFieldType.getAnnotation(annotationType) != null;
77+
return (sourceFieldType.getAnnotation(annotationType) != null);
7578
}
7679
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
7780
Printer<?> printer = annotationFormatterFactory.getPrinter(sourceType.getAnnotation(annotationType), sourceType.getType());
78-
return new PrinterConverter(fieldType, printer, conversionService).convert(source, sourceType, targetType);
81+
return new PrinterConverter(fieldType, printer, FormattingConversionService.this).convert(source, sourceType, targetType);
7982
}
8083
public String toString() {
81-
return "@" + annotationType.getName() + " " + fieldType.getName() + " -> " + String.class.getName() + " : " + annotationFormatterFactory;
84+
return "@" + annotationType.getName() + " " + fieldType.getName() + " -> " +
85+
String.class.getName() + ": " + annotationFormatterFactory;
8286
}
8387
});
84-
getConverterRegistry().addGenericConverter(new ConditionalGenericConverter() {
88+
addGenericConverter(new ConditionalGenericConverter() {
8589
public Class<?>[][] getConvertibleTypes() {
86-
return new Class<?>[][] { { String.class, fieldType } };
90+
return new Class<?>[][] {{String.class, fieldType}};
8791
}
8892
public boolean matches(TypeDescriptor sourceFieldType, TypeDescriptor targetFieldType) {
89-
return targetFieldType.getAnnotation(annotationType) != null;
93+
return (targetFieldType.getAnnotation(annotationType) != null);
9094
}
9195
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
9296
Parser<?> parser = annotationFormatterFactory.getParser(targetType.getAnnotation(annotationType), targetType.getType());
93-
return new ParserConverter(fieldType, parser, conversionService).convert(source, sourceType, targetType);
97+
return new ParserConverter(fieldType, parser, FormattingConversionService.this).convert(source, sourceType, targetType);
9498
}
9599
public String toString() {
96-
return String.class.getName() + " -> @" + annotationType.getName() + " " + fieldType.getName() + " : " + annotationFormatterFactory;
100+
return String.class.getName() + " -> @" + annotationType.getName() + " " +
101+
fieldType.getName() + ": " + annotationFormatterFactory;
97102
}
98103
});
99104
}
100105
}
101106

102-
public ConverterRegistry getConverterRegistry() {
103-
return (ConverterRegistry) this.conversionService;
104-
}
105-
106-
// implementing ConverisonService
107107

108-
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
109-
return canConvert(TypeDescriptor.valueOf(sourceType), TypeDescriptor.valueOf(targetType));
110-
}
111-
112-
@SuppressWarnings("unchecked")
113-
public <T> T convert(Object source, Class<T> targetType) {
114-
return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType));
115-
}
116-
117-
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
118-
return this.conversionService.canConvert(sourceType, targetType);
119-
}
120-
121-
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
122-
return this.conversionService.convert(source, sourceType, targetType);
123-
}
124-
125-
public String toString() {
126-
return this.conversionService.toString();
127-
}
128-
129-
// internal helpers
130-
131-
@SuppressWarnings("unchecked")
132-
private Class<? extends Annotation> resolveAnnotationType(AnnotationFormatterFactory<?> annotationFormatterFactory) {
133-
return (Class<? extends Annotation>) GenericTypeResolver.resolveTypeArgument(annotationFormatterFactory.getClass(), AnnotationFormatterFactory.class);
134-
}
135-
136108
private static class PrinterConverter implements GenericConverter {
137109

138110
private Class<?> fieldType;
@@ -172,6 +144,7 @@ public String toString() {
172144
}
173145
}
174146

147+
175148
private static class ParserConverter implements GenericConverter {
176149

177150
private Class<?> fieldType;
@@ -198,22 +171,24 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t
198171
Object parsedValue;
199172
try {
200173
parsedValue = this.parser.parse(submittedValue, LocaleContextHolder.getLocale());
201-
} catch (ParseException e) {
202-
throw new ConversionFailedException(sourceType, targetType, source, e);
174+
}
175+
catch (ParseException ex) {
176+
throw new ConversionFailedException(sourceType, targetType, source, ex);
203177
}
204178
TypeDescriptor parsedObjectType = TypeDescriptor.valueOf(parsedValue.getClass());
205179
if (!parsedObjectType.isAssignableTo(targetType)) {
206180
try {
207181
parsedValue = this.conversionService.convert(parsedValue, parsedObjectType, targetType);
208-
} catch (ConversionFailedException e) {
209-
throw new ConversionFailedException(sourceType, targetType, source, e);
182+
}
183+
catch (ConversionException ex) {
184+
throw new ConversionFailedException(sourceType, targetType, source, ex);
210185
}
211186
}
212187
return parsedValue;
213188
}
214189

215190
public String toString() {
216-
return String.class.getName() + " -> " + this.fieldType.getName() + " : " + this.parser;
191+
return String.class.getName() + " -> " + this.fieldType.getName() + ": " + this.parser;
217192
}
218193

219194
}

org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionServiceFactoryBean.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,52 +13,60 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
package org.springframework.format.support;
1718

1819
import org.springframework.beans.factory.FactoryBean;
1920
import org.springframework.beans.factory.InitializingBean;
20-
import org.springframework.core.convert.ConversionService;
21+
import org.springframework.core.convert.support.ConversionServiceFactory;
2122
import org.springframework.format.FormatterRegistry;
2223
import org.springframework.format.datetime.joda.JodaTimeFormattingConfigurer;
2324
import org.springframework.format.number.NumberFormatAnnotationFormatterFactory;
2425
import org.springframework.format.number.NumberFormatter;
2526
import org.springframework.util.ClassUtils;
2627

2728
/**
28-
* A factory for a FormattingConversionService that installs default formatters for common types such as numbers and datetimes.
29-
* Subclasses may override {@link #installFormatters(FormatterRegistry)} to register custom formatters.
29+
* A factory for a {@link FormattingConversionService} that installs default
30+
* formatters for common types such as numbers and datetimes.
31+
*
32+
* <p>Subclasses may override {@link #installFormatters(FormatterRegistry)}
33+
* to register custom formatters.
34+
*
3035
* @author Keith Donald
36+
* @author Juergen Hoeller
3137
* @since 3.0
3238
*/
33-
public class FormattingConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
39+
public class FormattingConversionServiceFactoryBean
40+
implements FactoryBean<FormattingConversionService>, InitializingBean {
3441

3542
private static final boolean jodaTimePresent = ClassUtils.isPresent(
3643
"org.joda.time.DateTime", FormattingConversionService.class.getClassLoader());
3744

3845
private FormattingConversionService conversionService;
3946

40-
// implementing InitializingBean
41-
47+
4248
public void afterPropertiesSet() {
4349
this.conversionService = new FormattingConversionService();
50+
ConversionServiceFactory.addDefaultConverters(this.conversionService);
4451
installFormatters(this.conversionService);
4552
}
4653

4754
// implementing FactoryBean
4855

49-
public ConversionService getObject() {
56+
public FormattingConversionService getObject() {
5057
return this.conversionService;
5158
}
5259

53-
public Class<ConversionService> getObjectType() {
54-
return ConversionService.class;
60+
public Class<? extends FormattingConversionService> getObjectType() {
61+
return FormattingConversionService.class;
5562
}
5663

5764
public boolean isSingleton() {
5865
return true;
5966
}
6067

61-
// subclassing hooks
68+
69+
// subclassing hooks
6270

6371
/**
6472
* Install Formatters and Converters into the new FormattingConversionService using the FormatterRegistry SPI.
@@ -72,4 +80,4 @@ protected void installFormatters(FormatterRegistry registry) {
7280
}
7381
}
7482

75-
}
83+
}

org.springframework.context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1-
package org.springframework.format.datetime.joda;
1+
/*
2+
* Copyright 2002-2009 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
216

3-
import static org.junit.Assert.assertEquals;
17+
package org.springframework.format.datetime.joda;
418

519
import java.util.Calendar;
620
import java.util.Date;
@@ -12,15 +26,22 @@
1226
import org.joda.time.LocalDateTime;
1327
import org.joda.time.LocalTime;
1428
import org.junit.After;
29+
import static org.junit.Assert.*;
1530
import org.junit.Before;
1631
import org.junit.Test;
32+
1733
import org.springframework.beans.MutablePropertyValues;
1834
import org.springframework.context.i18n.LocaleContextHolder;
35+
import org.springframework.core.convert.support.ConversionServiceFactory;
1936
import org.springframework.format.annotation.DateTimeFormat;
2037
import org.springframework.format.annotation.DateTimeFormat.ISO;
2138
import org.springframework.format.support.FormattingConversionService;
2239
import org.springframework.validation.DataBinder;
2340

41+
/**
42+
* @author Keith Donald
43+
* @author Juergen Hoeller
44+
*/
2445
public class JodaTimeFormattingTests {
2546

2647
private FormattingConversionService conversionService = new FormattingConversionService();
@@ -29,6 +50,8 @@ public class JodaTimeFormattingTests {
2950

3051
@Before
3152
public void setUp() {
53+
ConversionServiceFactory.addDefaultConverters(conversionService);
54+
3255
JodaTimeFormattingConfigurer configurer = new JodaTimeFormattingConfigurer();
3356
configurer.installJodaTimeFormatting(conversionService);
3457

@@ -81,6 +104,16 @@ public void testBindLocalDateAnnotated() {
81104
assertEquals("Oct 31, 2009", binder.getBindingResult().getFieldValue("localDateAnnotated"));
82105
}
83106

107+
@Test
108+
public void testBindLocalDateAnnotatedWithDirectFieldAccess() {
109+
binder.initDirectFieldAccess();
110+
MutablePropertyValues propertyValues = new MutablePropertyValues();
111+
propertyValues.add("localDateAnnotated", "Oct 31, 2009");
112+
binder.bind(propertyValues);
113+
assertEquals(0, binder.getBindingResult().getErrorCount());
114+
assertEquals("Oct 31, 2009", binder.getBindingResult().getFieldValue("localDateAnnotated"));
115+
}
116+
84117
@Test
85118
public void testBindLocalTime() {
86119
MutablePropertyValues propertyValues = new MutablePropertyValues();
@@ -235,6 +268,7 @@ public void testBindISODateTime() {
235268
assertEquals("2009-10-31T07:00:00.000-05:00", binder.getBindingResult().getFieldValue("isoDateTime"));
236269
}
237270

271+
238272
@SuppressWarnings("unused")
239273
private static class JodaTimeBean {
240274

0 commit comments

Comments
 (0)