diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/BeanDelimitingLineAggregator.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/BeanDelimitingLineAggregator.java new file mode 100644 index 000000000..814e07d0a --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/BeanDelimitingLineAggregator.java @@ -0,0 +1,61 @@ +/* + * Copyright 2006-2007 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.file.transform; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; + +/** + * This is a delimited line aggregator for a java bean. Given an array of + * property names, it will reflectively call getters on the item to aggregate. + * + * @author Dan Garrette + * @since 2.0 + */ +public class BeanDelimitingLineAggregator implements LineAggregator, InitializingBean { + + private BeanWrapperFieldExtractor extractor; + + private DelimitedLineAggregator delimitedLineAggregator = new DelimitedLineAggregator(); + + /** + * @param names names of properties of the aggregated item that will be + * included in the resulting string. Must not be null. + */ + public void setNames(String[] names) { + extractor = new BeanWrapperFieldExtractor(); + extractor.setNames(names); + extractor.afterPropertiesSet(); + } + + /** + * @param delimiter used to separate property values in the + * {@link #aggregate(Object)} result + */ + public void setDelimiter(String delimiter) { + this.delimitedLineAggregator.setDelimiter(delimiter); + } + + public String aggregate(T item) { + Object[] fields = this.extractor.extract(item); + return this.delimitedLineAggregator.aggregate(fields); + } + + public void afterPropertiesSet() throws Exception { + Assert.notNull(this.extractor, "The 'names' property must be set."); + } +} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/BeanWrapperFieldExtractor.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/BeanWrapperFieldExtractor.java new file mode 100644 index 000000000..c584a49cf --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/transform/BeanWrapperFieldExtractor.java @@ -0,0 +1,62 @@ +/* + * Copyright 2006-2007 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.file.transform; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.BeanWrapperImpl; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; + +/** + * This is a field extractor for a java bean. Given an array of property names, + * it will reflectively call getters on the item and return an array of all the + * values. + * + * @author Dan Garrette + * @since 2.0 + */ +public class BeanWrapperFieldExtractor implements FieldExtractor, InitializingBean { + + private String[] names; + + /** + * @param names field names to be extracted by the {@link #extract(Object)} method. + */ + public void setNames(String[] names) { + this.names = names; + } + + /** + * @see org.springframework.batch.item.file.transform.FieldExtractor#extract(java.lang.Object) + */ + public Object[] extract(T item) { + List values = new ArrayList(); + + BeanWrapper bw = new BeanWrapperImpl(item); + for (String propertyName : this.names) { + values.add(bw.getPropertyValue(propertyName)); + } + return values.toArray(); + } + + public void afterPropertiesSet() { + Assert.notNull(names, "The 'names' property must be set."); + } +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/transform/BeanDelimitingLineAggregatorTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/transform/BeanDelimitingLineAggregatorTests.java new file mode 100644 index 000000000..6448cf8fd --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/transform/BeanDelimitingLineAggregatorTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2006-2007 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.file.transform; + +import static junit.framework.Assert.assertEquals; + +import org.junit.Test; + +/** + * @author Dan Garrette + * @since 2.0 + */ +public class BeanDelimitingLineAggregatorTests { + + private BeanDelimitingLineAggregator aggregator = new BeanDelimitingLineAggregator(); + + @Test + public void testAggregate() throws Exception { + aggregator.setNames(new String[] { "first", "last", "born" }); + aggregator.afterPropertiesSet(); + + String first = "Alan"; + String last = "Turing"; + int born = 1912; + + Name n = new Name(first, last, born); + String value = aggregator.aggregate(n); + + assertEquals("Alan,Turing,1912", value); + } + + @Test(expected = IllegalArgumentException.class) + public void testNamesMustBeSet() throws Exception { + aggregator.afterPropertiesSet(); + } + + @Test(expected = IllegalArgumentException.class) + public void testNamesMustNotBeNull() throws Exception { + aggregator.setNames(null); + } +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/transform/BeanWrapperFieldExtractorTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/transform/BeanWrapperFieldExtractorTests.java new file mode 100644 index 000000000..0d99cb470 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/transform/BeanWrapperFieldExtractorTests.java @@ -0,0 +1,77 @@ +/* + * Copyright 2006-2007 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.batch.item.file.transform; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; + +import org.junit.Test; +import org.springframework.beans.NotReadablePropertyException; + +/** + * @author Dan Garrette + * @since 2.0 + */ +public class BeanWrapperFieldExtractorTests { + + private BeanWrapperFieldExtractor extractor = new BeanWrapperFieldExtractor(); + + @Test + public void testExtract() throws Exception { + extractor.setNames(new String[] { "first", "last", "born" }); + extractor.afterPropertiesSet(); + + String first = "Alan"; + String last = "Turing"; + int born = 1912; + + Name n = new Name(first, last, born); + Object[] values = extractor.extract(n); + + assertEquals(3, values.length); + assertEquals(first, values[0]); + assertEquals(last, values[1]); + assertEquals(born, values[2]); + } + + @Test + public void testExtract_invalidProperty() throws Exception { + extractor.setNames(new String[] { "first", "last", "birthday" }); + extractor.afterPropertiesSet(); + + String first = "Alan"; + String last = "Turing"; + int born = 1912; + + Name n = new Name(first, last, born); + + try { + extractor.extract(n); + fail(); + } + catch (NotReadablePropertyException e) { + assertTrue(e.getMessage().startsWith("Invalid property 'birthday'")); + } + } + + @Test(expected = IllegalArgumentException.class) + public void testNamesPropertyMustBeSet() throws Exception { + extractor.setNames(null); + extractor.afterPropertiesSet(); + } +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/transform/Name.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/transform/Name.java new file mode 100644 index 000000000..9ee3793a3 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/transform/Name.java @@ -0,0 +1,41 @@ +package org.springframework.batch.item.file.transform; + +public class Name { + private String first; + private String last; + private int born; + + public Name() { + + } + + public Name(String first, String last, int born) { + this.first = first; + this.last = last; + this.born = born; + } + + public String getFirst() { + return first; + } + + public void setFirst(String first) { + this.first = first; + } + + public String getLast() { + return last; + } + + public void setLast(String last) { + this.last = last; + } + + public int getBorn() { + return born; + } + + public void setBorn(int born) { + this.born = born; + } +}