RESOLVED - issue BATCH-50: Allow fixed-length file columns with column ranges

http://opensource.atlassian.com/projects/spring/browse/BATCH-50

Applied patch (and moved property editor).
This commit is contained in:
dsyer
2007-10-10 10:00:25 +00:00
parent 4744f78aa3
commit c4cf4a8910
7 changed files with 808 additions and 457 deletions

View File

@@ -1,141 +1,164 @@
/*
* 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.support.transform;
import org.springframework.util.Assert;
/**
* Class used to create string representing object. Each value has define length
* defined by record descriptor.
*
* @author tomas.slanina
*
*/
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 int[] lengths = new int[0];
private int align = ALIGN_LEFT;
private String padding = " ";
/**
* Setter for field lengths.
*
* @param lengths
*/
public void setLengths(int[] lengths) {
this.lengths = lengths;
}
/**
* Method used to create string representing object.
*
* @param args arrays of strings representing data to be stored
* @param lineDescriptor defines the structure of the final string
*/
public String aggregate(String[] args) {
StringBuffer stringBuffer = new StringBuffer();
Assert.notNull(args);
Assert.isTrue(args.length<=lengths.length,
"Number of arguments must match number of fields in a record");
for (int i = 0; i < args.length; i++) {
stringBuffer.append(formatText(args[i], lengths[i]));
}
return stringBuffer.toString();
}
private String formatText(String textToFormat, int length) {
String text;
if (textToFormat == null) {
text = "";
}
else {
text = textToFormat;
}
int currentLength = text.length();
Assert.isTrue(currentLength <= length, "Supplied text: " + text + " is longer than defined length: " + length);
if (currentLength == length) {
return text;
}
else {
StringBuffer stringBuffer = new StringBuffer();
switch (align) {
case ALIGN_RIGHT:
pad(stringBuffer, length - text.length());
stringBuffer.append(text);
break;
case ALIGN_CENTER:
int toAdd = length - text.length();
pad(stringBuffer, toAdd / 2);
stringBuffer.append(text);
pad(stringBuffer, toAdd - toAdd / 2);
break;
case ALIGN_LEFT:
stringBuffer.append(text);
pad(stringBuffer, length - text.length());
break;
}
return stringBuffer.toString();
}
}
private void pad(StringBuffer stringBuffer, int howMany) {
for (int i = 0; i < howMany; i++) {
stringBuffer.append(padding);
}
}
/**
* Recognized alignments are <code>CENTER, RIGHT, LEFT</code>.
* <code>LEFT</code> is used as default in case the argument does not
* match any of the recognized values.
*/
public void setAlignment(String alignment) {
if ("CENTER".equalsIgnoreCase(alignment)) {
this.align = ALIGN_CENTER;
}
else if ("RIGHT".equalsIgnoreCase(alignment)) {
this.align = ALIGN_RIGHT;
}
else {
// LEFT is default alignment, therefore use it
// if no other alignment was defined.
this.align = ALIGN_LEFT;
}
}
/**
* Setter for padding (default space).
* @param padding
*/
public void setPadding(String padding) {
this.padding = padding;
}
}
/*
* 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.support.transform;
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[])}.</br>
*
* @author tomas.slanina
* @author peter.zozom
*/
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.
*
* @param columns
* array of Range objects which specify column start and end
* position
*/
public void setColumns(Range[] columns) {
Assert.notNull(columns);
lastColumn = findLastColumn(columns);
this.ranges = columns;
}
/**
* Aggregate provided strings into single line using specified column ranges.
*
* @param args arrays of strings representing data to be aggregated
* @return aggregated strings
*/
public String aggregate(String[] args) {
Assert.notNull(args);
Assert.notNull(ranges);
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];
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
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);
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;
}
stringBuffer.replace(start, start + textToInsert.length(), textToInsert);
}
return stringBuffer.toString();
}
/**
* Recognized alignments are <code>CENTER, RIGHT, LEFT</code>.
* An IllegalArgumentException is thrown in case the argument does not
* match any of the recognized values.
*
* @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)) {
this.align = ALIGN_RIGHT;
}
else if ("LEFT".equalsIgnoreCase(alignment)) {
this.align = ALIGN_LEFT;
}
else {
throw new IllegalArgumentException("Only 'CENTER', 'RIGHT' or 'LEFT' are allowed alignment values");
}
}
/**
* Setter for padding (default 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).
*/
private int findLastColumn(Range[] columns) {
int lastOffset = 1;
int lastIndex = 0;
for(int i = 0; i < columns.length; i++) {
if (columns[i].getMin() > lastOffset) {
lastOffset = columns[i].getMin();
lastIndex = i;
}
}
return lastIndex;
}
}

