diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/FlatFileItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/FlatFileItemWriter.java index c8cd5e5ce..c02cfc4bb 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/FlatFileItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/FlatFileItemWriter.java @@ -24,12 +24,11 @@ import java.io.IOException; import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.charset.UnsupportedCharsetException; -import java.util.Collection; -import java.util.Iterator; import java.util.Properties; import org.springframework.batch.io.exception.BatchCriticalException; import org.springframework.batch.io.exception.BatchEnvironmentException; +import org.springframework.batch.io.file.transform.RecursiveCollectionItemTransformer; import org.springframework.batch.io.support.AbstractTransactionalIoSource; import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemStream; @@ -75,15 +74,7 @@ public class FlatFileItemWriter extends AbstractTransactionalIoSource implements private OutputState state = null; - private ItemTransformer transformer = new ItemTransformer() { - public Object transform(Object input) { - return "" + input; - } - }; - - private static class BooleanHolder { - public boolean value; - } + private ItemTransformer transformer = new RecursiveCollectionItemTransformer(); /** * Assert that mandatory properties (resource) are set. @@ -128,51 +119,7 @@ public class FlatFileItemWriter extends AbstractTransactionalIoSource implements * @throws Exception if the transformer or file output fail */ public void write(Object data) throws Exception { - transformAndWrite(data, new BooleanHolder()); - } - - /** - * Convert the date to a format that can be output and then write it out. - * @param data - * @param converted - * @throws Exception - */ - private void transformAndWrite(Object data, BooleanHolder converted) throws Exception { - - if (data instanceof Collection) { - converted.value = false; - for (Iterator iterator = ((Collection) data).iterator(); iterator.hasNext();) { - Object value = (Object) iterator.next(); - // (recursive) - write(value); - } - return; - } - if (data.getClass().isArray()) { - converted.value = false; - Object[] array = (Object[]) data; - for (int i = 0; i < array.length; i++) { - Object value = array[i]; - // (recursive) - write(value); - } - return; - } - if (data instanceof String) { - // This is where the output stream is actually written to - getOutputState().write(data + LINE_SEPARATOR); - } - else if (!converted.value) { - // (recursive) - converted.value = true; - transformAndWrite(transformer.transform(data), converted); - return; - } - else { - // Should not happen... - throw new IllegalStateException( - "Infinite loop detected - converter did not convert to String or collection/array of objects convertible to String."); - } + getOutputState().write(transformer.transform(data) + LINE_SEPARATOR); } /** diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/transform/LineAggregatorItemTransformer.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/transform/LineAggregatorItemTransformer.java new file mode 100644 index 000000000..a9566d9af --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/transform/LineAggregatorItemTransformer.java @@ -0,0 +1,48 @@ +/* + * 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.io.file.transform; + +import org.springframework.batch.item.writer.ItemTransformer; + +/** + * An {@link ItemTransformer} that expects a String[] as input and delegates to + * a {@link LineAggregator}. + * + * @author Dave Syer + * + */ +public class LineAggregatorItemTransformer implements ItemTransformer { + + private LineAggregator aggregator = new DelimitedLineAggregator(); + + /** + * Public setter for the {@link LineAggregator}. + * @param aggregator the aggregator to set + */ + public void setAggregator(LineAggregator aggregator) { + this.aggregator = aggregator; + } + + /** + * Assume the item is an array of String (no check is made) and delegate to + * the aggregator. + * + * @see org.springframework.batch.item.writer.ItemTransformer#transform(java.lang.Object) + */ + public Object transform(Object item) throws Exception { + return aggregator.aggregate((String[]) item); + } +} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/transform/RecursiveCollectionItemTransformer.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/transform/RecursiveCollectionItemTransformer.java new file mode 100644 index 000000000..7accd44ac --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/transform/RecursiveCollectionItemTransformer.java @@ -0,0 +1,91 @@ +package org.springframework.batch.io.file.transform; + +import java.util.Collection; +import java.util.Iterator; + +import org.springframework.batch.item.writer.ItemTransformer; + +/** + * An implementation of {@link ItemTransformer} that just calls toString() on + * its argument, unless it it an array or collection, in which case it loops + * though, calling itself on each member in turn, concatenating the result with + * line separators. + * + * @author Dave Syer + * + */ +public class RecursiveCollectionItemTransformer implements ItemTransformer { + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + /* (non-Javadoc) + * @see org.springframework.batch.item.writer.ItemTransformer#transform(java.lang.Object) + */ + public Object transform(Object input) { + TransformHolder holder = new TransformHolder(); + transformRecursively(input, holder); + String result = holder.builder.toString(); + return result.substring(0, result.lastIndexOf(LINE_SEPARATOR)); + } + + public String stringify(Object input) { + return "" + input; + } + + /** + * Convert the date to a format that can be output and then write it out. + * @param data + * @param converted + * @throws Exception + */ + private void transformRecursively(Object data, TransformHolder converted) { + + if (data instanceof Collection) { + converted.value = false; + for (Iterator iterator = ((Collection) data).iterator(); iterator.hasNext();) { + Object value = (Object) iterator.next(); + // (recursive) + transformRecursively(value, new TransformHolder(converted.builder)); + } + return; + } + if (data.getClass().isArray()) { + converted.value = false; + Object[] array = (Object[]) data; + for (int i = 0; i < array.length; i++) { + Object value = array[i]; + // (recursive) + transformRecursively(value, new TransformHolder(converted.builder)); + } + return; + } + if (data instanceof String) { + // This is where the output stream is actually written to + converted.builder.append(data + LINE_SEPARATOR); + } + else if (!converted.value) { + // (recursive) + converted.value = true; + transformRecursively(stringify(data), converted); + return; + } + else { + // Should not happen... + throw new IllegalStateException( + "Infinite loop detected - converter did not convert to String or collection/array of objects convertible to String."); + } + } + + private static class TransformHolder { + boolean value; + + StringBuilder builder = new StringBuilder(); + + TransformHolder() { + } + + TransformHolder(StringBuilder builder) { + this.builder = builder; + } + } +} \ No newline at end of file diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/file/FlatFileItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/file/FlatFileItemWriterTests.java index 51199e7f1..5d65cacef 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/file/FlatFileItemWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/file/FlatFileItemWriterTests.java @@ -157,77 +157,11 @@ public class FlatFileItemWriterTests extends TestCase { assertEquals("FOO:" + data.toString(), lineFromFile); } - /** - * Regular usage of write(String) method - * @throws Exception - */ - public void testWriteWithConverterAndInfiniteLoopInCollection() throws Exception { - inputSource.setTransformer(new ItemTransformer() { - public Object transform(Object input) { - return "FOO:" + input; - } - }); - Object data = new Object(); - inputSource.write(new Object[] { data, data }); - inputSource.close(); - String lineFromFile = readLine(); - assertEquals("FOO:" + data.toString(), lineFromFile); - lineFromFile = readLine(); - assertEquals("FOO:" + data.toString(), lineFromFile); - } - - /** - * Regular usage of write(String) method - * @throws Exception - */ - public void testWriteWithConverterAndInfiniteLoopInConvertedCollection() throws Exception { - inputSource.setTransformer(new ItemTransformer() { - boolean converted = false; - - public Object transform(Object input) { - if (converted) { - return input; - } - converted = true; - return new Object[] { input, input }; - } - }); - Object data = new Object(); - try { - inputSource.write(data); - fail("Expected IllegalStateException"); - } - catch (IllegalStateException e) { - // expected - assertTrue("Wrong message: " + e, e.getMessage().toLowerCase().indexOf("infinite") >= 0); - } - inputSource.close(); - String lineFromFile = readLine(); - assertNull(lineFromFile); - } - /** * Regular usage of write(String) method * @throws Exception */ public void testWriteWithConverterAndString() throws Exception { - inputSource.setTransformer(new ItemTransformer() { - public Object transform(Object input) { - return "FOO:" + input; - } - }); - inputSource.write(Collections.singleton(TEST_STRING)); - inputSource.close(); - String lineFromFile = readLine(); - // converter not used if input is String - assertEquals(TEST_STRING, lineFromFile); - } - - /** - * Regular usage of write(String) method - * @throws Exception - */ - public void testWriteWithConverterAndCollectionOfString() throws Exception { inputSource.setTransformer(new ItemTransformer() { public Object transform(Object input) { return "FOO:" + input; @@ -236,8 +170,7 @@ public class FlatFileItemWriterTests extends TestCase { inputSource.write(TEST_STRING); inputSource.close(); String lineFromFile = readLine(); - // converter not used if input is String - assertEquals(TEST_STRING, lineFromFile); + assertEquals("FOO:"+TEST_STRING, lineFromFile); } /** diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/file/transform/LineAggregatorItemTransformerTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/file/transform/LineAggregatorItemTransformerTests.java new file mode 100644 index 000000000..be4bc3625 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/file/transform/LineAggregatorItemTransformerTests.java @@ -0,0 +1,64 @@ +/* + * 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.io.file.transform; + +import junit.framework.TestCase; + +/** + * @author Dave Syer + * + */ +public class LineAggregatorItemTransformerTests extends TestCase { + + private LineAggregatorItemTransformer transformer = new LineAggregatorItemTransformer(); + + /** + * Test method for {@link org.springframework.batch.io.file.transform.LineAggregatorItemTransformer#setAggregator(org.springframework.batch.io.file.transform.LineAggregator)}. + * @throws Exception + */ + public void testSetAggregator() throws Exception { + transformer.setAggregator(new LineAggregator() { + public String aggregate(String[] args) { + return "foo"; + } + }); + String value = (String) transformer.transform(new String[] {"a", "b"}); + assertEquals("foo", value); + } + + /** + * Test method for {@link org.springframework.batch.io.file.transform.LineAggregatorItemTransformer#transform(java.lang.Object)}. + * @throws Exception + */ + public void testTransform() throws Exception { + String value = (String) transformer.transform(new String[] {"a", "b"}); + assertTrue("Wrong value: "+value, value.startsWith("a,b")); + } + + /** + * Test method for {@link org.springframework.batch.io.file.transform.LineAggregatorItemTransformer#transform(java.lang.Object)}. + * @throws Exception + */ + public void testTransformWrongType() throws Exception { + try { + transformer.transform("foo"); + fail("Expected ClassCastException"); + } catch (ClassCastException e) { + // Expected + } + + } +}