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: + * + * Minimum and maximum values can be from interval <1, Integer.MAX_VALUE-1> + *

+ * 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 + } + } +}