View File

@@ -1,76 +1,76 @@
/*
* 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.support.transform;
import java.util.ArrayList;
import java.util.List;
/**
* Tokenizer used to process data obtained from files with fixed-length format.
*
* @author tomas.slanina
*/
public class FixedLengthTokenizer extends AbstractLineTokenizer {
private int[] lengths = new int[0];
/**
* Setter for field lengths.
*
* @param lengths
*/
public void setLengths(int[] lengths) {
this.lengths = lengths;
}
/**
* Yields the tokens resulting from the splitting of the supplied
* <code>line</code>.
*
* @param line the line to be tokenised (can be <code>null</code>)
*
* @return the resulting tokens
*/
protected List doTokenize(String line) {
List tokens = new ArrayList();
int lineLength;
int startPos = 0;
int endPos = 0;
String token;
lineLength = (line == null) ? (-1) : line.length();
for (int i = 0; i < lengths.length; i++) {
endPos += lengths[i];
if (lineLength >= endPos) {
token = line.substring(startPos, endPos);
}
else if (lineLength >= startPos) {
token = line.substring(startPos);
}
else {
token = "";
}
tokens.add(token);
startPos = endPos;
}
return tokens;
}
}
/*
* 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.support.transform;
import java.util.ArrayList;
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[])}).
*
* @author tomas.slanina
* @author peter.zozom
*/
public class FixedLengthTokenizer extends AbstractLineTokenizer {
private Range[] ranges;
/**
* Set the column ranges.
* @param ranges
*/
public void setColumns(Range[] ranges) {
this.ranges = ranges;
}
/**
* Yields the tokens resulting from the splitting of the supplied
* <code>line</code>.
*
* @param line the line to be tokenised (can be <code>null</code>)
*
* @return the resulting tokens
*/
protected List doTokenize(String line) {
List tokens = new ArrayList(ranges.length);
int lineLength;
String token;
lineLength = line.length();
for (int i = 0; i < ranges.length; i++) {
int startPos = ranges[i].getMin()-1;
int endPos = ranges[i].getMax();
if (lineLength >= endPos) {
token = line.substring(startPos, endPos);
}
else if (lineLength >= startPos) {
token = line.substring(startPos);
}
else {
token = "";
}
tokens.add(token);
}
return tokens;
}
}

View File

@@ -0,0 +1,62 @@
package org.springframework.batch.io.file.support.transform;
import org.springframework.util.Assert;
/**
* A class to represent ranges. A Range can have minimum/maximum values from
* interval &lt;1,Integer.MAX_VALUE-1&gt; A Range can be unbounded at maximum
* side. This can be specified by passing {@link Range#UPPER_BORDER_NOT_DEFINED}} as max
* value or using constructor {@link #Range(int)}.
*
* @author peter.zozom
*/
public class Range {
private final static int UPPER_BORDER_NOT_DEFINED = Integer.MAX_VALUE;
private int min;
private int max;
public Range(int min) {
checkMinMaxValues(min, UPPER_BORDER_NOT_DEFINED);
this.min = min;
this.max = UPPER_BORDER_NOT_DEFINED;
}
public Range(int min, int max) {
checkMinMaxValues(min, max);
this.min = min;
this.max = max;
}
public int getMax() {
return max;
}
public int getMin() {
return min;
}
public void setMax(int max) {
checkMinMaxValues(this.min, max);
this.max = max;
}
public void setMin(int min) {
checkMinMaxValues(min, this.max);
this.min = min;
}
public boolean hasMaxValue() {
return max != UPPER_BORDER_NOT_DEFINED;
}
public String toString() {
return hasMaxValue() ? min + "-" + max : String.valueOf(min);
}
private void checkMinMaxValues(int min, int max) {
Assert.isTrue(min>0, "Min value must be higher than zero");
Assert.isTrue(min<=max, "Min value should be lower or equal to max value");
}
}

