diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java index dedda9eb1f1..87c1b585da4 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -136,7 +136,7 @@ class TypeConverterDelegate { ConversionService conversionService = this.propertyEditorRegistry.getConversionService(); if (editor == null && conversionService != null && convertedValue != null) { TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(convertedValue); - TypeDescriptor targetTypeDesc = typeDescriptor.forElementType(requiredType); + TypeDescriptor targetTypeDesc = typeDescriptor; if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) { return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc); } 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 5749c249c11..a75348d009b 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -95,7 +95,7 @@ public class FormattingConversionService extends GenericConversionService return (sourceType.getAnnotation(annotationType) != null); } public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { - FieldFormatterKey key = new FieldFormatterKey(sourceType.getAnnotation(annotationType), fieldType); + FieldFormatterKey key = new FieldFormatterKey(sourceType.getAnnotation(annotationType), sourceType.getType()); GenericConverter converter = cachedPrinters.get(key); if (converter == null) { Printer printer = annotationFormatterFactory.getPrinter(key.getAnnotation(), key.getFieldType()); @@ -117,7 +117,7 @@ public class FormattingConversionService extends GenericConversionService return (targetType.getAnnotation(annotationType) != null); } public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { - FieldFormatterKey key = new FieldFormatterKey(targetType.getAnnotation(annotationType), fieldType); + FieldFormatterKey key = new FieldFormatterKey(targetType.getAnnotation(annotationType), sourceType.getType()); GenericConverter converter = cachedParsers.get(key); if (converter == null) { Parser printer = annotationFormatterFactory.getParser(key.getAnnotation(), key.getFieldType()); diff --git a/org.springframework.context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java b/org.springframework.context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java index a37809574cf..39823903be2 100644 --- a/org.springframework.context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java +++ b/org.springframework.context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -27,12 +27,11 @@ import org.joda.time.DateTime; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.junit.After; -import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; import org.springframework.beans.BeanUtils; -import org.springframework.beans.BeanWrapper; +import org.springframework.beans.ConfigurablePropertyAccessor; import org.springframework.beans.PropertyAccessorFactory; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -41,11 +40,14 @@ import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.support.ConversionServiceFactory; +import org.springframework.format.Printer; import org.springframework.format.datetime.joda.DateTimeParser; import org.springframework.format.datetime.joda.JodaDateTimeFormatAnnotationFormatterFactory; import org.springframework.format.datetime.joda.ReadablePartialPrinter; import org.springframework.format.number.NumberFormatter; +import static org.junit.Assert.*; + /** * @author Keith Donald * @author Juergen Hoeller @@ -93,7 +95,13 @@ public class FormattingConversionServiceTests { @Test public void testFormatFieldForAnnotation() throws Exception { formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory()); - doTestFormatFieldForAnnotation(Model.class); + doTestFormatFieldForAnnotation(Model.class, false); + } + + @Test + public void testFormatFieldForAnnotationWithDirectFieldAccess() throws Exception { + formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory()); + doTestFormatFieldForAnnotation(Model.class, true); } @Test @@ -108,7 +116,7 @@ public class FormattingConversionServiceTests { context.refresh(); context.getBeanFactory().initializeBean(formattingService, "formattingService"); formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory()); - doTestFormatFieldForAnnotation(ModelWithPlaceholders.class); + doTestFormatFieldForAnnotation(ModelWithPlaceholders.class, false); } @Test @@ -123,10 +131,10 @@ public class FormattingConversionServiceTests { context.getBeanFactory().registerSingleton("ppc", ppc); context.refresh(); formattingService = context.getBean("formattingService", FormattingConversionService.class); - doTestFormatFieldForAnnotation(ModelWithPlaceholders.class); + doTestFormatFieldForAnnotation(ModelWithPlaceholders.class, false); } - private void doTestFormatFieldForAnnotation(Class modelClass) throws Exception { + private void doTestFormatFieldForAnnotation(Class modelClass, boolean directFieldAccess) throws Exception { formattingService.addConverter(new Converter() { public Long convert(Date source) { return source.getTime(); @@ -159,26 +167,30 @@ public class FormattingConversionServiceTests { assertEquals(new LocalDate(2009, 11, 2), new LocalDate(dates.get(2))); Object model = BeanUtils.instantiate(modelClass); - BeanWrapper accessor = PropertyAccessorFactory.forBeanPropertyAccess(model); + ConfigurablePropertyAccessor accessor = directFieldAccess ? PropertyAccessorFactory.forDirectFieldAccess(model) : + PropertyAccessorFactory.forBeanPropertyAccess(model); accessor.setConversionService(formattingService); accessor.setPropertyValue("dates", "10-31-09,11-1-09,11-2-09"); dates = (List) accessor.getPropertyValue("dates"); assertEquals(new LocalDate(2009, 10, 31), new LocalDate(dates.get(0))); assertEquals(new LocalDate(2009, 11, 1), new LocalDate(dates.get(1))); assertEquals(new LocalDate(2009, 11, 2), new LocalDate(dates.get(2))); - accessor.setPropertyValue("dates[0]", "10-30-09"); - accessor.setPropertyValue("dates[1]", "10-1-09"); - accessor.setPropertyValue("dates[2]", "10-2-09"); - dates = (List) accessor.getPropertyValue("dates"); - assertEquals(new LocalDate(2009, 10, 30), new LocalDate(dates.get(0))); - assertEquals(new LocalDate(2009, 10, 1), new LocalDate(dates.get(1))); - assertEquals(new LocalDate(2009, 10, 2), new LocalDate(dates.get(2))); + if (!directFieldAccess) { + accessor.setPropertyValue("dates[0]", "10-30-09"); + accessor.setPropertyValue("dates[1]", "10-1-09"); + accessor.setPropertyValue("dates[2]", "10-2-09"); + dates = (List) accessor.getPropertyValue("dates"); + assertEquals(new LocalDate(2009, 10, 30), new LocalDate(dates.get(0))); + assertEquals(new LocalDate(2009, 10, 1), new LocalDate(dates.get(1))); + assertEquals(new LocalDate(2009, 10, 2), new LocalDate(dates.get(2))); + } } - + @Test public void testPrintNull() throws ParseException { formattingService.addFormatterForFieldType(Number.class, new NumberFormatter()); - assertEquals("", formattingService.convert(null, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(String.class))); + assertEquals("", formattingService + .convert(null, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(String.class))); } @Test @@ -190,7 +202,8 @@ public class FormattingConversionServiceTests { @Test public void testParseEmptyString() throws ParseException { formattingService.addFormatterForFieldType(Number.class, new NumberFormatter()); - assertNull(formattingService.convert("", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class))); + assertNull(formattingService + .convert("", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class))); } @Test @@ -208,14 +221,35 @@ public class FormattingConversionServiceTests { assertNull(formattingService.convert("", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class))); } + @Test + public void testFormatFieldForAnnotationWithSubclassAsFieldType() throws Exception { + formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory() { + public Printer getPrinter(org.springframework.format.annotation.DateTimeFormat annotation, Class fieldType) { + assertEquals(MyDate.class, fieldType); + return super.getPrinter(annotation, fieldType); + } + }); + formattingService.addConverter(new Converter() { + public Long convert(MyDate source) { + return source.getTime(); + } + }); + formattingService.addConverter(new Converter() { + public Date convert(MyDate source) { + return source; + } + }); + + formattingService.convert(new MyDate(), new TypeDescriptor(ModelWithSubclassField.class.getField("date")), + TypeDescriptor.valueOf(String.class)); + } + public static class Model { - @SuppressWarnings("unused") @org.springframework.format.annotation.DateTimeFormat(style="S-") public Date date; - @SuppressWarnings("unused") @org.springframework.format.annotation.DateTimeFormat(pattern="M-d-yy") public List dates; @@ -231,11 +265,9 @@ public class FormattingConversionServiceTests { public static class ModelWithPlaceholders { - @SuppressWarnings("unused") @org.springframework.format.annotation.DateTimeFormat(style="${dateStyle}") public Date date; - @SuppressWarnings("unused") @org.springframework.format.annotation.DateTimeFormat(pattern="${datePattern}") public List dates; @@ -248,4 +280,15 @@ public class FormattingConversionServiceTests { } } + + public static class MyDate extends Date { + } + + + private static class ModelWithSubclassField { + + @org.springframework.format.annotation.DateTimeFormat(style = "S-") + public MyDate date; + } + } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java index 3d361e66c8a..648275c4f32 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -50,7 +50,6 @@ import javax.validation.Valid; import javax.validation.constraints.NotNull; import javax.xml.bind.annotation.XmlRootElement; -import static org.junit.Assert.*; import org.junit.Test; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; @@ -134,6 +133,8 @@ import org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMap import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.util.NestedServletException; +import static org.junit.Assert.*; + /** * @author Juergen Hoeller * @author Sam Brannen @@ -1156,6 +1157,7 @@ public class ServletAnnotationControllerTests { MockHttpServletResponse response = new MockHttpServletResponse(); try { servlet.service(request, response); + fail("Didn't fail with due to ambiguous method mapping"); } catch (NestedServletException ex) { assertTrue(ex.getCause() instanceof IllegalStateException); @@ -1815,7 +1817,7 @@ public class ServletAnnotationControllerTests { } @Test - public void parameterCsvAsStringArray() throws Exception { + public void parameterCsvAsIntegerArray() throws Exception { servlet = new DispatcherServlet() { @Override protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { @@ -1842,6 +1844,34 @@ public class ServletAnnotationControllerTests { assertEquals("1-2", response.getContentAsString()); } + @Test + public void parameterCsvAsIntegerSet() throws Exception { + servlet = new DispatcherServlet() { + @Override + protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { + GenericWebApplicationContext wac = new GenericWebApplicationContext(); + wac.registerBeanDefinition("controller", new RootBeanDefinition(CsvController.class)); + RootBeanDefinition csDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class); + RootBeanDefinition wbiDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class); + wbiDef.getPropertyValues().add("conversionService", csDef); + RootBeanDefinition adapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class); + adapterDef.getPropertyValues().add("webBindingInitializer", wbiDef); + wac.registerBeanDefinition("handlerAdapter", adapterDef); + wac.refresh(); + return wac; + } + }; + servlet.init(new MockServletConfig()); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/integerSet"); + request.setMethod("POST"); + request.addParameter("content", "1,2"); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("1-2", response.getContentAsString()); + } + /* * Controllers @@ -3102,6 +3132,12 @@ public class ServletAnnotationControllerTests { public void processCsv(@RequestParam("content") Integer[] content, HttpServletResponse response) throws IOException { response.getWriter().write(StringUtils.arrayToDelimitedString(content, "-")); } + + @RequestMapping("/integerSet") + public void processCsv(@RequestParam("content") Set content, HttpServletResponse response) throws IOException { + assertTrue(content.iterator().next() instanceof Integer); + response.getWriter().write(StringUtils.collectionToDelimitedString(content, "-")); + } } }