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 a912d3e6a..63cd77518 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
@@ -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 CENTER, RIGHT, LEFT.
- * LEFT 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[])}.
+ *
+ * @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 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
+ */
+ 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;
+ }
+}
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 319fd8eeb..33c51997e 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
@@ -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
- * line.
- *
- * @param line the line to be tokenised (can be null)
- *
- * @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
+ * line.
+ *
+ * @param line the line to be tokenised (can be null)
+ *
+ * @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;
+ }
+}
diff --git a/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/Range.java b/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/Range.java
new file mode 100644
index 000000000..90dd46030
--- /dev/null
+++ b/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/Range.java
@@ -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 <1,Integer.MAX_VALUE-1> 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");
+ }
+}
diff --git a/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/RangeArrayPropertyEditor.java b/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/RangeArrayPropertyEditor.java
new file mode 100644
index 000000000..4e056ca7e
--- /dev/null
+++ b/infrastructure/src/main/java/org/springframework/batch/io/file/support/transform/RangeArrayPropertyEditor.java
@@ -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. Input string should be
+ * provided in following format: 'range1, range2, range3,...' where range is
+ * specified as:
+ *
+ * Examples: + * '1, 15, 25, 38, 55-60' is equal to '1-14, 15-24, 25-37, 38-54, 55-60' + * '36, 14, 1-10, 15, 49-57' is equal to '36-48, 14-14, 1-10, 15-35, 49-57' + *
+ * 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] + ")"); + } + } +} diff --git a/infrastructure/src/test/java/org/springframework/batch/io/file/support/transform/FixedLengthLineAggregatorTests.java b/infrastructure/src/test/java/org/springframework/batch/io/file/support/transform/FixedLengthLineAggregatorTests.java index 8898a384a..d5df34d6f 100644 --- a/infrastructure/src/test/java/org/springframework/batch/io/file/support/transform/FixedLengthLineAggregatorTests.java +++ b/infrastructure/src/test/java/org/springframework/batch/io/file/support/transform/FixedLengthLineAggregatorTests.java @@ -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)); + } +} diff --git a/infrastructure/src/test/java/org/springframework/batch/io/file/support/transform/FixedLengthTokenizerTests.java b/infrastructure/src/test/java/org/springframework/batch/io/file/support/transform/FixedLengthTokenizerTests.java index 095e12a92..7496105ee 100644 --- a/infrastructure/src/test/java/org/springframework/batch/io/file/support/transform/FixedLengthTokenizerTests.java +++ b/infrastructure/src/test/java/org/springframework/batch/io/file/support/transform/FixedLengthTokenizerTests.java @@ -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); + } + } + +} 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 new file mode 100644 index 000000000..dd5d44a7c --- /dev/null +++ b/infrastructure/src/test/java/org/springframework/batch/io/file/support/transform/RangeArrayPropertyEditorTests.java @@ -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 + } + } +}