View File

@@ -0,0 +1,131 @@
package org.springframework.batch.io.file.support.transform;
import java.beans.PropertyEditorSupport;
import java.util.Arrays;
import java.util.Comparator;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Property editor implementation which parses string and creates array of
* ranges. Ranges can be provided in any order. </br> Input string should be
* provided in following format: 'range1, range2, range3,...' where range is
* specified as:
* <ul>
* <li>'X-Y', where X is minimum value and Y is maximum value (condition X<=Y
* is verified)</li>
* <li>or 'Z', where Z is minimum and maximum is calculated as (minimum of
* adjacent range - 1). Maximum of the last range is never calculated. Range
* stays unbound at maximum side if maximum value is not provided.</li>
* </ul>
* Minimum and maximum values can be from interval &lt;1, Integer.MAX_VALUE-1&gt;
* <p>
* Examples:</br>
* '1, 15, 25, 38, 55-60' is equal to '1-14, 15-24, 25-37, 38-54, 55-60' </br>
* '36, 14, 1-10, 15, 49-57' is equal to '36-48, 14-14, 1-10, 15-35, 49-57'
* <p>
* Property editor also allows to validate whether ranges are disjoint. Validation
* can be turned on/off by using {@link #forceDisjointRanges}. By default
* validation is turned off.
*
* @author peter.zozom
*/
public class RangeArrayPropertyEditor extends PropertyEditorSupport {
private boolean forceDisjointRanges = false;
/**
* Set force disjoint ranges. If set to TRUE, ranges are validated to be disjoint.
* For example: defining ranges '1-10, 5-15' will cause IllegalArgumentException in
* case of forceDisjointRanges=TRUE.
* @param forceDisjointRanges
*/
public void setForceDisjointRanges(boolean forceDisjointRanges) {
this.forceDisjointRanges = forceDisjointRanges;
}
public void setAsText(String text) throws IllegalArgumentException {
//split text into ranges
String[] strRanges = text.split(",");
Range[] ranges = new Range[strRanges.length];
//parse ranges and create array of Range objects
for (int i = 0; i < strRanges.length; i++) {
String[] range = strRanges[i].split("-");
int min;
int max;
if ((range.length == 1) && (StringUtils.hasText(range[0]))) {
min = Integer.parseInt(range[0].trim());
// correct max value will be assigned later
ranges[i] = new Range(min);
} else if ((range.length == 2) && (StringUtils.hasText(range[0]))
&& (StringUtils.hasText(range[1]))) {
min = Integer.parseInt(range[0].trim());
max = Integer.parseInt(range[1].trim());
ranges[i] = new Range(min,max);
} else {
throw new IllegalArgumentException("Range[" + i + "]: range (" + strRanges[i] + ") is invalid");
}
}
setMaxValues(ranges);
setValue(ranges);
}
public String getAsText() {
Range[] ranges = (Range[])getValue();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < ranges.length; i++) {
if(i>0) {
sb.append(", ");
}
sb.append(ranges[i]);
}
return sb.toString();
}
private void setMaxValues(Range[] ranges) {
//clone array, original array should stay same
Range[] c = (Range[])ranges.clone();
//sort array of Ranges
Arrays.sort(c, new Comparator() {
public int compare(Object o1, Object o2) {
Range c1 = (Range)o1;
Range c2 = (Range)o2;
return c1.getMin()-c2.getMin();
}
}
);
//set max values for all unbound ranges (except last range)
for (int i = 0; i < c.length - 1; i++) {
if (!c[i].hasMaxValue()) {
//set max value to (min value - 1) of the next range
c[i].setMax(c[i+1].getMin() - 1);
}
}
if (forceDisjointRanges) {
verifyRanges(c);
}
}
private void verifyRanges(Range[] ranges) {
//verify that ranges are disjoint
for(int i = 1; i < ranges.length;i++) {
Assert.isTrue(ranges[i-1].getMax() < ranges[i].getMin(),
"Ranges must be disjoint. Range[" + (i-1) + "]: (" + ranges[i-1] +
") Range[" + i +"]: (" + ranges[i] + ")");
}
}
}

