From 7e5106d1ac0c988ae2f698a6f9475207f9fcad58 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 27 Nov 2009 01:58:31 +0000 Subject: [PATCH] FormatterRegistry extends ConverterRegistry now; FormattingConversionService extends GenericConversionService --- .../format/FormatterRegistry.java | 13 +-- .../support/FormattingConversionService.java | 97 +++++++------------ ...ormattingConversionServiceFactoryBean.java | 30 +++--- .../joda/JodaTimeFormattingTests.java | 38 +++++++- .../FormattingConversionServiceTests.java | 19 ++-- .../support/ConversionServiceFactory.java | 24 +++-- 6 files changed, 119 insertions(+), 102 deletions(-) diff --git a/org.springframework.context/src/main/java/org/springframework/format/FormatterRegistry.java b/org.springframework.context/src/main/java/org/springframework/format/FormatterRegistry.java index 45511524289..319eb029534 100644 --- a/org.springframework.context/src/main/java/org/springframework/format/FormatterRegistry.java +++ b/org.springframework.context/src/main/java/org/springframework/format/FormatterRegistry.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.format; import java.lang.annotation.Annotation; @@ -23,9 +24,10 @@ import org.springframework.core.convert.converter.ConverterRegistry; * A registry of field formatting logic. * * @author Keith Donald + * @author Juergen Hoeller * @since 3.0 */ -public interface FormatterRegistry { +public interface FormatterRegistry extends ConverterRegistry { /** * Adds a Formatter to format fields of a specific type. @@ -57,14 +59,5 @@ public interface FormatterRegistry { * @param annotationFormatterFactory the annotation formatter factory to add */ void addFormatterForFieldAnnotation(AnnotationFormatterFactory annotationFormatterFactory); - - /** - * Returns the registry of Converters that coerse field values to types required by Formatters. - * Allows clients to register their own custom converters directly. - * For example, a date/time formatting configuration might expect a java.util.Date field value to be coersed to a Long for formatting. - * Registering a simpler DateToLongConverter allievates the need to register multiple formatters for closely related types. - * @return the converter registry, allowing new Converters to be registered - */ - ConverterRegistry getConverterRegistry(); } diff --git a/org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionService.java b/org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionService.java index de89a0c77a7..24b64ec8285 100644 --- a/org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionService.java +++ b/org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionService.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.format.support; import java.lang.annotation.Annotation; @@ -21,13 +22,13 @@ import java.util.Set; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.core.GenericTypeResolver; +import org.springframework.core.convert.ConversionException; import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.ConditionalGenericConverter; -import org.springframework.core.convert.converter.ConverterRegistry; import org.springframework.core.convert.converter.GenericConverter; -import org.springframework.core.convert.support.ConversionServiceFactory; +import org.springframework.core.convert.support.GenericConversionService; import org.springframework.format.AnnotationFormatterFactory; import org.springframework.format.Formatter; import org.springframework.format.FormatterRegistry; @@ -35,29 +36,30 @@ import org.springframework.format.Parser; import org.springframework.format.Printer; /** - * A ConversionService implementation designed to be configured as a {@link FormatterRegistry}.. + * A {@link org.springframework.core.convert.ConversionService} implementation + * designed to be configured as a {@link FormatterRegistry}. + * * @author Keith Donald + * @author Juergen Hoeller * @since 3.0 */ -public class FormattingConversionService implements FormatterRegistry, ConversionService { +public class FormattingConversionService extends GenericConversionService + implements FormatterRegistry { - private ConversionService conversionService = ConversionServiceFactory.createDefaultConversionService(); - - // implementing FormattingRegistry - public void addFormatterForFieldType(Class fieldType, Printer printer, Parser parser) { - getConverterRegistry().addGenericConverter(new PrinterConverter(fieldType, printer, this.conversionService)); - getConverterRegistry().addGenericConverter(new ParserConverter(fieldType, parser, this.conversionService)); + addGenericConverter(new PrinterConverter(fieldType, printer, this)); + addGenericConverter(new ParserConverter(fieldType, parser, this)); } public void addFormatterForFieldType(Class fieldType, Formatter formatter) { - getConverterRegistry().addGenericConverter(new PrinterConverter(fieldType, formatter, this.conversionService)); - getConverterRegistry().addGenericConverter(new ParserConverter(fieldType, formatter, this.conversionService)); + addGenericConverter(new PrinterConverter(fieldType, formatter, this)); + addGenericConverter(new ParserConverter(fieldType, formatter, this)); } @SuppressWarnings("unchecked") public void addFormatterForFieldAnnotation(final AnnotationFormatterFactory annotationFormatterFactory) { - final Class annotationType = resolveAnnotationType(annotationFormatterFactory); + final Class annotationType = (Class) + GenericTypeResolver.resolveTypeArgument(annotationFormatterFactory.getClass(), AnnotationFormatterFactory.class); if (annotationType == null) { throw new IllegalArgumentException( "Unable to extract parameterized Annotation type argument from AnnotationFormatterFactory [" @@ -65,74 +67,44 @@ public class FormattingConversionService implements FormatterRegistry, Conversio + "]; does the factory parameterize the generic type?"); } Set> fieldTypes = annotationFormatterFactory.getFieldTypes(); + for (final Class fieldType : fieldTypes) { - getConverterRegistry().addGenericConverter(new ConditionalGenericConverter() { + addGenericConverter(new ConditionalGenericConverter() { public Class[][] getConvertibleTypes() { - return new Class[][] { { fieldType, String.class } }; + return new Class[][] {{fieldType, String.class}}; } public boolean matches(TypeDescriptor sourceFieldType, TypeDescriptor targetFieldType) { - return sourceFieldType.getAnnotation(annotationType) != null; + return (sourceFieldType.getAnnotation(annotationType) != null); } public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { Printer printer = annotationFormatterFactory.getPrinter(sourceType.getAnnotation(annotationType), sourceType.getType()); - return new PrinterConverter(fieldType, printer, conversionService).convert(source, sourceType, targetType); + return new PrinterConverter(fieldType, printer, FormattingConversionService.this).convert(source, sourceType, targetType); } public String toString() { - return "@" + annotationType.getName() + " " + fieldType.getName() + " -> " + String.class.getName() + " : " + annotationFormatterFactory; + return "@" + annotationType.getName() + " " + fieldType.getName() + " -> " + + String.class.getName() + ": " + annotationFormatterFactory; } }); - getConverterRegistry().addGenericConverter(new ConditionalGenericConverter() { + addGenericConverter(new ConditionalGenericConverter() { public Class[][] getConvertibleTypes() { - return new Class[][] { { String.class, fieldType } }; + return new Class[][] {{String.class, fieldType}}; } public boolean matches(TypeDescriptor sourceFieldType, TypeDescriptor targetFieldType) { - return targetFieldType.getAnnotation(annotationType) != null; + return (targetFieldType.getAnnotation(annotationType) != null); } public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { Parser parser = annotationFormatterFactory.getParser(targetType.getAnnotation(annotationType), targetType.getType()); - return new ParserConverter(fieldType, parser, conversionService).convert(source, sourceType, targetType); + return new ParserConverter(fieldType, parser, FormattingConversionService.this).convert(source, sourceType, targetType); } public String toString() { - return String.class.getName() + " -> @" + annotationType.getName() + " " + fieldType.getName() + " : " + annotationFormatterFactory; + return String.class.getName() + " -> @" + annotationType.getName() + " " + + fieldType.getName() + ": " + annotationFormatterFactory; } }); } } - public ConverterRegistry getConverterRegistry() { - return (ConverterRegistry) this.conversionService; - } - // implementing ConverisonService - - public boolean canConvert(Class sourceType, Class targetType) { - return canConvert(TypeDescriptor.valueOf(sourceType), TypeDescriptor.valueOf(targetType)); - } - - @SuppressWarnings("unchecked") - public T convert(Object source, Class targetType) { - return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType)); - } - - public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) { - return this.conversionService.canConvert(sourceType, targetType); - } - - public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { - return this.conversionService.convert(source, sourceType, targetType); - } - - public String toString() { - return this.conversionService.toString(); - } - - // internal helpers - - @SuppressWarnings("unchecked") - private Class resolveAnnotationType(AnnotationFormatterFactory annotationFormatterFactory) { - return (Class) GenericTypeResolver.resolveTypeArgument(annotationFormatterFactory.getClass(), AnnotationFormatterFactory.class); - } - private static class PrinterConverter implements GenericConverter { private Class fieldType; @@ -172,6 +144,7 @@ public class FormattingConversionService implements FormatterRegistry, Conversio } } + private static class ParserConverter implements GenericConverter { private Class fieldType; @@ -198,22 +171,24 @@ public class FormattingConversionService implements FormatterRegistry, Conversio Object parsedValue; try { parsedValue = this.parser.parse(submittedValue, LocaleContextHolder.getLocale()); - } catch (ParseException e) { - throw new ConversionFailedException(sourceType, targetType, source, e); + } + catch (ParseException ex) { + throw new ConversionFailedException(sourceType, targetType, source, ex); } TypeDescriptor parsedObjectType = TypeDescriptor.valueOf(parsedValue.getClass()); if (!parsedObjectType.isAssignableTo(targetType)) { try { parsedValue = this.conversionService.convert(parsedValue, parsedObjectType, targetType); - } catch (ConversionFailedException e) { - throw new ConversionFailedException(sourceType, targetType, source, e); + } + catch (ConversionException ex) { + throw new ConversionFailedException(sourceType, targetType, source, ex); } } return parsedValue; } public String toString() { - return String.class.getName() + " -> " + this.fieldType.getName() + " : " + this.parser; + return String.class.getName() + " -> " + this.fieldType.getName() + ": " + this.parser; } } diff --git a/org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionServiceFactoryBean.java b/org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionServiceFactoryBean.java index d93cc5976fe..994194292ed 100644 --- a/org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionServiceFactoryBean.java +++ b/org.springframework.context/src/main/java/org/springframework/format/support/FormattingConversionServiceFactoryBean.java @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.format.support; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; -import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.support.ConversionServiceFactory; import org.springframework.format.FormatterRegistry; import org.springframework.format.datetime.joda.JodaTimeFormattingConfigurer; import org.springframework.format.number.NumberFormatAnnotationFormatterFactory; @@ -25,40 +26,47 @@ import org.springframework.format.number.NumberFormatter; import org.springframework.util.ClassUtils; /** - * A factory for a FormattingConversionService that installs default formatters for common types such as numbers and datetimes. - * Subclasses may override {@link #installFormatters(FormatterRegistry)} to register custom formatters. + * A factory for a {@link FormattingConversionService} that installs default + * formatters for common types such as numbers and datetimes. + * + *

Subclasses may override {@link #installFormatters(FormatterRegistry)} + * to register custom formatters. + * * @author Keith Donald + * @author Juergen Hoeller * @since 3.0 */ -public class FormattingConversionServiceFactoryBean implements FactoryBean, InitializingBean { +public class FormattingConversionServiceFactoryBean + implements FactoryBean, InitializingBean { private static final boolean jodaTimePresent = ClassUtils.isPresent( "org.joda.time.DateTime", FormattingConversionService.class.getClassLoader()); private FormattingConversionService conversionService; - // implementing InitializingBean - + public void afterPropertiesSet() { this.conversionService = new FormattingConversionService(); + ConversionServiceFactory.addDefaultConverters(this.conversionService); installFormatters(this.conversionService); } // implementing FactoryBean - public ConversionService getObject() { + public FormattingConversionService getObject() { return this.conversionService; } - public Class getObjectType() { - return ConversionService.class; + public Class getObjectType() { + return FormattingConversionService.class; } public boolean isSingleton() { return true; } - // subclassing hooks + + // subclassing hooks /** * Install Formatters and Converters into the new FormattingConversionService using the FormatterRegistry SPI. @@ -72,4 +80,4 @@ public class FormattingConversionServiceFactoryBean implements FactoryBean() { + formattingService.addConverter(new Converter() { public LocalDate convert(DateTime source) { return source.toLocalDate(); } @@ -76,18 +77,18 @@ public class FormattingConversionServiceTests { .shortDate()), new DateTimeParser(DateTimeFormat.shortDate())); String formatted = formattingService.convert(new LocalDate(2009, 10, 31), String.class); assertEquals("10/31/09", formatted); - LocalDate date = (LocalDate) formattingService.convert("10/31/09", LocalDate.class); + LocalDate date = formattingService.convert("10/31/09", LocalDate.class); assertEquals(new LocalDate(2009, 10, 31), date); } @Test public void testFormatFieldForAnnotation() throws Exception { - formattingService.getConverterRegistry().addConverter(new Converter() { + formattingService.addConverter(new Converter() { public Long convert(Date source) { return source.getTime(); } }); - formattingService.getConverterRegistry().addConverter(new Converter() { + formattingService.addConverter(new Converter() { public Date convert(DateTime source) { return source.toDate(); } diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ConversionServiceFactory.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ConversionServiceFactory.java index aea4ad964c7..0434ea78216 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/ConversionServiceFactory.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/ConversionServiceFactory.java @@ -21,21 +21,27 @@ import org.springframework.core.convert.converter.ConverterRegistry; /** * A factory for creating common ConversionService configurations. + * * @author Keith Donald + * @author Juergen Hoeller * @since 3.0 */ -public final class ConversionServiceFactory { - - private ConversionServiceFactory() { - - } +public abstract class ConversionServiceFactory { /** - * Create a new default ConversionService prototype that can be safely modified. - * Callers may cast the returned ConversionService to a {@link ConverterRegistry} to supplement or override the default converters. + * Create a new default ConversionService instance that can be safely modified. */ public static ConversionService createDefaultConversionService() { GenericConversionService conversionService = new GenericConversionService(); + addDefaultConverters(conversionService); + return conversionService; + } + + /** + * Populate the given ConversionService instance with all applicable default converters. + * Callers may cast the returned ConversionService to a {@link ConverterRegistry} to supplement or override the default converters. + */ + public static void addDefaultConverters(GenericConversionService conversionService) { conversionService.addGenericConverter(new ArrayToArrayConverter(conversionService)); conversionService.addGenericConverter(new ArrayToCollectionConverter(conversionService)); conversionService.addGenericConverter(new ArrayToMapConverter(conversionService)); @@ -51,6 +57,7 @@ public final class ConversionServiceFactory { conversionService.addGenericConverter(new ObjectToArrayConverter(conversionService)); conversionService.addGenericConverter(new ObjectToCollectionConverter(conversionService)); conversionService.addGenericConverter(new ObjectToMapConverter(conversionService)); + conversionService.addConverter(new ObjectToStringConverter()); conversionService.addConverter(new StringToBooleanConverter()); conversionService.addConverter(new StringToCharacterConverter()); conversionService.addConverter(new StringToLocaleConverter()); @@ -59,9 +66,8 @@ public final class ConversionServiceFactory { conversionService.addConverterFactory(new StringToEnumConverterFactory()); conversionService.addConverterFactory(new NumberToNumberConverterFactory()); conversionService.addConverterFactory(new CharacterToNumberFactory()); - conversionService.addConverter(new ObjectToStringConverter()); conversionService.addGenericConverter(new ObjectToObjectGenericConverter()); conversionService.addGenericConverter(new IdToEntityConverter(conversionService)); - return conversionService; } + }