diff --git a/common/source/java/ch/systemsx/cisd/common/parser/DefaultReaderParser.java b/common/source/java/ch/systemsx/cisd/common/parser/DefaultParser.java similarity index 70% rename from common/source/java/ch/systemsx/cisd/common/parser/DefaultReaderParser.java rename to common/source/java/ch/systemsx/cisd/common/parser/DefaultParser.java index 3ae7b838629f7cb603635b8abc082a3fd136ebb7..65f2fd2c36f7762870974678d06ab23c14345f45 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/DefaultReaderParser.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/DefaultParser.java @@ -16,15 +16,12 @@ package ch.systemsx.cisd.common.parser; -import java.io.Reader; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.LineIterator; - /** - * A default <code>IReaderParser</code> implementation. + * A default {@link IParser} implementation. * <p> * The object type returned by this implementation is generic. This implementation defines a <code>ILineFilter</code> * that filters out comment and empty lines. @@ -32,18 +29,24 @@ import org.apache.commons.io.LineIterator; * * @author Christian Ribeaud */ -public class DefaultReaderParser<E> implements IReaderParser<E> +public class DefaultParser<E> implements IParser<E> { private final ILineTokenizer lineTokenizer; private IParserObjectFactory<E> factory; - public DefaultReaderParser() + /** + * Creates an instance based on the {@link DefaultLineTokenizer}. + */ + public DefaultParser() { this(new DefaultLineTokenizer()); } - public DefaultReaderParser(final ILineTokenizer lineTokenizer) + /** + * Creates an instance for the specified line tokenizer. + */ + public DefaultParser(final ILineTokenizer lineTokenizer) { this.lineTokenizer = lineTokenizer; } @@ -61,7 +64,7 @@ public class DefaultReaderParser<E> implements IReaderParser<E> * * @param lineNumber line number. */ - protected String[] parseLine(final int lineNumber, final String line) + private String[] parseLine(final int lineNumber, final String line) { return lineTokenizer.tokenize(line); } @@ -70,35 +73,31 @@ public class DefaultReaderParser<E> implements IReaderParser<E> // Parser // - public final List<E> parse(final Reader reader) - { - return parse(reader, AlwaysAcceptLineFilter.INSTANCE); - } - - public final List<E> parse(final Reader reader, final ILineFilter lineFilter) throws ParsingException + public final List<E> parse(final Iterator<Line> lineIterator, final ILineFilter lineFilter) throws ParsingException { final List<E> elements = new ArrayList<E>(); synchronized (lineTokenizer) { lineTokenizer.init(); - final LineIterator lineIterator = IOUtils.lineIterator(reader); - for (int lineNumber = 0; lineIterator.hasNext(); lineNumber++) + while (lineIterator.hasNext()) { - final String nextLine = lineIterator.nextLine(); - if (lineFilter.acceptLine(nextLine, lineNumber)) + Line line = lineIterator.next(); + final String nextLine = line.getText(); + int number = line.getNumber(); + if (lineFilter.acceptLine(nextLine, number)) { - final String[] tokens = parseLine(lineNumber, nextLine); + final String[] tokens = parseLine(number, nextLine); E object = null; try { object = createObject(tokens); } catch (final ParserException parserException) { - throw new ParsingException(parserException, tokens, lineNumber); + throw new ParsingException(parserException, tokens, number); } catch (final RuntimeException runtimeException) { // This should not happen but... - throw new ParsingException(runtimeException, tokens, lineNumber); + throw new ParsingException(runtimeException, tokens, number); } elements.add(object); } diff --git a/common/source/java/ch/systemsx/cisd/common/parser/IParser.java b/common/source/java/ch/systemsx/cisd/common/parser/IParser.java new file mode 100644 index 0000000000000000000000000000000000000000..c83eb104c156ff817f74cd2781a6dc5a329cbdb1 --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/parser/IParser.java @@ -0,0 +1,46 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * 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 ch.systemsx.cisd.common.parser; + +import java.util.Iterator; +import java.util.List; + +/** + * <code>IReaderParser</code> is able to parse a given text lines and to return a list of objects of type + * <code>E</code>. + * + * @author Christian Ribeaud + */ +public interface IParser<E> +{ + + /** + * Parses the lines delivered by the specified iterator and creating elements of type <code>E</code>. + * + * @param lineFilter A filter lines have to pass in order to be parsed. + * @return a <code>List</code> of elements. + */ + public List<E> parse(final Iterator<Line> lineIterator, final ILineFilter lineFilter) throws ParsingException; + + /** + * Sets the <code>IParserObjectFactory</code>. + * <p> + * Typically, the given <code>factory</code> transforms a line into an element. + * </p> + */ + public void setObjectFactory(final IParserObjectFactory<E> factory); +} diff --git a/common/source/java/ch/systemsx/cisd/common/parser/IParserObjectFactory.java b/common/source/java/ch/systemsx/cisd/common/parser/IParserObjectFactory.java index 27eb3b269830b303f8ee23111efb08f5744cce1f..107c438e41c340700d619bfc31f3e83ed66a1766 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/IParserObjectFactory.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/IParserObjectFactory.java @@ -20,7 +20,7 @@ package ch.systemsx.cisd.common.parser; * A <code>IParserObjectFactory</code> implementation knows how to deal with given line tokens and convert them into * an appropriate <code>Object</code>. * <p> - * A <code>IParserObjectFactory</code> is typically registered in {@link IReaderParser}. + * A <code>IParserObjectFactory</code> is typically registered in {@link IParser}. * </p> * * @author Christian Ribeaud diff --git a/common/source/java/ch/systemsx/cisd/common/parser/IReaderParser.java b/common/source/java/ch/systemsx/cisd/common/parser/IReaderParser.java deleted file mode 100644 index 65325b2538fcd92c97da2724b18712fb1dab2e65..0000000000000000000000000000000000000000 --- a/common/source/java/ch/systemsx/cisd/common/parser/IReaderParser.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2007 ETH Zuerich, CISD - * - * 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 ch.systemsx.cisd.common.parser; - -import java.io.BufferedReader; -import java.io.Reader; -import java.util.List; - -/** - * <code>IReaderParser</code> is able to parse a given {@link Reader} and to returns <code>Object</code> instances. - * - * @author Christian Ribeaud - */ -public interface IReaderParser<E> -{ - - /** - * Parses given <code>Reader</code>. Encapsulates given <code>Reader</code> in a {@link BufferedReader} for - * better performance (if not already done). - * <p> - * Note that this does not close given <code>Parser</code>. It is your responsability to do so. - * </p> - * - * @param lineFilter you could define a filter for the lines found in given <code>reader</code>. - * @return a <code>List</code> of elements. - */ - public List<E> parse(final Reader reader, final ILineFilter lineFilter) throws ParsingException; - - /** - * Parses given <code>Reader</code>. Encapsulates given <code>Reader</code> in a {@link BufferedReader} for - * better performance (if not already done). - * <p> - * Note that this does not close given <code>Parser</code>. It is your responsability to do so. - * </p> - * - * @return a <code>List</code> of elements. - */ - public List<E> parse(final Reader reader); - - /** - * Sets the <code>IParserObjectFactory</code>. - * <p> - * Typically, the given <code>factory</code> transforms a line into an element. - * </p> - */ - public void setObjectFactory(final IParserObjectFactory<E> factory); -} diff --git a/common/source/java/ch/systemsx/cisd/common/parser/Line.java b/common/source/java/ch/systemsx/cisd/common/parser/Line.java new file mode 100644 index 0000000000000000000000000000000000000000..332b4086fde5a6e695897e562138fb224eabdd74 --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/parser/Line.java @@ -0,0 +1,32 @@ +package ch.systemsx.cisd.common.parser; + +/** + * A small object that represents a line in a <code>File</code> context. + * + * @author Christian Ribeaud + */ +public final class Line +{ + private final String text; + + private final int number; + + Line(final int number, final String text) + { + assert text != null : "Unspecified text."; + this.number = number; + this.text = text; + } + + public final String getText() + { + return text; + } + + public final int getNumber() + { + return number; + } + + +} \ No newline at end of file diff --git a/common/source/java/ch/systemsx/cisd/common/parser/ParserUtilities.java b/common/source/java/ch/systemsx/cisd/common/parser/ParserUtilities.java index 14983891d65565bc7174572255e8024c81917ec5..9bd5e73c9e4a4e6e66c1389dd857060aaae7ad57 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/ParserUtilities.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/ParserUtilities.java @@ -41,24 +41,6 @@ public final class ParserUtilities // Can not be instantiated. } - /** - * A small object that represents a line in a <code>File</code> context. - * - * @author Christian Ribeaud - */ - public final static class Line - { - public final String text; - - public final int number; - - Line(final int number, final String text) - { - this.number = number; - this.text = text; - } - } - /** * Returns the first <code>Line</code> that is not filtered out by given <code>ILineFilter</code>. * <p> diff --git a/common/source/java/ch/systemsx/cisd/common/parser/TabFileLoader.java b/common/source/java/ch/systemsx/cisd/common/parser/TabFileLoader.java index 308f92ae9851da63c32a049c84d7f2e290c99e57..fe740329977886fccffb156d437064481b6f2c80 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/TabFileLoader.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/TabFileLoader.java @@ -19,11 +19,16 @@ package ch.systemsx.cisd.common.parser; import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; import org.apache.commons.io.IOUtils; +import org.apache.commons.io.LineIterator; import org.apache.commons.lang.StringUtils; import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; @@ -36,6 +41,7 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException; */ public class TabFileLoader<T> { + private static final String PREFIX = "#"; private final IParserObjectFactoryFactory<T> factory; @@ -58,23 +64,11 @@ public class TabFileLoader<T> assert file != null : "Given file must not be null"; assert file.isFile() : "Given file '" + file.getAbsolutePath() + "' is not a file."; - final DefaultReaderParser<T> parser = new DefaultReaderParser<T>(); - final ParserUtilities.Line headerLine = - ParserUtilities.getFirstAcceptedLine(file, ExcludeEmptyAndCommentLineFilter.INSTANCE); - if (headerLine == null) - { - throw new IllegalArgumentException("No header line found in file '" + file.getAbsolutePath() + "'."); - } - final HeaderLineFilter lineFilter = new HeaderLineFilter(headerLine.number); - final String[] tokens = StringUtils.split(headerLine.text, "\t"); - notUnique(tokens); - final IAliasPropertyMapper propertyMapper = new HeaderFilePropertyMapper(tokens); - parser.setObjectFactory(factory.createFactory(propertyMapper)); FileReader reader = null; try { reader = new FileReader(file); - return parser.parse(reader, lineFilter); + return load(reader); } catch (IOException ex) { throw new EnvironmentFailureException(ex.getMessage()); @@ -87,6 +81,65 @@ public class TabFileLoader<T> IOUtils.closeQuietly(reader); } } + + + List<T> load(Reader reader) + { + Iterator<Line> lineIterator = createLineIterator(reader); + Line previousLine = null; + Line line = new Line(0, PREFIX); + boolean previousLineHasColumnHeaders = false; + while (lineIterator.hasNext()) + { + previousLineHasColumnHeaders = previousLine != null && PREFIX.equals(previousLine.getText()); + previousLine = line; + line = lineIterator.next(); + if (line.getText().startsWith(PREFIX) == false) + { + break; + } + } + String headerLine = previousLineHasColumnHeaders ? previousLine.getText().substring(1) : line.getText(); + + final DefaultParser<T> parser = new DefaultParser<T>(); + final String[] tokens = StringUtils.split(headerLine, "\t"); + notUnique(tokens); + final IAliasPropertyMapper propertyMapper = new HeaderFilePropertyMapper(tokens); + parser.setObjectFactory(factory.createFactory(propertyMapper)); + ILineFilter filter = AlwaysAcceptLineFilter.INSTANCE; + List<T> result = new ArrayList<T>(); + if (previousLineHasColumnHeaders) + { + result.addAll(parser.parse(Arrays.asList(line).iterator(), filter)); + } + result.addAll(parser.parse(lineIterator, filter)); + return result; + } + + private Iterator<Line> createLineIterator(Reader reader) + { + final LineIterator lineIterator = IOUtils.lineIterator(reader); + Iterator<Line> iterator = new Iterator<Line>() + { + private int lineNumber; + public void remove() + { + lineIterator.remove(); + } + + public Line next() + { + return new Line(++lineNumber, lineIterator.nextLine()); + } + + public boolean hasNext() + { + return lineIterator.hasNext(); + } + + }; + return iterator; + } /** * Throws given <var>ex</var> or translates it into another kind of exception. diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/parser/DefaultReaderParserTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/parser/DefaultParserTest.java similarity index 50% rename from common/sourceTest/java/ch/systemsx/cisd/common/parser/DefaultReaderParserTest.java rename to common/sourceTest/java/ch/systemsx/cisd/common/parser/DefaultParserTest.java index afb4717ea3ff2e1d9c946b7bf3f07b463b11c216..bd988ecd83c09e313d558a90298cfc9d427fed1b 100644 --- a/common/sourceTest/java/ch/systemsx/cisd/common/parser/DefaultReaderParserTest.java +++ b/common/sourceTest/java/ch/systemsx/cisd/common/parser/DefaultParserTest.java @@ -18,56 +18,52 @@ package ch.systemsx.cisd.common.parser; import static org.testng.AssertJUnit.assertEquals; -import java.io.Reader; -import java.io.StringReader; +import java.util.Arrays; +import java.util.Iterator; import java.util.List; -import org.apache.commons.io.IOUtils; import org.testng.annotations.Test; /** - * Test cases for corresponding {@link DefaultReaderParser} class. + * Test cases for corresponding {@link DefaultParser} class. * * @author Christian Ribeaud */ -public final class DefaultReaderParserTest +public final class DefaultParserTest { - private final String text = - "\n# This is a comment\n" + "firstName\tlastName\taddress\tcity\n" - + "Christian\tRibeaud\tKapfrain 2/2\tEfringen-Kirchen\n" - + "Marcel\tOdiet\tRue des Pervenches 46\t2800 DelŽmont\n"; + private final static List<String> text + = Arrays.asList("", "# This is a comment", "firstName\tlastName\taddress\tcity", + "Charles\tDarwin\tHumboldt Ave. 1865\t4242 Somewhere", + "Albert\tEinstein\tNewton Road 1905\t4711 Princton"); @Test public final void testParseWithoutFactoryAndHeader() { - final IReaderParser<String[]> parser = new DefaultReaderParser<String[]>(); + final IParser<String[]> parser = new DefaultParser<String[]>(); parser.setObjectFactory(IParserObjectFactory.STRING_ARRAY_OBJECT_FACTORY); - final Reader reader = new StringReader(text); - final List<String[]> result = parser.parse(reader, new HeaderLineFilter()); + final List<String[]> result = parser.parse(createLineIterator(), new HeaderLineFilter()); assertEquals(3, result.size()); assertEquals(result.get(0)[0], "firstName"); - assertEquals(result.get(1)[1], "Ribeaud"); - assertEquals(result.get(2)[2], "Rue des Pervenches 46"); - IOUtils.closeQuietly(reader); + assertEquals(result.get(1)[1], "Darwin"); + assertEquals(result.get(2)[2], "Newton Road 1905"); + assertEquals(result.get(1)[3], "4242 Somewhere"); } @Test public final void testParseWithoutFactoryWithLineFilter() { - final IReaderParser<String[]> parser = new DefaultReaderParser<String[]>(); + final IParser<String[]> parser = new DefaultParser<String[]>(); parser.setObjectFactory(IParserObjectFactory.STRING_ARRAY_OBJECT_FACTORY); - final Reader reader = new StringReader(text); - final List<String[]> result = parser.parse(reader, new HeaderLineFilter(2)); + final List<String[]> result = parser.parse(createLineIterator(), new HeaderLineFilter(2)); assertEquals(2, result.size()); - assertEquals(result.get(0)[1], "Ribeaud"); - assertEquals(result.get(1)[2], "Rue des Pervenches 46"); - IOUtils.closeQuietly(reader); + assertEquals(result.get(0)[0], "Charles"); + assertEquals(result.get(1)[1], "Einstein"); } @Test public final void testCreateObjectWithException() { - final IReaderParser<String[]> parser = new DefaultReaderParser<String[]>() + final IParser<String[]> parser = new DefaultParser<String[]>() { // // DefaultReaderParser @@ -79,16 +75,38 @@ public final class DefaultReaderParserTest } }; parser.setObjectFactory(IParserObjectFactory.STRING_ARRAY_OBJECT_FACTORY); - final Reader reader = new StringReader(text); try { - parser.parse(reader, new HeaderLineFilter(2)); + parser.parse(createLineIterator(), new HeaderLineFilter(2)); } catch (ParsingException ex) { assertEquals( - "Creating an object with following tokens '[Christian, Ribeaud, Kapfrain 2/2, Efringen-Kirchen]' failed.", + "Creating an object with following tokens '[Charles, Darwin, Humboldt Ave. 1865, 4242 Somewhere]' failed.", ex.getMessage()); assertEquals(3, ex.getLineNumber()); } } + + private Iterator<Line> createLineIterator() + { + return new Iterator<Line>() + { + private Iterator<String> iterator = text.iterator(); + private int lineNumber; + public void remove() + { + throw new UnsupportedOperationException(); + } + public Line next() + { + return new Line(++lineNumber, iterator.next()); + } + + public boolean hasNext() + { + return iterator.hasNext(); + } + }; + } + } \ No newline at end of file diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/parser/ParserUtilitiesTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/parser/ParserUtilitiesTest.java index 4b4fc1b5878fd6d7004d8891320f1c12aba98c21..dce574df6d6db66f803f2a54035c675adde1a36d 100644 --- a/common/sourceTest/java/ch/systemsx/cisd/common/parser/ParserUtilitiesTest.java +++ b/common/sourceTest/java/ch/systemsx/cisd/common/parser/ParserUtilitiesTest.java @@ -30,7 +30,6 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import ch.systemsx.cisd.common.logging.LogInitializer; -import ch.systemsx.cisd.common.parser.ParserUtilities.Line; /** * Test cases for corresponding {@link ParserUtilities} class. @@ -90,8 +89,8 @@ public final class ParserUtilitiesTest File file = new File(workingDirectory, "test.txt"); FileUtils.writeLines(file, Arrays.asList(lines)); Line line = ParserUtilities.getFirstAcceptedLine(file, null); - assertEquals(StringUtils.EMPTY, line.text); - assertEquals(0, line.number); + assertEquals(StringUtils.EMPTY, line.getText()); + assertEquals(0, line.getNumber()); assert file.delete(); } @@ -103,8 +102,8 @@ public final class ParserUtilitiesTest File file = new File(workingDirectory, "test.txt"); FileUtils.writeLines(file, Arrays.asList(lines)); Line line = ParserUtilities.getFirstAcceptedLine(file, ExcludeEmptyAndCommentLineFilter.INSTANCE); - assertEquals("hello", line.text); - assertEquals(3, line.number); + assertEquals("hello", line.getText()); + assertEquals(3, line.getNumber()); assert file.delete(); } } diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/parser/TabFileLoaderTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/parser/TabFileLoaderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8e0e931ee68ecbe776b1c71b548ae5fab73fc9dd --- /dev/null +++ b/common/sourceTest/java/ch/systemsx/cisd/common/parser/TabFileLoaderTest.java @@ -0,0 +1,140 @@ +/* + * Copyright 2008 ETH Zuerich, CISD + * + * 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 ch.systemsx.cisd.common.parser; + +import static org.testng.AssertJUnit.assertEquals; + +import java.io.StringReader; +import java.util.List; + +import org.testng.annotations.Test; + +/** + * + * + * @author Franz-Josef Elmer + */ +public class TabFileLoaderTest +{ + public static final class ABC + { + private String a; + private String b; + private String c; + public final String getA() + { + return a; + } + public final void setA(String a) + { + this.a = a; + } + public final String getB() + { + return b; + } + public final void setB(String b) + { + this.b = b; + } + public final String getC() + { + return c; + } + public final void setC(String c) + { + this.c = c; + } + @Override + public String toString() + { + return a + b + c; + } + + } + + private static final class ABCFactoryFactory implements IParserObjectFactoryFactory<ABC> + { + public IParserObjectFactory<ABC> createFactory(IAliasPropertyMapper propertyMapper) throws ParserException + { + return new IParserObjectFactory<ABC>() + { + public ABC createObject(String[] lineTokens) throws ParserException + { + ABC abc = new ABC(); + abc.setA(lineTokens[0]); + abc.setB(lineTokens[1]); + abc.setC(lineTokens[2]); + return abc; + } + }; + } + } + + @Test + public void testEmptyInput() + { + TabFileLoader<ABC> loader = new TabFileLoader<ABC>(new ABCFactoryFactory()); + List<ABC> list = loader.load(new StringReader("")); + + assertEquals(0, list.size()); + } + + @Test + public void testFirstLineHasHeadersWithoutHashSymbolButNoRows() + { + TabFileLoader<ABC> loader = new TabFileLoader<ABC>(new ABCFactoryFactory()); + List<ABC> list = loader.load(new StringReader("A\tB\tC\n")); + + assertEquals(list.toString(), 0, list.size()); + } + + @Test + public void testFirstLineHasHeadersWithoutHashSymbol() + { + loadAndCheck(""); + } + + @Test + public void testFirstLineHasHeadersWithHashSymbol() + { + loadAndCheck("#"); + } + + @Test + public void testFirstLineHasMarkerAndSecondLineHasHeadersWithHashSymbol() + { + loadAndCheck("#\n#"); + } + + @Test + public void testFirstTwoLinesWithHashAndSomething() + { + loadAndCheck("#blabla\n" + "#blubub\n"); + } + + private void loadAndCheck(String preamble) + { + TabFileLoader<ABC> loader = new TabFileLoader<ABC>(new ABCFactoryFactory()); + List<ABC> list = loader.load(new StringReader(preamble + "A\tB\tC\n" + "a1\tb1\tc1\n" + "a2\tb2\tc2\n")); + + assertEquals(list.toString(), 2, list.size()); + assertEquals("a1b1c1", list.get(0).toString()); + assertEquals("a2b2c2", list.get(1).toString()); + } + +}