View File

@@ -1,128 +1,170 @@
/*
* 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.support.transform;
import org.springframework.batch.io.file.support.transform.FixedLengthLineAggregator;
import junit.framework.TestCase;
/**
* Unit tests for {@link FixedLengthLineAggregator}
*
* @author robert.kasanicky
*/
public class FixedLengthLineAggregatorTests extends TestCase {
// object under test
private FixedLengthLineAggregator aggregator = new FixedLengthLineAggregator();
/**
* Record descriptor is null => BatchCriticalException
*/
public void testAggregateNullRecordDescriptor() {
String[] args = { "does not matter what is here" };
try {
aggregator.aggregate(args);
fail("should not work with null LineDescriptor");
}
catch (IllegalArgumentException expected) {
// expected
}
}
/**
* Argument count does not match the number of fields in the
* LineDescriptor
*/
public void testAggregateWrongArgumentCount() {
String[] args = { "only one argument" };
aggregator.setLengths(new int[0]);
try {
aggregator.aggregate(args);
fail("Wrong argument count, exception exptected");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
/**
* Argument length exceeds the length specified by FieldDescriptor
*/
public void testAggregateInvalidInputLength() {
String[] args = { "Oversize" };
aggregator.setLengths(new int[] {args[0].length()-1});
try {
aggregator.aggregate(args);
fail("Invalid argument length, exception should have been thrown");
}
catch (IllegalArgumentException expected) {
// expected
}
}
/**
* Regular use with valid LineDescriptor
*/
public void testAggregate() {
String[] args = { "Matchsize", "Smallsize" };
aggregator.setLengths(new int[] {args[0].length(), args[1].length()});
String result = aggregator.aggregate(args);
assertEquals("MatchsizeSmallsize", result);
}
/**
* Regular use with valid LineDescriptor
*/
public void testAggregateFormattedRight() {
String[] args = { "Matchsize", "Smallsize" };
aggregator.setAlignment("right");
aggregator.setLengths(new int[] {args[0].length()+4, args[1].length()+1});
String result = aggregator.aggregate(args);
assertEquals(result, " Matchsize Smallsize");
}
/**
* Regular use with valid LineDescriptor
*/
public void testAggregateFormattedCenter() {
String[] args = { "Matchsize", "Smallsize" };
aggregator.setAlignment("center");
aggregator.setLengths(new int[] {args[0].length()+4, args[1].length()+1});
String result = aggregator.aggregate(args);
assertEquals(result, " Matchsize Smallsize ");
}
/**
* If one of the passed arguments is null, string filled with spaces should
* be returned
*/
public void testAggregateNullArgument() {
String[] args = { null };
aggregator.setLengths(new int[] {3});
try {
assertEquals(" ", aggregator.aggregate(args));
}
catch (NullPointerException unexpected) {
fail("incorrect handling of null arguments");
}
}
}
/*
* 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.support.transform;
import org.springframework.batch.io.file.support.transform.FixedLengthLineAggregator;
import junit.framework.TestCase;
/**
* Unit tests for {@link FixedLengthLineAggregator}
*
* @author robert.kasanicky
* @author peter.zozom
*/
public class FixedLengthLineAggregatorTests extends TestCase {
// object under test
private FixedLengthLineAggregator aggregator = new FixedLengthLineAggregator();
/**
* If no ranges are specified, IllegalArgumentException is thrown
*/
public void testAggregateNullRecordDescriptor() {
String[] args = { "does not matter what is here" };
try {
aggregator.aggregate(args);
fail("should not work with no ranges specified");
}
catch (IllegalArgumentException expected) {
// expected
}
}
/**
* Count of aggregated strings does not match the number of columns
*/
public void testAggregateWrongArgumentCount() {
String[] string = { "only one test string" };
aggregator.setColumns(new Range[0]);
try {
aggregator.aggregate(string);
fail("Exception expected: count of aggregated strings"
+ " does not match the number of columns");
}
catch (IllegalArgumentException expected) {
// expected
}
}
/**
* Text length exceeds the length of the column.
*/
public void testAggregateInvalidInputLength() {
String[] args = { "Oversize" };
aggregator.setColumns(new Range[] {new Range(1,args[0].length()-1)});
try {
aggregator.aggregate(args);
fail("Invalid text length, exception should have been thrown");
}
catch (IllegalArgumentException expected) {
// expected
}
}
/**
* Test aggregation
*/
public void testAggregate() {
String[] args = { "Matchsize", "Smallsize" };
aggregator.setColumns(new Range[] {new Range(1,9), new Range(10,18)});
String result = aggregator.aggregate(args);
assertEquals("MatchsizeSmallsize", result);
}
/**
* Test aggregation with last range unbound
*/
public void testAggregateWithLastRangeUnbound() {
String[] args = { "Matchsize", "Smallsize" };
aggregator.setColumns(new Range[] {new Range(1,12), new Range(13)});
String result = aggregator.aggregate(args);
assertEquals("Matchsize Smallsize", result);
}
/**
* Test aggregation with right alignment
*/
public void testAggregateFormattedRight() {
String[] args = { "Matchsize", "Smallsize" };
aggregator.setAlignment("right");
aggregator.setColumns(new Range[] {new Range(1,13), new Range(14,23)});
String result = aggregator.aggregate(args);
assertEquals(23,result.length());
assertEquals(result, " Matchsize Smallsize");
}
/**
* Test aggregation with center alignment
*/
public void testAggregateFormattedCenter() {
String[] args = { "Matchsize", "Smallsize" };
aggregator.setAlignment("center");
aggregator.setColumns(new Range[] {new Range(1,13), new Range(14,25)});
String result = aggregator.aggregate(args);
assertEquals(result, " Matchsize Smallsize ");
}
/**
* Test aggregation with left alignment
*/
public void testAggregateWithCustomPadding() {
String[] args = { "Matchsize", "Smallsize" };
aggregator.setPadding('.');
aggregator.setAlignment("left");
aggregator.setColumns(new Range[] {new Range(1,13), new Range(14,24)});
String result = aggregator.aggregate(args);
assertEquals(result, "Matchsize....Smallsize..");
}
/**
* Test aggregation with left alignment
*/
public void testAggregateFormattedLeft() {
String[] args = { "Matchsize", "Smallsize" };
aggregator.setAlignment("left");
aggregator.setColumns(new Range[] {new Range(1,13), new Range(14,24)});
String result = aggregator.aggregate(args);
assertEquals(result, "Matchsize Smallsize ");
}
/**
* Try set ivalid alignment
*/
public void testInvalidAlignment() {
try {
aggregator.setAlignment("foo");
fail("Exception was expected: invalid alignment value");
} catch (IllegalArgumentException iae) {
// expected
}
}
/**
* If one of the passed arguments is null, string filled with spaces should
* be returned
*/
public void testAggregateNullArgument() {
String[] args = { null };
aggregator.setColumns(new Range[] {new Range(1,3)});
assertEquals(" ", aggregator.aggregate(args));
}
}

View File

@@ -1,112 +1,127 @@
/*
* 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.support.transform;
import junit.framework.TestCase;
import org.springframework.batch.io.file.FieldSet;
import org.springframework.batch.io.file.support.transform.FixedLengthTokenizer;
public class FixedLengthTokenizerTests extends TestCase {
private FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();
private String line = null;
/**
* even if null or empty string is tokenized, tokenizer returns as many
* empty tokens as defined by recordDescriptor.
*/
public void testTokenizeEmptyString() {
tokenizer.setLengths(new int[] {5,5,5});
FieldSet tokens = tokenizer.tokenize(null);
assertEquals(0, tokens.getFieldCount());
}
public void testTokenizeNullString() {
tokenizer.setLengths(new int[] {5,5,5});
FieldSet tokens = tokenizer.tokenize("");
assertEquals(0, tokens.getFieldCount());
}
public void testTokenizeRegularUse() {
tokenizer.setLengths(new int[] {2,5,5});
// test shorter line as defined by record descriptor
line = "H1";
FieldSet tokens = tokenizer.tokenize(line);
assertEquals(3, tokens.getFieldCount());
assertEquals("H1", tokens.readString(0));
assertEquals("", tokens.readString(1));
assertEquals("", tokens.readString(2));
}
public void testNormalLength() throws Exception {
tokenizer.setLengths(new int[] {10,15,5});
// test shorter line as defined by record descriptor
line = "H1";
FieldSet tokens = tokenizer.tokenize(line);
// test normal length
line = "H1 12345678 12345";
tokens = tokenizer.tokenize(line);
assertEquals(3, tokens.getFieldCount());
assertEquals(line.substring(0, 10).trim(), tokens.readString(0));
assertEquals(line.substring(10, 25).trim(), tokens.readString(1));
assertEquals(line.substring(25).trim(), tokens.readString(2));
}
public void testLongerLinesRestIgnored() throws Exception {
tokenizer.setLengths(new int[] {10,15,5});
// test shorter line as defined by record descriptor
line = "H1";
FieldSet tokens = tokenizer.tokenize(line);
// test longer lines => rest will be ignored
line = "H1 12345678 1234567890";
tokens = tokenizer.tokenize(line);
assertEquals(3, tokens.getFieldCount());
assertEquals(line.substring(0, 10).trim(), tokens.readString(0));
assertEquals(line.substring(10, 25).trim(), tokens.readString(1));
assertEquals(line.substring(25, 30).trim(), tokens.readString(2));
}
public void testAnotherTypeOfRecord() throws Exception {
tokenizer.setLengths(new int[] {5,10,10,2});
// test shorter line as defined by record descriptor
line = "H1";
FieldSet tokens = tokenizer.tokenize(line);
// test another type of record
line = "H2 123456 12345 12";
tokens = tokenizer.tokenize(line);
assertEquals(4, tokens.getFieldCount());
assertEquals(line.substring(0, 5).trim(), tokens.readString(0));
assertEquals(line.substring(5, 15).trim(), tokens.readString(1));
assertEquals(line.substring(15, 25).trim(), tokens.readString(2));
assertEquals(line.substring(25).trim(), tokens.readString(3));
}
public void testTokenizerInvalidSetup() {
tokenizer.setNames(new String[] {"a", "b"});
tokenizer.setLengths(new int[] {5,5,5,2});
try {
tokenizer.tokenize("McDonalds - I'm lovin' it.");
fail("tokenizer works even with invalid names!");
}
catch (Exception e) {
assertTrue(true);
}
}
}
/*
* 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.support.transform;
import junit.framework.TestCase;
import org.springframework.batch.io.file.FieldSet;
import org.springframework.batch.io.file.support.transform.FixedLengthTokenizer;
public class FixedLengthTokenizerTests extends TestCase {
private FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();
private String line = null;
/**
* if null or empty string is tokenized, tokenizer returns empty fieldset
* (with no tokens).
*/
public void testTokenizeEmptyString() {
tokenizer.setColumns(new Range[] {new Range(1,5),new Range(6,10),new Range(11,15)});
FieldSet tokens = tokenizer.tokenize("");
assertEquals(0, tokens.getFieldCount());
}
public void testTokenizeNullString() {
tokenizer.setColumns(new Range[] {new Range(1,5),new Range(6,10),new Range(11,15)});
FieldSet tokens = tokenizer.tokenize(null);
assertEquals(0, tokens.getFieldCount());
}
public void testTokenizeRegularUse() {
tokenizer.setColumns(new Range[] {new Range(1,2),new Range(3,7),new Range(8,12)});
// test shorter line as defined by record descriptor
line = "H1";
FieldSet tokens = tokenizer.tokenize(line);
assertEquals(3, tokens.getFieldCount());
assertEquals("H1", tokens.readString(0));
assertEquals("", tokens.readString(1));
assertEquals("", tokens.readString(2));
}
public void testNormalLength() throws Exception {
tokenizer.setColumns(new Range[] {new Range(1,10),new Range(11,25),new Range(26,30)});
// test shorter line as defined by record descriptor
line = "H1";
FieldSet tokens = tokenizer.tokenize(line);
// test normal length
line = "H1 12345678 12345";
tokens = tokenizer.tokenize(line);
assertEquals(3, tokens.getFieldCount());
assertEquals(line.substring(0, 10).trim(), tokens.readString(0));
assertEquals(line.substring(10, 25).trim(), tokens.readString(1));
assertEquals(line.substring(25).trim(), tokens.readString(2));
}
public void testLongerLinesRestIgnored() throws Exception {
tokenizer.setColumns(new Range[] {new Range(1,10),new Range(11,25),new Range(26,30)});
// test shorter line as defined by record descriptor
line = "H1";
FieldSet tokens = tokenizer.tokenize(line);
// test longer lines => rest will be ignored
line = "H1 12345678 1234567890";
tokens = tokenizer.tokenize(line);
assertEquals(3, tokens.getFieldCount());
assertEquals(line.substring(0, 10).trim(), tokens.readString(0));
assertEquals(line.substring(10, 25).trim(), tokens.readString(1));
assertEquals(line.substring(25, 30).trim(), tokens.readString(2));
}
public void testNonAdjacentRangesUnsorted() throws Exception {
tokenizer.setColumns(new Range[] {new Range(14,28), new Range(34,38), new Range(1,10)});
// test shorter line as defined by record descriptor
line = "H1";
FieldSet tokens = tokenizer.tokenize(line);
// test normal length
line = "H1 +++12345678 +++++12345+++";
tokens = tokenizer.tokenize(line);
assertEquals(3, tokens.getFieldCount());
assertEquals(line.substring(0, 10).trim(), tokens.readString(2));
assertEquals(line.substring(13, 28).trim(), tokens.readString(0));
assertEquals(line.substring(33, 38).trim(), tokens.readString(1));
}
public void testAnotherTypeOfRecord() throws Exception {
tokenizer.setColumns(new Range[] {new Range(1,5),new Range(6,15),new Range(16,25),new Range(26,27)});
// test shorter line as defined by record descriptor
line = "H1";
FieldSet tokens = tokenizer.tokenize(line);
// test another type of record
line = "H2 123456 12345 12";
tokens = tokenizer.tokenize(line);
assertEquals(4, tokens.getFieldCount());
assertEquals(line.substring(0, 5).trim(), tokens.readString(0));
assertEquals(line.substring(5, 15).trim(), tokens.readString(1));
assertEquals(line.substring(15, 25).trim(), tokens.readString(2));
assertEquals(line.substring(25).trim(), tokens.readString(3));
}
public void testTokenizerInvalidSetup() {
tokenizer.setNames(new String[] {"a", "b"});
tokenizer.setColumns(new Range[] {new Range(1,5),new Range(6,15),new Range(16,25),new Range(26,27)});
try {
tokenizer.tokenize("Test tokenize");
fail("Exception was expected: too few names provided");
}
catch (Exception e) {
assertTrue(true);
}
}
}

View File

@@ -0,0 +1,78 @@
package org.springframework.batch.io.file.support.transform;
import org.springframework.batch.io.file.support.transform.Range;
import org.springframework.batch.io.file.support.transform.RangeArrayPropertyEditor;
import junit.framework.TestCase;
public class RangeArrayPropertyEditorTests extends TestCase {
private Range[] ranges;
private RangeArrayPropertyEditor pe;
public void setUp() {
ranges = null;
pe = new RangeArrayPropertyEditor() {
public void setValue(Object value) {
ranges = (Range[])value;
}
public Object getValue() {
return ranges;
}
};
}
public void testSetAsText() {
pe.setAsText("15, 32, 1-10, 33");
//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());
assertFalse(ranges[3].hasMaxValue());
}
public void testGetAsText() {
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() {
pe.setForceDisjointRanges(true);
//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
try {
pe.setAsText("1-10, 5-15");
fail("Exception expected: ranges are not disjoint");
} catch (IllegalArgumentException iae) {
// expected
}
}
public void testInvalidInput() {
try {
pe.setAsText("1-5, b");
fail("Exception expected: 2nd range is invalid");
} catch (IllegalArgumentException iae) {
// expected
}
}
}