OPEN - issue BATCH-371: FlatFileItemWriter no longer uses LineAggregator

http://jira.springframework.org/browse/BATCH-371

Moved existing default transformer out to a top level class, and introduce lineAggregatorItemTransformer as an option.  I still don't see a good way to get a String[] in to it reliably.
This commit is contained in:
dsyer
2008-02-19 21:40:44 +00:00
parent 10d4632dcb
commit 91a9e94590
5 changed files with 207 additions and 124 deletions

View File

@@ -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);
}
/**

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -157,77 +157,11 @@ public class FlatFileItemWriterTests extends TestCase {
assertEquals("FOO:" + data.toString(), lineFromFile);
}
/**
* Regular usage of <code>write(String)</code> 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 <code>write(String)</code> 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 <code>write(String)</code> 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 <code>write(String)</code> 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);
}
/**

View File

@@ -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
}
}
}