From cb17024c01fb71b94feae5c0b617a005eb494ca6 Mon Sep 17 00:00:00 2001 From: dsyer Date: Wed, 9 Dec 2009 13:09:02 +0000 Subject: [PATCH] RESOLVED - issue BATCH-924: BeanWrapperFieldSetMapper could create child objects if they are null when needed --- .../mapping/BeanWrapperFieldSetMapper.java | 24 ++++++++++++++- .../BeanWrapperFieldSetMapperTests.java | 29 ++++++++++++++----- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapper.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapper.java index cb90b25e8..6a845778e 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapper.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapper.java @@ -264,6 +264,10 @@ public class BeanWrapperFieldSetMapper extends DefaultPropertyEditorRegistrar private String findPropertyName(Object bean, String key) { + if (bean == null) { + return null; + } + Class cls = bean.getClass(); int index = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(key); @@ -280,7 +284,7 @@ public class BeanWrapperFieldSetMapper extends DefaultPropertyEditorRegistrar return null; } - Object nestedValue = new BeanWrapperImpl(bean).getPropertyValue(nestedName); + Object nestedValue = getPropertyValue(bean, nestedName); String nestedPropertyName = findPropertyName(nestedValue, suffix); return nestedPropertyName == null ? null : nestedName + "." + nestedPropertyName; } @@ -316,6 +320,24 @@ public class BeanWrapperFieldSetMapper extends DefaultPropertyEditorRegistrar return name; } + private Object getPropertyValue(Object bean, String nestedName) { + BeanWrapperImpl wrapper = new BeanWrapperImpl(bean); + Object nestedValue = wrapper.getPropertyValue(nestedName); + if (nestedValue == null) { + try { + nestedValue = wrapper.getPropertyType(nestedName).newInstance(); + wrapper.setPropertyValue(nestedName, nestedValue); + } + catch (InstantiationException e) { + ReflectionUtils.handleReflectionException(e); + } + catch (IllegalAccessException e) { + ReflectionUtils.handleReflectionException(e); + } + } + return nestedValue; + } + private void switchPropertyNames(Properties properties, String oldName, String newName) { String value = properties.getProperty(oldName); properties.remove(oldName); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapperTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapperTests.java index 29bcf614c..707dddf72 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapperTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/mapping/BeanWrapperFieldSetMapperTests.java @@ -86,6 +86,18 @@ public class BeanWrapperFieldSetMapperTests { assertEquals('C', result.getVarChar()); } + @Test + public void testNullPropertyAutoCreated() throws Exception { + BeanWrapperFieldSetMapper mapper = new BeanWrapperFieldSetMapper(); + mapper.setTargetType(TestNestedA.class); + mapper.afterPropertiesSet(); + + FieldSet fieldSet = new DefaultFieldSet(new String[] { "Foo", "Bar" }, new String[] { "valueA", + "testObjectB.valueA" }); + TestNestedA result = mapper.mapFieldSet(fieldSet); + assertEquals("Bar", result.getTestObjectB().getValueA()); + } + @Test public void testMapperWithSingleton() throws Exception { BeanWrapperFieldSetMapper mapper = new BeanWrapperFieldSetMapper(); @@ -377,11 +389,13 @@ public class BeanWrapperFieldSetMapperTests { BeanWrapperFieldSetMapper mapper = new BeanWrapperFieldSetMapper(); mapper.setTargetType(TestObject.class); - FieldSet fieldSet = new DefaultFieldSet(new String[] { "foo", "7890.1" }, new String[] { "varDouble", "varFloat" }); + FieldSet fieldSet = new DefaultFieldSet(new String[] { "foo", "7890.1" }, new String[] { "varDouble", + "varFloat" }); try { mapper.mapFieldSet(fieldSet); fail("Expected BindException"); - } catch (BindException e) { + } + catch (BindException e) { assertEquals(1, e.getErrorCount()); assertEquals("typeMismatch", e.getFieldError("varDouble").getCode()); } @@ -474,7 +488,7 @@ public class BeanWrapperFieldSetMapperTests { } - private static class TestNestedA { + public static class TestNestedA { private String valueA; private int valueB; @@ -507,7 +521,7 @@ public class BeanWrapperFieldSetMapperTests { } - private static class TestNestedB { + public static class TestNestedB { private String valueA; private TestNestedC testObjectC; @@ -530,6 +544,7 @@ public class BeanWrapperFieldSetMapperTests { } + @SuppressWarnings("unused") private static class TestNestedC { private int value; @@ -564,7 +579,7 @@ public class BeanWrapperFieldSetMapperTests { } } - + public static class TestObject { String varString; @@ -589,11 +604,11 @@ public class BeanWrapperFieldSetMapperTests { Date varDate; public Date getVarDate() { - return (Date)varDate.clone(); + return (Date) varDate.clone(); } public void setVarDate(Date varDate) { - this.varDate = varDate == null ? null : (Date)varDate.clone(); + this.varDate = varDate == null ? null : (Date) varDate.clone(); } public TestObject() {