diff --git a/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/FixedLengthLineAggregator.java b/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/FixedLengthLineAggregator.java
index 63cd77518..285d30876 100644
--- a/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/FixedLengthLineAggregator.java
+++ b/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/FixedLengthLineAggregator.java
@@ -21,26 +21,30 @@ import java.util.Arrays;
import org.springframework.util.Assert;
/**
- * LineAggregator implementation which produces line by aggregating provided
- * strings into columns with fixed length. Columns are specified by array of ranges
- * ({@link #setColumns(Range[])}.
+ * LineAggregator implementation which produces line by aggregating provided
+ * strings into columns with fixed length. Columns are specified by array of
+ * ranges ({@link #setColumns(Range[])}.
*
* @author tomas.slanina
- * @author peter.zozom
+ * @author peter.zozom
+ * @author Dave Syer
*/
public class FixedLengthLineAggregator implements LineAggregator {
private static final int ALIGN_CENTER = 1;
private static final int ALIGN_RIGHT = 2;
private static final int ALIGN_LEFT = 3;
-
+
private Range[] ranges;
private int lastColumn;
private int align = ALIGN_LEFT;
private char padding = ' ';
-
+
/**
- * Set column ranges.
+ * Set column ranges. Used in conjunction with the
+ * {@link RangeArrayPropertyEditor} this property can be set in the form of
+ * a String describing the range boundaries, e.g. "1,4,7" or "1-3,4-6,7" or
+ * "1-2,4-5,7-10".
*
* @param columns
* array of Range objects which specify column start and end
@@ -51,11 +55,13 @@ public class FixedLengthLineAggregator implements LineAggregator {
lastColumn = findLastColumn(columns);
this.ranges = columns;
}
-
+
/**
- * Aggregate provided strings into single line using specified column ranges.
+ * Aggregate provided strings into single line using specified column
+ * ranges.
*
- * @param args arrays of strings representing data to be aggregated
+ * @param args
+ * arrays of strings representing data to be aggregated
* @return aggregated strings
*/
public String aggregate(String[] args) {
@@ -65,100 +71,107 @@ public class FixedLengthLineAggregator implements LineAggregator {
Assert.isTrue(args.length <= ranges.length,
"Number of arguments must match number of fields in a record");
- //calculate line length
- int lineLength = ranges[lastColumn].hasMaxValue() ? ranges[lastColumn].getMax()
- : ranges[lastColumn].getMin() + args[lastColumn].length() - 1;
-
- //create stringBuffer with length of line filled with padding characters
- char[] emptyLine = new char[lineLength];
+ // calculate line length
+ int lineLength = ranges[lastColumn].hasMaxValue() ? ranges[lastColumn]
+ .getMax() : ranges[lastColumn].getMin()
+ + args[lastColumn].length() - 1;
+
+ // create stringBuffer with length of line filled with padding
+ // characters
+ char[] emptyLine = new char[lineLength];
Arrays.fill(emptyLine, padding);
-
+
StringBuffer stringBuffer = new StringBuffer(lineLength);
stringBuffer.append(emptyLine);
-
- //aggregate all strings
- for(int i = 0; i < args.length; i++) {
-
- //offset where text will be inserted
- int start = ranges[i].getMin() - 1;
-
- //calculate column length
+
+ // aggregate all strings
+ for (int i = 0; i < args.length; i++) {
+
+ // offset where text will be inserted
+ int start = ranges[i].getMin() - 1;
+
+ // calculate column length
int columnLength;
if ((i == lastColumn) && (!ranges[lastColumn].hasMaxValue())) {
columnLength = args[lastColumn].length();
} else {
columnLength = ranges[i].getMax() - ranges[i].getMin() + 1;
}
-
+
String textToInsert = (args[i] == null) ? "" : args[i];
- Assert.isTrue(columnLength >= textToInsert.length(),
- "Supplied text: " + textToInsert + " is longer than defined length: " + columnLength);
-
+ Assert
+ .isTrue(columnLength >= textToInsert.length(),
+ "Supplied text: " + textToInsert
+ + " is longer than defined length: "
+ + columnLength);
+
switch (align) {
- case ALIGN_RIGHT:
- start += (columnLength - textToInsert.length());
- break;
- case ALIGN_CENTER:
- start += ((columnLength - textToInsert.length()) / 2);
- break;
- case ALIGN_LEFT:
- //nothing to do
- break;
+ case ALIGN_RIGHT:
+ start += (columnLength - textToInsert.length());
+ break;
+ case ALIGN_CENTER:
+ start += ((columnLength - textToInsert.length()) / 2);
+ break;
+ case ALIGN_LEFT:
+ // nothing to do
+ break;
}
- stringBuffer.replace(start, start + textToInsert.length(), textToInsert);
+ stringBuffer.replace(start, start + textToInsert.length(),
+ textToInsert);
}
-
+
return stringBuffer.toString();
}
-
+
/**
- * Recognized alignments are CENTER, RIGHT, LEFT.
- * An IllegalArgumentException is thrown in case the argument does not
- * match any of the recognized values.
+ * Recognized alignments are CENTER, RIGHT, LEFT. An
+ * IllegalArgumentException is thrown in case the argument does not match
+ * any of the recognized values.
*
- * @param alignment the alignment to be used
+ * @param alignment
+ * the alignment to be used
*/
public void setAlignment(String alignment) {
if ("CENTER".equalsIgnoreCase(alignment)) {
this.align = ALIGN_CENTER;
- }
- else if ("RIGHT".equalsIgnoreCase(alignment)) {
+ } else if ("RIGHT".equalsIgnoreCase(alignment)) {
this.align = ALIGN_RIGHT;
- }
- else if ("LEFT".equalsIgnoreCase(alignment)) {
+ } else if ("LEFT".equalsIgnoreCase(alignment)) {
this.align = ALIGN_LEFT;
- }
- else {
- throw new IllegalArgumentException("Only 'CENTER', 'RIGHT' or 'LEFT' are allowed alignment values");
+ } else {
+ throw new IllegalArgumentException(
+ "Only 'CENTER', 'RIGHT' or 'LEFT' are allowed alignment values");
}
}
-
+
/**
- * Setter for padding (default space).
- * @param padding the padding character
+ * Setter for padding (default is space).
+ *
+ * @param padding
+ * the padding character
*/
public void setPadding(char padding) {
this.padding = padding;
}
/*
- * Find last column. Columns are not sorted.
- * Returns index of last column (column with highest offset).
+ * Find last column. Columns are not sorted. Returns index of last column
+ * (column with highest offset).
*/
private int findLastColumn(Range[] columns) {
-
+
int lastOffset = 1;
int lastIndex = 0;
-
- for(int i = 0; i < columns.length; i++) {
+
+ for (int i = 0; i < columns.length; i++) {
if (columns[i].getMin() > lastOffset) {
lastOffset = columns[i].getMin();
lastIndex = i;
}
}
-
+
return lastIndex;
}
}
diff --git a/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/FixedLengthTokenizer.java b/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/FixedLengthTokenizer.java
index 33c51997e..308045e31 100644
--- a/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/FixedLengthTokenizer.java
+++ b/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/FixedLengthTokenizer.java
@@ -21,30 +21,36 @@ import java.util.List;
/**
* Tokenizer used to process data obtained from files with fixed-length format.
- * Columns are specified by array of Range objects ({@link #setColumns(Range[])}).
+ * Columns are specified by array of Range objects ({@link #setColumns(Range[])}).
*
* @author tomas.slanina
* @author peter.zozom
+ * @author Dave Syer
*/
public class FixedLengthTokenizer extends AbstractLineTokenizer {
-
+
private Range[] ranges;
-
+
/**
- * Set the column ranges.
- * @param ranges
+ * Set the column ranges. Used in conjunction with the
+ * {@link RangeArrayPropertyEditor} this property can be set in the form of
+ * a String describing the range boundaries, e.g. "1,4,7" or "1-3,4-6,7" or
+ * "1-2,4-5,7-10".
+ *
+ * @param ranges the column ranges expected in the input
*/
public void setColumns(Range[] ranges) {
this.ranges = ranges;
}
-
+
/**
* Yields the tokens resulting from the splitting of the supplied
* line.
*
- * @param line the line to be tokenised (can be null)
+ * @param line
+ * the line to be tokenised (can be null)
*
- * @return the resulting tokens
+ * @return the resulting tokens (empty if the line is null)
*/
protected List doTokenize(String line) {
List tokens = new ArrayList(ranges.length);
@@ -55,16 +61,14 @@ public class FixedLengthTokenizer extends AbstractLineTokenizer {
for (int i = 0; i < ranges.length; i++) {
- int startPos = ranges[i].getMin()-1;
+ int startPos = ranges[i].getMin() - 1;
int endPos = ranges[i].getMax();
-
+
if (lineLength >= endPos) {
token = line.substring(startPos, endPos);
- }
- else if (lineLength >= startPos) {
+ } else if (lineLength >= startPos) {
token = line.substring(startPos);
- }
- else {
+ } else {
token = "";
}
diff --git a/infrastructure/src/test/java/org/springframework/batch/io/file/support/transform/RangeArrayPropertyEditorTests.java b/infrastructure/src/test/java/org/springframework/batch/io/file/support/transform/RangeArrayPropertyEditorTests.java
index dd5d44a7c..538df9aff 100644
--- a/infrastructure/src/test/java/org/springframework/batch/io/file/support/transform/RangeArrayPropertyEditorTests.java
+++ b/infrastructure/src/test/java/org/springframework/batch/io/file/support/transform/RangeArrayPropertyEditorTests.java
@@ -8,56 +8,75 @@ import junit.framework.TestCase;
public class RangeArrayPropertyEditorTests extends TestCase {
private Range[] ranges;
- private RangeArrayPropertyEditor pe;
-
+ private RangeArrayPropertyEditor pe;
+
public void setUp() {
-
+
ranges = null;
-
+
pe = new RangeArrayPropertyEditor() {
public void setValue(Object value) {
- ranges = (Range[])value;
+ ranges = (Range[]) value;
}
+
public Object getValue() {
return ranges;
}
};
}
-
- public void testSetAsText() {
+
+ public void testSetAsText() {
pe.setAsText("15, 32, 1-10, 33");
-
- //result should be 15-31, 32-32, 1-10, 33-unbound
+
+ // result should be 15-31, 32-32, 1-10, 33-unbound
assertEquals(4, ranges.length);
- assertEquals(15,ranges[0].getMin());
- assertEquals(31,ranges[0].getMax());
- assertEquals(32,ranges[1].getMin());
- assertEquals(32,ranges[1].getMax());
- assertEquals(1,ranges[2].getMin());
- assertEquals(10,ranges[2].getMax());
- assertEquals(33,ranges[3].getMin());
+ assertEquals(15, ranges[0].getMin());
+ assertEquals(31, ranges[0].getMax());
+ assertEquals(32, ranges[1].getMin());
+ assertEquals(32, ranges[1].getMax());
+ assertEquals(1, ranges[2].getMin());
+ assertEquals(10, ranges[2].getMax());
+ assertEquals(33, ranges[3].getMin());
assertFalse(ranges[3].hasMaxValue());
}
-
+
+ public void testSetAsTextWithNoSpaces() {
+ pe.setAsText("15,32");
+
+ // result should be 15-31, 32-unbound
+ assertEquals(2, ranges.length);
+ assertEquals(15, ranges[0].getMin());
+ assertEquals(31, ranges[0].getMax());
+ assertEquals(32, ranges[1].getMin());
+ assertFalse(ranges[1].hasMaxValue());
+ }
+
public void testGetAsText() {
-
- ranges = new Range[] {new Range(20),new Range(6,15),new Range(2),new Range(26,95)};
+
+ ranges = new Range[] { new Range(20), new Range(6, 15), new Range(2),
+ new Range(26, 95) };
assertEquals("20, 6-15, 2, 26-95", pe.getAsText());
}
-
- public void testValidateDisjointRanges() {
+
+ public void testValidDisjointRanges() {
pe.setForceDisjointRanges(true);
-
- //test disjoint ranges
+
+ // test disjoint ranges
pe.setAsText("1-5,11-15");
-
+
assertEquals(2, ranges.length);
- assertEquals(1,ranges[0].getMin());
- assertEquals(5,ranges[0].getMax());
- assertEquals(11,ranges[1].getMin());
- assertEquals(15,ranges[1].getMax());
-
- //test joint ranges
+ assertEquals(1, ranges[0].getMin());
+ assertEquals(5, ranges[0].getMax());
+ assertEquals(11, ranges[1].getMin());
+ assertEquals(15, ranges[1].getMax());
+
+ }
+
+ public void testInvalidOverlappingRanges() {
+
+ pe.setForceDisjointRanges(true);
+
+ // test joint ranges
try {
pe.setAsText("1-10, 5-15");
fail("Exception expected: ranges are not disjoint");
@@ -65,9 +84,21 @@ public class RangeArrayPropertyEditorTests extends TestCase {
// expected
}
}
-
+
+ public void testValidOverlappingRanges() {
+
+ // test joint ranges
+ pe.setAsText("1-10, 5-15");
+ assertEquals(2, ranges.length);
+ assertEquals(1, ranges[0].getMin());
+ assertEquals(10, ranges[0].getMax());
+ assertEquals(5, ranges[1].getMin());
+ assertEquals(15, ranges[1].getMax());
+
+ }
+
public void testInvalidInput() {
-
+
try {
pe.setAsText("1-5, b");
fail("Exception expected: 2nd range is invalid");