From fe8270010350f9d1c215d78b5e2db0702273c1dc Mon Sep 17 00:00:00 2001 From: gpawel <gpawel> Date: Tue, 30 Aug 2011 12:45:31 +0000 Subject: [PATCH] LMS-2462 import from Excel files SVN: 22710 --- .../cisd/common/filesystem/FileUtilities.java | 3 +- .../common/parser/DefaultLineTokenizer.java | 6 +- .../cisd/common/parser/DefaultParser.java | 28 +- .../cisd/common/parser/ExcelFileLoader.java | 366 ++++++++++++++++++ .../systemsx/cisd/common/parser/ExcelRow.java | 69 ++++ .../cisd/common/parser/ExcelRowTokenizer.java | 87 +++++ .../cisd/common/parser/HeaderLineFilter.java | 6 +- .../ch/systemsx/cisd/common/parser/ILine.java | 31 ++ .../cisd/common/parser/ILineTokenizer.java | 8 +- .../systemsx/cisd/common/parser/IParser.java | 6 +- .../ch/systemsx/cisd/common/parser/Line.java | 9 +- .../cisd/common/parser/ParserUtilities.java | 16 +- .../cisd/common/parser/TabFileLoader.java | 84 ++-- .../parser/filter/AlwaysAcceptLineFilter.java | 4 +- .../ExcludeEmptyAndCommentLineFilter.java | 6 +- .../common/parser/filter/ILineFilter.java | 4 +- .../parser/filter/NonEmptyLineFilter.java | 6 +- .../cisd/common/parser/DefaultParserTest.java | 78 ++-- .../common/parser/HeaderLineFilterTest.java | 22 +- .../common/parser/ParserUtilitiesTest.java | 14 +- .../java/MigrationStepExecutor.java | 10 +- .../shared/parser/BisExcelFileLoader.java | 109 ++++++ .../shared/parser/ExcelFileSection.java | 167 ++++++++ .../parser/SampleUploadSectionsParser.java | 146 +++++-- .../parser/MaterialUploadSectionsParser.java | 147 +++++-- 25 files changed, 1211 insertions(+), 221 deletions(-) create mode 100644 common/source/java/ch/systemsx/cisd/common/parser/ExcelFileLoader.java create mode 100644 common/source/java/ch/systemsx/cisd/common/parser/ExcelRow.java create mode 100644 common/source/java/ch/systemsx/cisd/common/parser/ExcelRowTokenizer.java create mode 100644 common/source/java/ch/systemsx/cisd/common/parser/ILine.java create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/parser/BisExcelFileLoader.java create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/parser/ExcelFileSection.java diff --git a/common/source/java/ch/systemsx/cisd/common/filesystem/FileUtilities.java b/common/source/java/ch/systemsx/cisd/common/filesystem/FileUtilities.java index dbe472c8751..2aed775d097 100644 --- a/common/source/java/ch/systemsx/cisd/common/filesystem/FileUtilities.java +++ b/common/source/java/ch/systemsx/cisd/common/filesystem/FileUtilities.java @@ -59,6 +59,7 @@ import ch.systemsx.cisd.common.exceptions.FileExistsException; import ch.systemsx.cisd.common.exceptions.UnknownLastChangedException; import ch.systemsx.cisd.common.logging.ISimpleLogger; import ch.systemsx.cisd.common.logging.LogLevel; +import ch.systemsx.cisd.common.parser.Line; import ch.systemsx.cisd.common.parser.filter.AlwaysAcceptLineFilter; import ch.systemsx.cisd.common.parser.filter.ILineFilter; import ch.systemsx.cisd.common.utilities.StringUtilities; @@ -410,7 +411,7 @@ public final class FileUtilities String line = reader.readLine(); for (int lineNumber = 0; line != null; ++lineNumber, line = reader.readLine()) { - if (lineFilter.acceptLine(line, lineNumber)) + if (lineFilter.acceptLine(new Line(lineNumber, line))) { list.add(line); } diff --git a/common/source/java/ch/systemsx/cisd/common/parser/DefaultLineTokenizer.java b/common/source/java/ch/systemsx/cisd/common/parser/DefaultLineTokenizer.java index db16bc4b767..394bb680603 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/DefaultLineTokenizer.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/DefaultLineTokenizer.java @@ -32,7 +32,7 @@ import org.apache.commons.lang.text.StrTokenizer; * * @author Christian Ribeaud */ -public class DefaultLineTokenizer implements ILineTokenizer +public class DefaultLineTokenizer implements ILineTokenizer<String> { /** Allowed <code>Properties</code> keys. */ @@ -82,8 +82,8 @@ public class DefaultLineTokenizer implements ILineTokenizer /** * Sets a property for this <code>TabReaderParser</code>. * <p> - * Does nothing if given <code>key</code> is <code>null</code> and resets <code>key</code> - * to default value if given <code>value</code> is <code>null</code>. + * Does nothing if given <code>key</code> is <code>null</code> and resets <code>key</code> to + * default value if given <code>value</code> is <code>null</code>. * </p> */ public final void setProperty(PropertyKey key, String value) diff --git a/common/source/java/ch/systemsx/cisd/common/parser/DefaultParser.java b/common/source/java/ch/systemsx/cisd/common/parser/DefaultParser.java index e715fc77493..014209e81f0 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/DefaultParser.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/DefaultParser.java @@ -32,24 +32,24 @@ import ch.systemsx.cisd.common.parser.filter.ILineFilter; * * @author Christian Ribeaud */ -public class DefaultParser<E> implements IParser<E> +public class DefaultParser<E, T> implements IParser<E, T> { - private final ILineTokenizer lineTokenizer; + private final ILineTokenizer<T> lineTokenizer; private IParserObjectFactory<E> factory; /** * Creates an instance based on the {@link DefaultLineTokenizer}. */ - public DefaultParser() + public static <E> DefaultParser<E, String> createDefaultParser() { - this(new DefaultLineTokenizer()); + return new DefaultParser<E, String>(new DefaultLineTokenizer()); } /** * Creates an instance for the specified line tokenizer. */ - public DefaultParser(final ILineTokenizer lineTokenizer) + public DefaultParser(final ILineTokenizer<T> lineTokenizer) { this.lineTokenizer = lineTokenizer; } @@ -59,17 +59,17 @@ public class DefaultParser<E> implements IParser<E> return factory.createObject(tokens); } - public final List<E> parse(final Iterator<Line> lineIterator, final ILineFilter lineFilter, + public final List<E> parse(final Iterator<ILine<T>> lineIterator, final ILineFilter lineFilter, final int headerLength) throws ParsingException { final List<E> elements = new ArrayList<E>(); lineTokenizer.init(); while (lineIterator.hasNext()) { - final Line line = lineIterator.next(); - final String nextLine = line.getText(); + final ILine<T> line = lineIterator.next(); + final T nextLine = line.getObject(); final int number = line.getNumber(); - if (lineFilter.acceptLine(nextLine, number)) + if (lineFilter.acceptLine(line)) { E object = null; String[] tokens = parseLine(number, nextLine, headerLength); @@ -91,13 +91,13 @@ public class DefaultParser<E> implements IParser<E> return elements; } - public final Iterator<E> parseIteratively(final Iterator<Line> lineIterator, + public final Iterator<E> parseIteratively(final Iterator<ILine<T>> lineIterator, final ILineFilter lineFilter, final int headerLength) throws ParsingException { lineTokenizer.init(); return new Iterator<E>() { - Line currentLine = null; + ILine<T> currentLine = null; public boolean hasNext() { @@ -105,7 +105,7 @@ public class DefaultParser<E> implements IParser<E> while (hasNext) { currentLine = lineIterator.next(); - if (lineFilter.acceptLine(currentLine.getText(), currentLine.getNumber())) + if (lineFilter.acceptLine(currentLine)) { break; } @@ -125,7 +125,7 @@ public class DefaultParser<E> implements IParser<E> { throw new NoSuchElementException(); } - final String nextLine = currentLine.getText(); + final T nextLine = currentLine.getObject(); final int number = currentLine.getNumber(); currentLine = null; String[] tokens = parseLine(number, nextLine, headerLength); @@ -151,7 +151,7 @@ public class DefaultParser<E> implements IParser<E> this.factory = factory; } - private String[] parseLine(final int lineNumber, final String nextLine, final int headerLength) + private String[] parseLine(final int lineNumber, final T nextLine, final int headerLength) { String[] tokens = lineTokenizer.tokenize(nextLine); if (tokens.length > headerLength) diff --git a/common/source/java/ch/systemsx/cisd/common/parser/ExcelFileLoader.java b/common/source/java/ch/systemsx/cisd/common/parser/ExcelFileLoader.java new file mode 100644 index 00000000000..c63545e93e2 --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/parser/ExcelFileLoader.java @@ -0,0 +1,366 @@ +/* + * 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.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; + +import ch.systemsx.cisd.common.exceptions.NotImplementedException; +import ch.systemsx.cisd.common.parser.filter.AlwaysAcceptLineFilter; +import ch.systemsx.cisd.common.parser.filter.ILineFilter; + +/** + * Convenient class to load (or iterate over) a tab file, a reader or a stream. The loader delivers + * either a list or an iterator of beans of type <code>T</code>. The following formats for the + * column headers are recognized. + * <ol> + * <li>Column headers in first line: + * + * <pre> + * column1 column2 column2 + * </pre> + * <li>Comment section: + * + * <pre> + * # 1. line of comment + * # 2. line of comment + * # ... + * column1 column2 column2 + * </pre> + * <li>Column headers at the end of the comment section: + * + * <pre> + * # 1. line of comment + * # 2. line of comment + * # ... + * # + * #column1 column2 column2 + * </pre> + * + * </ol> + * + * @author Franz-Josef Elmer + */ +public class ExcelFileLoader<T> +{ + public static final String COMMENT_PREFIX = "#"; + + // Excel can put comments in double quotes (") if they contain quote character. Multiple saving + // of the can cause multiple '"' to occure before the '#' character. + public static final Pattern COMMENT_REGEXP_PATTERN = Pattern.compile("\"*#.*"); + + public static final String DEFAULT_SECTION = "[DEFAULT]"; + + private final IParserObjectFactoryFactory<T> factory; + + /** + * Creates a new instance based on the factory which uses only bean annotations. + */ + public ExcelFileLoader(final Class<T> beanClass) + { + this.factory = new IParserObjectFactoryFactory<T>() + { + public IParserObjectFactory<T> createFactory(IPropertyMapper propertyMapper) + throws ParserException + { + return new AbstractParserObjectFactory<T>(beanClass, propertyMapper) + { + }; + } + }; + } + + /** + * Creates a new instance based on the specified factory. + */ + public ExcelFileLoader(final IParserObjectFactoryFactory<T> factory) + { + assert factory != null : "Undefined factory"; + this.factory = factory; + } + + /** + * Loads data from the specified reader. + * <p> + * The header can contain comments which are ignored. The column names can be the first + * uncommented line or the last commented line. The latter case is determined by the fact, that + * the one before the last line is a single hash. + * </p> + */ + public List<T> load(final Sheet sheet, int begin, int end, Map<String, String> defaults) + throws ParserException, ParsingException, IllegalArgumentException + { + assert sheet != null : "Unspecified reader"; + + final Iterator<ILine<Row>> rowIterator = createRowIterator(sheet, begin, end); + return load(rowIterator, defaults); + } + + public static final Map<String, String> parseDefaults(Sheet sheet, int begin, int end) + { + final Iterator<ILine<Row>> lineIterator = createRowIterator(sheet, begin, end); + final Map<String, String> defaults = new HashMap<String, String>(); + return parseDefaults(lineIterator, defaults); + } + + private static final Map<String, String> parseDefaults(final Iterator<ILine<Row>> rowIterator, + final Map<String, String> defaults) + { + while (rowIterator.hasNext()) + { + ILine<Row> row = rowIterator.next(); + if (startsDefaultSection(row)) + { + break; + } + + String[] tokens = ExcelRowTokenizer.tokenizeRow(row.getObject()); + if (tokens.length >= 2) + { + String name = tokens[0]; + String value = tokens[1]; + defaults.put(name.toLowerCase(), value); + } + } + + return defaults; + } + + private List<T> load(final Iterator<ILine<Row>> lineIterator, Map<String, String> fileDefaults) + { + ILine<Row> previousLine = null; + ILine<Row> line = null; + boolean previousLineHasColumnHeaders = false; + + Map<String, String> defaults = new HashMap<String, String>(fileDefaults); + while (lineIterator.hasNext()) + { + previousLineHasColumnHeaders = (previousLine != null) && isComment(previousLine); + previousLine = line; + line = lineIterator.next(); + if (isComment(line)) + { + continue; + } else if (startsDefaultSection(line)) + { + parseDefaults(lineIterator, defaults); + line = previousLine = null; + previousLineHasColumnHeaders = false; + } else if (isComment(line) == false) + { + break; + } + } + + if (line == null) // no lines present + { + return new ArrayList<T>(); + } + + Row headerRow = null; + if (previousLineHasColumnHeaders && (previousLine != null /* just for eclipse */)) + { + headerRow = trimComment(previousLine); + } else + { + headerRow = line.getObject(); + } + + final IParser<T, Row> parser = createParser(); + final String[] tokens = ExcelRowTokenizer.tokenizeRow(headerRow); + final int headerLength = tokens.length; + notUnique(tokens); + + final IPropertyMapper propertyMapper = new DefaultPropertyMapper(tokens, defaults); + parser.setObjectFactory(factory.createFactory(propertyMapper)); + + ILine<Row> firstContentLine = previousLineHasColumnHeaders ? line : null; + Iterator<ILine<Row>> contentLineIterator = + createContentIterator(firstContentLine, lineIterator); + final ILineFilter filter = AlwaysAcceptLineFilter.INSTANCE; + return parser.parse(contentLineIterator, filter, headerLength); + } + + private static boolean startsDefaultSection(Row row) + { + Cell cell = row.getCell(0); + if (cell == null || cell.getCellType() != Cell.CELL_TYPE_STRING) + { + return false; + } else + { + return DEFAULT_SECTION.equals(cell.getStringCellValue()); + } + } + + private static boolean startsDefaultSection(ILine<Row> row) + { + return startsDefaultSection(row.getObject()); + } + + private static Row trimComment(ILine<Row> previousLine) + { + if (isComment(previousLine)) + { + Cell firstCell = previousLine.getObject().getCell(0); + firstCell.setCellValue(firstCell.getStringCellValue() + .substring(COMMENT_PREFIX.length())); + } + return previousLine.getObject(); + } + + private static boolean isComment(ILine<Row> line) + { + Row row = line.getObject(); + return row.getCell(0) != null && row.getCell(0).toString().startsWith(COMMENT_PREFIX); + } + + /** + * @param firstContentLineOrNull if not null, it will be returned as the first iterator element, + * followed by all iterator elements from the second parameter + */ + private static Iterator<ILine<Row>> createContentIterator( + final ILine<Row> firstContentLineOrNull, final Iterator<ILine<Row>> lineIterator) + { + return new Iterator<ILine<Row>>() + { + private ILine<Row> firstLineOrNull = firstContentLineOrNull; + + public boolean hasNext() + { + return firstLineOrNull != null || lineIterator.hasNext(); + } + + public ILine<Row> next() + { + return nextLine(); + } + + private ILine<Row> nextLine() + { + if (firstLineOrNull != null) + { + ILine<Row> line = firstLineOrNull; + firstLineOrNull = null; + return line; + } else + { + return lineIterator.next(); + } + } + + public void remove() + { + throw new NotImplementedException(); + } + }; + } + + private static Iterator<ILine<Row>> createRowIterator(Sheet sheet, int begin, int end) + { + return new ExcelFileRowIterator(sheet, begin, end); + } + + /** + * Checks given <var>tokens</var> whether there is no duplicate. + * <p> + * Note that the search is case-insensitive. + * </p> + * + * @throws IllegalArgumentException if there is at least one duplicate in the given + * <var>tokens</var>. + */ + private final static void notUnique(final String[] tokens) + { + assert tokens != null : "Given tokens can not be null."; + final Set<String> unique = new HashSet<String>(); + for (final String token : tokens) + { + if (unique.add(token.toLowerCase()) == false) + { + throw new IllegalArgumentException(String.format("Duplicated column name '%s'.", + token)); + } + } + } + + private final <E> IParser<E, Row> createParser() + { + ExcelRowTokenizer tokenizer = new ExcelRowTokenizer(); + return new DefaultParser<E, Row>(tokenizer); + } + + // + // Helper classes + // + private final static class ExcelFileRowIterator implements Iterator<ILine<Row>> + { + private final Sheet sheet; + + private int current; + + private final int end; + + private ExcelFileRowIterator(Sheet sheet, int begin, int end) + { + this.sheet = sheet; + this.current = begin; + this.end = end; + getFirstNonEmptyCurrent(); + } + + private void getFirstNonEmptyCurrent() + { + while (sheet.getRow(current) == null && current <= end) + { + current++; + } + } + + public final void remove() + { + throw new UnsupportedOperationException(); + } + + public final ILine<Row> next() + { + try + { + return new ExcelRow(sheet.getRow(current)); + } finally + { + current++; + getFirstNonEmptyCurrent(); + } + } + + public final boolean hasNext() + { + return current <= end; + } + } +} \ No newline at end of file diff --git a/common/source/java/ch/systemsx/cisd/common/parser/ExcelRow.java b/common/source/java/ch/systemsx/cisd/common/parser/ExcelRow.java new file mode 100644 index 00000000000..545a43d51e7 --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/parser/ExcelRow.java @@ -0,0 +1,69 @@ +/* + * Copyright 2011 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 org.apache.poi.ss.usermodel.Row; + +/** + * @author Pawel Glyzewski + */ +public class ExcelRow implements ILine<Row> +{ + private Row row; + + private String text; + + public ExcelRow(Row row) + { + this.row = row; + } + + public String getText() + { + if (text == null) + { + text = toString(row); + } + return text; + } + + public int getNumber() + { + return row.getRowNum(); + } + + public Row getObject() + { + return row; + } + + private static String toString(Row row) + { + StringBuilder sb = new StringBuilder(); + + for (String s : new ExcelRowTokenizer().tokenize(row)) + { + if (s.length() > 0) + { + sb.append("\t"); + } + sb.append(s); + } + + return sb.toString(); + } +} diff --git a/common/source/java/ch/systemsx/cisd/common/parser/ExcelRowTokenizer.java b/common/source/java/ch/systemsx/cisd/common/parser/ExcelRowTokenizer.java new file mode 100644 index 00000000000..5be83b61689 --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/parser/ExcelRowTokenizer.java @@ -0,0 +1,87 @@ +/* + * Copyright 2011 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 org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.util.CellReference; + +import ch.systemsx.cisd.common.exceptions.UserFailureException; + +/** + * @author Pawel Glyzewski + */ +public class ExcelRowTokenizer implements ILineTokenizer<Row> +{ + public void init() + { + } + + public String[] tokenize(Row row) + { + return tokenizeRow(row); + } + + public static String[] tokenizeRow(Row row) + { + String[] line = new String[row.getLastCellNum()]; + for (Cell cell : row) + { + String value = extractCellValue(cell); + line[cell.getColumnIndex()] = value; + } + + return line; + } + + private static String extractCellValue(Cell cell) + { + switch (cell.getCellType()) + { + case Cell.CELL_TYPE_BLANK: + return "BLANK"; + case Cell.CELL_TYPE_BOOLEAN: + return Boolean.toString(cell.getBooleanCellValue()); + case Cell.CELL_TYPE_NUMERIC: + return Double.toString(cell.getNumericCellValue()); + case Cell.CELL_TYPE_STRING: + return cell.getStringCellValue(); + case Cell.CELL_TYPE_FORMULA: + throw new UserFailureException( + "Excel formulas are not supported but one was found in cell " + + extractCellPosition(cell)); + case Cell.CELL_TYPE_ERROR: + throw new UserFailureException("There is an error in cell " + + extractCellPosition(cell)); + default: + throw new UserFailureException("Unknown data type of cell " + + extractCellPosition(cell)); + } + } + + /** @return cell coordinates in Excel style (e.g. D5) */ + private static String extractCellPosition(Cell cell) + { + String col = CellReference.convertNumToColString(cell.getColumnIndex()); + String row = "" + (cell.getRowIndex() + 1); + return col + row; + } + + public void destroy() + { + } +} diff --git a/common/source/java/ch/systemsx/cisd/common/parser/HeaderLineFilter.java b/common/source/java/ch/systemsx/cisd/common/parser/HeaderLineFilter.java index 5701c069f1b..08837e5ebc1 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/HeaderLineFilter.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/HeaderLineFilter.java @@ -54,10 +54,10 @@ public final class HeaderLineFilter implements ILineFilter // LineFilter // - public final boolean acceptLine(String line, int lineNumber) + public final <T> boolean acceptLine(ILine<T> line) { - if (ExcludeEmptyAndCommentLineFilter.INSTANCE.acceptLine(line, lineNumber) == false - || lineNumber == headerLineNumber) + if (ExcludeEmptyAndCommentLineFilter.INSTANCE.acceptLine(line) == false + || line.getNumber() == headerLineNumber) { return false; } diff --git a/common/source/java/ch/systemsx/cisd/common/parser/ILine.java b/common/source/java/ch/systemsx/cisd/common/parser/ILine.java new file mode 100644 index 00000000000..db46e9cd992 --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/parser/ILine.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * A small object that represents a line in a <code>File</code> context. + * + * @author Pawel Glyzewski + */ +public interface ILine<T> +{ + public String getText(); + + public int getNumber(); + + public T getObject(); +} \ No newline at end of file diff --git a/common/source/java/ch/systemsx/cisd/common/parser/ILineTokenizer.java b/common/source/java/ch/systemsx/cisd/common/parser/ILineTokenizer.java index de749399a5d..91b7f82d504 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/ILineTokenizer.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/ILineTokenizer.java @@ -17,12 +17,12 @@ package ch.systemsx.cisd.common.parser; /** - * A <code>ILineTokenizer</code> implementation is able to split a given <code>String</code> - * line in an array of tokens. + * A <code>ILineTokenizer</code> implementation is able to split a given <code>T</code> line in an + * array of tokens. * * @author Christian Ribeaud */ -public interface ILineTokenizer +public interface ILineTokenizer<T> { /** @@ -34,7 +34,7 @@ public interface ILineTokenizer public void init(); /** Splits given <code>line</code> into an array of tokens. */ - public String[] tokenize(String line); + public String[] tokenize(T line); /** * Cleans up resources used by this <code>ILineTokenizer</code>. diff --git a/common/source/java/ch/systemsx/cisd/common/parser/IParser.java b/common/source/java/ch/systemsx/cisd/common/parser/IParser.java index 62de1595e0e..8912dff0c8c 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/IParser.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/IParser.java @@ -27,7 +27,7 @@ import ch.systemsx.cisd.common.parser.filter.ILineFilter; * * @author Christian Ribeaud */ -public interface IParser<E> +public interface IParser<E, T> { /** @@ -38,7 +38,7 @@ public interface IParser<E> * @param headerLength number of columns in the header * @return a <code>List</code> of elements. */ - public List<E> parse(final Iterator<Line> lineIterator, final ILineFilter lineFilter, + public List<E> parse(final Iterator<ILine<T>> lineIterator, final ILineFilter lineFilter, final int headerLength) throws ParsingException; /** @@ -51,7 +51,7 @@ public interface IParser<E> * @param headerLength number of columns in the header * @return an <code>List</code> of elements. */ - public Iterator<E> parseIteratively(final Iterator<Line> lineIterator, + public Iterator<E> parseIteratively(final Iterator<ILine<T>> lineIterator, final ILineFilter lineFilter, final int headerLength) throws ParsingException; /** diff --git a/common/source/java/ch/systemsx/cisd/common/parser/Line.java b/common/source/java/ch/systemsx/cisd/common/parser/Line.java index dfcd0d41157..2e8745e063e 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/Line.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/Line.java @@ -21,13 +21,13 @@ package ch.systemsx.cisd.common.parser; * * @author Christian Ribeaud */ -public final class Line +public final class Line implements ILine<String> { private final String text; private final int number; - Line(final int number, final String text) + public Line(final int number, final String text) { assert text != null : "Unspecified text."; this.number = number; @@ -44,4 +44,9 @@ public final class Line return number; } + public String getObject() + { + return text; + } + } \ 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 6721c422f3f..0778dd90ee4 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/ParserUtilities.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/ParserUtilities.java @@ -59,6 +59,7 @@ public final class ParserUtilities { this(content, null); } + public LineSplitter(final String content, final ILineFilter lineFilterOrNull) { assert content != null : "Unspecified context."; @@ -72,7 +73,7 @@ public final class ParserUtilities { this(file, null); } - + public LineSplitter(final File file, final ILineFilter lineFilterOrNull) throws IOExceptionUnchecked { @@ -93,7 +94,7 @@ public final class ParserUtilities return (lineFilterOrNull == null) ? AlwaysAcceptLineFilter.INSTANCE : lineFilterOrNull; } - public void close() + public void close() { lineIterator.close(); } @@ -101,15 +102,16 @@ public final class ParserUtilities /** * Returns the next line that is accepted by the <var>lineFilter</var>. */ - public Line tryNextLine() + public ILine<String> tryNextLine() { for (int line = lineNumber; lineIterator.hasNext(); line++) { final String nextLine = lineIterator.nextLine(); - if (lineFilter.acceptLine(nextLine, line)) + final Line ret = new Line(line, nextLine); + if (lineFilter.acceptLine(ret)) { lineNumber = line + 1; - return new Line(line, nextLine); + return ret; } } return null; @@ -128,7 +130,7 @@ public final class ParserUtilities * @param content the content that is going to be analyzed. Can not be <code>null</code>. * @return <code>null</code> if all lines have been filtered out. */ - public final static Line tryGetFirstAcceptedLine(final String content, + public final static ILine<String> tryGetFirstAcceptedLine(final String content, final ILineFilter lineFilterOrNull) { final LineSplitter splitter = new LineSplitter(content, lineFilterOrNull); @@ -154,7 +156,7 @@ public final class ParserUtilities * exists. * @return <code>null</code> if all lines have been filtered out. */ - public final static Line tryGetFirstAcceptedLine(final File file, + public final static ILine<String> tryGetFirstAcceptedLine(final File file, final ILineFilter lineFilterOrNull) { final LineSplitter splitter = new LineSplitter(file, lineFilterOrNull); 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 c47a095b455..4dea725e2cd 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/TabFileLoader.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/TabFileLoader.java @@ -184,7 +184,7 @@ public class TabFileLoader<T> { assert reader != null : "Unspecified reader"; - final Iterator<Line> lineIterator = createLineIterator(reader); + final Iterator<ILine<String>> lineIterator = createLineIterator(reader); return iterate(lineIterator, defaults); } @@ -201,7 +201,7 @@ public class TabFileLoader<T> { assert reader != null : "Unspecified reader"; - final Iterator<Line> lineIterator = createLineIterator(reader); + final Iterator<ILine<String>> lineIterator = createLineIterator(reader); return load(lineIterator, defaults); } @@ -220,7 +220,7 @@ public class TabFileLoader<T> try { - final Iterator<Line> lineIterator = createLineIterator(stream); + final Iterator<ILine<String>> lineIterator = createLineIterator(stream); return iterate(lineIterator, defaults); } catch (IOException ex) { @@ -243,7 +243,7 @@ public class TabFileLoader<T> try { - final Iterator<Line> lineIterator = createLineIterator(stream); + final Iterator<ILine<String>> lineIterator = createLineIterator(stream); return load(lineIterator, defaults); } catch (IOException ex) { @@ -253,17 +253,17 @@ public class TabFileLoader<T> public static final Map<String, String> parseDefaults(Reader reader) { - final Iterator<Line> lineIterator = createLineIterator(reader); + final Iterator<ILine<String>> lineIterator = createLineIterator(reader); final Map<String, String> defaults = new HashMap<String, String>(); return parseDefaults(lineIterator, defaults); } - private static final Map<String, String> parseDefaults(final Iterator<Line> lineIterator, - final Map<String, String> defaults) + private static final Map<String, String> parseDefaults( + final Iterator<ILine<String>> lineIterator, final Map<String, String> defaults) { while (lineIterator.hasNext()) { - Line line = lineIterator.next(); + ILine<String> line = lineIterator.next(); String text = line.getText(); if (DEFAULT_SECTION.equals(text)) { @@ -281,10 +281,11 @@ public class TabFileLoader<T> return defaults; } - private List<T> load(final Iterator<Line> lineIterator, Map<String, String> fileDefaults) + private List<T> load(final Iterator<ILine<String>> lineIterator, + Map<String, String> fileDefaults) { - Line previousLine = null; - Line line = null; + ILine<String> previousLine = null; + ILine<String> line = null; boolean previousLineHasColumnHeaders = false; Map<String, String> defaults = new HashMap<String, String>(fileDefaults); @@ -321,7 +322,7 @@ public class TabFileLoader<T> headerLine = line.getText(); } - final IParser<T> parser = createParser(); + final IParser<T, String> parser = createParser(); final String[] tokens = StringUtils.split(headerLine, TOKENS_SEPARATOR); int lastEmptyHeadersToSkip = countLastEmptyTokens(headerLine); final int headerLength = tokens.length; @@ -330,17 +331,18 @@ public class TabFileLoader<T> final IPropertyMapper propertyMapper = new DefaultPropertyMapper(tokens, defaults); parser.setObjectFactory(factory.createFactory(propertyMapper)); - Line firstContentLine = previousLineHasColumnHeaders ? line : null; - Iterator<Line> contentLineIterator = + ILine<String> firstContentLine = previousLineHasColumnHeaders ? line : null; + Iterator<ILine<String>> contentLineIterator = createContentIterator(firstContentLine, lineIterator, lastEmptyHeadersToSkip); final ILineFilter filter = AlwaysAcceptLineFilter.INSTANCE; return parser.parse(contentLineIterator, filter, headerLength); } - private Iterator<T> iterate(final Iterator<Line> lineIterator, Map<String, String> fileDefaults) + private Iterator<T> iterate(final Iterator<ILine<String>> lineIterator, + Map<String, String> fileDefaults) { - Line previousLine = null; - Line line = null; + ILine<String> previousLine = null; + ILine<String> line = null; boolean previousLineHasColumnHeaders = false; Map<String, String> defaults = new HashMap<String, String>(fileDefaults); @@ -393,7 +395,7 @@ public class TabFileLoader<T> headerLine = line.getText(); } - final IParser<T> parser = createParser(); + final IParser<T, String> parser = createParser(); final String[] tokens = StringUtils.split(headerLine, TOKENS_SEPARATOR); int lastEmptyHeadersToSkip = countLastEmptyTokens(headerLine); final int headerLength = tokens.length; @@ -402,26 +404,26 @@ public class TabFileLoader<T> final IPropertyMapper propertyMapper = new DefaultPropertyMapper(tokens, defaults); parser.setObjectFactory(factory.createFactory(propertyMapper)); - Line firstContentLine = previousLineHasColumnHeaders ? line : null; - Iterator<Line> contentLineIterator = + ILine<String> firstContentLine = previousLineHasColumnHeaders ? line : null; + Iterator<ILine<String>> contentLineIterator = createContentIterator(firstContentLine, lineIterator, lastEmptyHeadersToSkip); final ILineFilter filter = AlwaysAcceptLineFilter.INSTANCE; return parser.parseIteratively(contentLineIterator, filter, headerLength); } - private static boolean startsDefaultSection(Line line) + private static boolean startsDefaultSection(ILine<String> line) { String text = line.getText(); return DEFAULT_SECTION.equals(text); } - private static boolean startsWithComment(Line line) + private static boolean startsWithComment(ILine<String> line) { String text = line.getText(); return COMMENT_REGEXP_PATTERN.matcher(text).matches(); } - private static String trimComment(Line previousLine) + private static String trimComment(ILine<String> previousLine) { String text = previousLine.getText().trim(); if (text.startsWith(COMMENT_PREFIX)) @@ -433,7 +435,7 @@ public class TabFileLoader<T> } } - private static boolean isComment(Line line) + private static boolean isComment(ILine<String> line) { String text = line.getText(); return COMMENT_PREFIX.equals(text); @@ -445,26 +447,27 @@ public class TabFileLoader<T> * @param lastEmptyTokensToSkip the number of token separators which will be removed form the * end of each iterated line */ - private static Iterator<Line> createContentIterator(final Line firstContentLineOrNull, - final Iterator<Line> lineIterator, final int lastEmptyTokensToSkip) + private static Iterator<ILine<String>> createContentIterator( + final ILine<String> firstContentLineOrNull, final Iterator<ILine<String>> lineIterator, + final int lastEmptyTokensToSkip) { final String suffixToDelete = multiply(lastEmptyTokensToSkip, TOKENS_SEPARATOR); - return new Iterator<Line>() + return new Iterator<ILine<String>>() { - private Line firstLineOrNull = firstContentLineOrNull; + private ILine<String> firstLineOrNull = firstContentLineOrNull; public boolean hasNext() { return firstLineOrNull != null || lineIterator.hasNext(); } - public Line next() + public ILine<String> next() { return trim(nextUntrimmed()); } - private Line trim(Line line) + private ILine<String> trim(ILine<String> line) { if (lastEmptyTokensToSkip == 0) { @@ -496,11 +499,11 @@ public class TabFileLoader<T> } } - private Line nextUntrimmed() + private ILine<String> nextUntrimmed() { if (firstLineOrNull != null) { - Line line = firstLineOrNull; + ILine<String> line = firstLineOrNull; firstLineOrNull = null; return line; } else @@ -539,18 +542,19 @@ public class TabFileLoader<T> return counter; } - private static Iterator<Line> createLineIterator(final Reader reader) + private static Iterator<ILine<String>> createLineIterator(final Reader reader) { final LineIterator lineIterator = IOUtils.lineIterator(reader); - final Iterator<Line> iterator = new TabFileLineIterator(lineIterator); + final Iterator<ILine<String>> iterator = new TabFileLineIterator(lineIterator); return iterator; } - private static Iterator<Line> createLineIterator(final InputStream stream) throws IOException + private static Iterator<ILine<String>> createLineIterator(final InputStream stream) + throws IOException { final LineIterator lineIterator = IOUtils.lineIterator(stream, UnicodeUtils.DEFAULT_UNICODE_CHARSET); - final Iterator<Line> iterator = new TabFileLineIterator(lineIterator); + final Iterator<ILine<String>> iterator = new TabFileLineIterator(lineIterator); return iterator; } @@ -577,19 +581,19 @@ public class TabFileLoader<T> } } - private final <E> IParser<E> createParser() + private final <E> IParser<E, String> createParser() { DefaultLineTokenizer tokenizer = new DefaultLineTokenizer(); // recognize default Excel text qualifiers tokenizer.setProperty(PropertyKey.QUOTE_CHARS, "'\""); - return new DefaultParser<E>(tokenizer); + return new DefaultParser<E, String>(tokenizer); } // // Helper classes // - private final static class TabFileLineIterator implements Iterator<Line> + private final static class TabFileLineIterator implements Iterator<ILine<String>> { private final LineIterator lineIterator; @@ -610,7 +614,7 @@ public class TabFileLoader<T> lineIterator.remove(); } - public final Line next() + public final ILine<String> next() { return new Line(++lineNumber, lineIterator.nextLine()); } diff --git a/common/source/java/ch/systemsx/cisd/common/parser/filter/AlwaysAcceptLineFilter.java b/common/source/java/ch/systemsx/cisd/common/parser/filter/AlwaysAcceptLineFilter.java index 6b9a72647b1..309684912c6 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/filter/AlwaysAcceptLineFilter.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/filter/AlwaysAcceptLineFilter.java @@ -16,6 +16,8 @@ package ch.systemsx.cisd.common.parser.filter; +import ch.systemsx.cisd.common.parser.ILine; + /** * A default line filter that accepts any line. * @@ -33,7 +35,7 @@ public final class AlwaysAcceptLineFilter implements ILineFilter // ILineFilter // - public final boolean acceptLine(final String line, final int lineNumber) + public final <T> boolean acceptLine(ILine<T> line) { return true; } diff --git a/common/source/java/ch/systemsx/cisd/common/parser/filter/ExcludeEmptyAndCommentLineFilter.java b/common/source/java/ch/systemsx/cisd/common/parser/filter/ExcludeEmptyAndCommentLineFilter.java index 91b12e2262f..3b029bbd75c 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/filter/ExcludeEmptyAndCommentLineFilter.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/filter/ExcludeEmptyAndCommentLineFilter.java @@ -16,6 +16,8 @@ package ch.systemsx.cisd.common.parser.filter; +import ch.systemsx.cisd.common.parser.ILine; + /** * A default <code>LineFilter</code> implementation that excludes empty and comment lines. * <p> @@ -36,10 +38,10 @@ public final class ExcludeEmptyAndCommentLineFilter implements ILineFilter // ILineFilter // - public final boolean acceptLine(final String line, final int lineNumber) + public final <T> boolean acceptLine(ILine<T> line) { assert line != null : "Unspecified line"; - final String trimmed = line.trim(); + final String trimmed = line.getText().trim(); return trimmed.length() > 0 && trimmed.startsWith("#") == false; } } \ No newline at end of file diff --git a/common/source/java/ch/systemsx/cisd/common/parser/filter/ILineFilter.java b/common/source/java/ch/systemsx/cisd/common/parser/filter/ILineFilter.java index 5c2e5cad8ff..7f66d1afda0 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/filter/ILineFilter.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/filter/ILineFilter.java @@ -16,6 +16,8 @@ package ch.systemsx.cisd.common.parser.filter; +import ch.systemsx.cisd.common.parser.ILine; + /** * A line filter for <code>ReaderParser</code>. * @@ -29,5 +31,5 @@ public interface ILineFilter * * @param line the line read from the <code>Reader</code>. Can not be <code>null</code>. */ - public boolean acceptLine(final String line, final int lineNumber); + public <T> boolean acceptLine(final ILine<T> line); } diff --git a/common/source/java/ch/systemsx/cisd/common/parser/filter/NonEmptyLineFilter.java b/common/source/java/ch/systemsx/cisd/common/parser/filter/NonEmptyLineFilter.java index 431073d753c..969b005824b 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/filter/NonEmptyLineFilter.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/filter/NonEmptyLineFilter.java @@ -17,6 +17,8 @@ package ch.systemsx.cisd.common.parser.filter; import org.apache.commons.lang.StringUtils; +import ch.systemsx.cisd.common.parser.ILine; + /** * A default <code>LineFilter</code> implementation that excludes empty lines. * @@ -34,8 +36,8 @@ public final class NonEmptyLineFilter implements ILineFilter // ILineFilter // - public final boolean acceptLine(final String line, final int lineNumber) + public final <T> boolean acceptLine(ILine<T> line) { - return StringUtils.isNotBlank(line); + return StringUtils.isNotBlank(line.getText()); } } \ No newline at end of file diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/parser/DefaultParserTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/parser/DefaultParserTest.java index 8b083ef7993..4b0b19824a9 100644 --- a/common/sourceTest/java/ch/systemsx/cisd/common/parser/DefaultParserTest.java +++ b/common/sourceTest/java/ch/systemsx/cisd/common/parser/DefaultParserTest.java @@ -16,7 +16,10 @@ package ch.systemsx.cisd.common.parser; -import static org.testng.AssertJUnit.*; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertTrue; +import static org.testng.AssertJUnit.fail; import java.util.Arrays; import java.util.Iterator; @@ -31,25 +34,24 @@ import org.testng.annotations.Test; */ public final class DefaultParserTest { - 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"); + 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"); - private final static List<String> textWithTab = - Arrays.asList("", "# This is a comment", "firstName\tlastName\taddress\tcity", - "Charles\tDarwin\tHumboldt Ave. 1865\t4242 Somewhere", - "Albert\t\tNewton Road 1905\t"); + private final static List<String> textWithTab = Arrays.asList("", "# This is a comment", + "firstName\tlastName\taddress\tcity", + "Charles\tDarwin\tHumboldt Ave. 1865\t4242 Somewhere", "Albert\t\tNewton Road 1905\t"); - private final static List<String> textWithMissingLastCells = - Arrays.asList("", "# This is a comment", "firstName\tlastName\taddress\tcity", - "\tDarwin\tHumboldt Ave. 1865", "Albert\tEinstein"); + private final static List<String> textWithMissingLastCells = Arrays.asList("", + "# This is a comment", "firstName\tlastName\taddress\tcity", + "\tDarwin\tHumboldt Ave. 1865", "Albert\tEinstein"); private final static int HEADER_LENGTH = 4; - private final static IParser<String[]> createParser() + private final static IParser<String[], String> createParser() { - final IParser<String[]> parser = new DefaultParser<String[]>(); + final IParser<String[], String> parser = DefaultParser.createDefaultParser(); parser.setObjectFactory(IParserObjectFactory.STRING_ARRAY_OBJECT_FACTORY); return parser; } @@ -57,7 +59,7 @@ public final class DefaultParserTest @Test public final void testParseWithoutFactoryAndHeader() { - final IParser<String[]> parser = createParser(); + final IParser<String[], String> parser = createParser(); final List<String[]> result = parser.parse(createLineIterator(text), new HeaderLineFilter(), HEADER_LENGTH); assertEquals(3, result.size()); @@ -70,7 +72,7 @@ public final class DefaultParserTest @Test public final void testParseIterativelyWithoutFactoryAndHeader() { - final IParser<String[]> parser = createParser(); + final IParser<String[], String> parser = createParser(); final Iterator<String[]> result = parser.parseIteratively(createLineIterator(text), new HeaderLineFilter(), HEADER_LENGTH); @@ -89,7 +91,7 @@ public final class DefaultParserTest @Test public final void testParseWithoutFactoryWithLineFilter() { - final IParser<String[]> parser = createParser(); + final IParser<String[], String> parser = createParser(); final List<String[]> result = parser.parse(createLineIterator(text), new HeaderLineFilter(3), HEADER_LENGTH); assertEquals(2, result.size()); @@ -100,11 +102,9 @@ public final class DefaultParserTest @Test public final void testParseFileWithTabs() { - final IParser<String[]> parser = createParser(); + final IParser<String[], String> parser = createParser(); final List<String[]> result = - parser - .parse(createLineIterator(textWithTab), new HeaderLineFilter(), - HEADER_LENGTH); + parser.parse(createLineIterator(textWithTab), new HeaderLineFilter(), HEADER_LENGTH); assertEquals(3, result.size()); assertEquals("Albert", result.get(2)[0]); assertEquals("", result.get(2)[1]); @@ -115,7 +115,7 @@ public final class DefaultParserTest @Test public final void testParseFileWithMissingLastCells() { - final IParser<String[]> parser = createParser(); + final IParser<String[], String> parser = createParser(); final List<String[]> result = parser.parse(createLineIterator(textWithMissingLastCells), new HeaderLineFilter(), HEADER_LENGTH); @@ -133,7 +133,7 @@ public final class DefaultParserTest @Test public final void testParseWithColumnSizeMismatching() { - final IParser<String[]> parser = createParser(); + final IParser<String[], String> parser = createParser(); try { parser.parse(createLineIterator(text), new HeaderLineFilter(3), HEADER_LENGTH + 1); @@ -149,18 +149,20 @@ public final class DefaultParserTest @Test public final void testCreateObjectWithParserException() { - final IParser<String[]> parser = new DefaultParser<String[]>() - { - // - // DefaultReaderParser - // - - @Override - protected final String[] createObject(final String[] tokens) throws ParserException - { - throw new ParserException(""); - } - }; + final IParser<String[], String> parser = + new DefaultParser<String[], String>(new DefaultLineTokenizer()) + { + // + // DefaultReaderParser + // + + @Override + protected final String[] createObject(final String[] tokens) + throws ParserException + { + throw new ParserException(""); + } + }; parser.setObjectFactory(IParserObjectFactory.STRING_ARRAY_OBJECT_FACTORY); try { @@ -175,9 +177,9 @@ public final class DefaultParserTest } } - private Iterator<Line> createLineIterator(final List<String> lines) + private Iterator<ILine<String>> createLineIterator(final List<String> lines) { - return new Iterator<Line>() + return new Iterator<ILine<String>>() { private final Iterator<String> iterator = lines.iterator(); @@ -188,7 +190,7 @@ public final class DefaultParserTest throw new UnsupportedOperationException(); } - public Line next() + public ILine<String> next() { return new Line(++lineNumber, iterator.next()); } diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/parser/HeaderLineFilterTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/parser/HeaderLineFilterTest.java index dd5103f7b36..7c4eb1280ae 100644 --- a/common/sourceTest/java/ch/systemsx/cisd/common/parser/HeaderLineFilterTest.java +++ b/common/sourceTest/java/ch/systemsx/cisd/common/parser/HeaderLineFilterTest.java @@ -33,7 +33,7 @@ public class HeaderLineFilterTest final HeaderLineFilter filter = new HeaderLineFilter(); for (int i = 0; i < 4; ++i) { - assert filter.acceptLine("text", i) : "Line " + i; + assert filter.acceptLine(new Line(i, "text")) : "Line " + i; } } @@ -56,12 +56,12 @@ public class HeaderLineFilterTest { if (headerLine == i) { - assert filter.acceptLine("text", i) == false : "Line " + i + " (header line: " - + headerLine + ")"; + assert filter.acceptLine(new Line(i, "text")) == false : "Line " + i + + " (header line: " + headerLine + ")"; } else { - assert filter.acceptLine("text", i) : "Line " + i + " (header line: " + headerLine - + ")"; + assert filter.acceptLine(new Line(i, "text")) : "Line " + i + " (header line: " + + headerLine + ")"; } } } @@ -70,36 +70,36 @@ public class HeaderLineFilterTest public void testEmptyHeaderLine() { final HeaderLineFilter filter = new HeaderLineFilter(0); - assert filter.acceptLine("", 0) == false; - assert filter.acceptLine("something", 1); + assert filter.acceptLine(new Line(0, "")) == false; + assert filter.acceptLine(new Line(1, "something")); } @Test public void testSkipEmptyLine() { final HeaderLineFilter filter = new HeaderLineFilter(); - assert filter.acceptLine("", 0) == false; + assert filter.acceptLine(new Line(0, "")) == false; } @Test public void testSkipBlankLine() { final HeaderLineFilter filter = new HeaderLineFilter(); - assert filter.acceptLine(" ", 0) == false; + assert filter.acceptLine(new Line(0, " ")) == false; } @Test public void testSkipCommentLine() { final HeaderLineFilter filter = new HeaderLineFilter(); - assert filter.acceptLine("# Some comment", 0) == false; + assert filter.acceptLine(new Line(0, "# Some comment")) == false; } @Test public void testSkipCommentLineWithLeadingWhiteSpace() { final HeaderLineFilter filter = new HeaderLineFilter(); - assert filter.acceptLine("\t# Some comment", 0) == false; + assert filter.acceptLine(new Line(0, "\t# Some comment")) == false; } } 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 d4f948377f0..461ea7cefa2 100644 --- a/common/sourceTest/java/ch/systemsx/cisd/common/parser/ParserUtilitiesTest.java +++ b/common/sourceTest/java/ch/systemsx/cisd/common/parser/ParserUtilitiesTest.java @@ -43,11 +43,11 @@ import ch.systemsx.cisd.common.parser.filter.NonEmptyLineFilter; public final class ParserUtilitiesTest { - private static final File unitTestRootDirectory = - new File("targets" + File.separator + "unit-test-wd"); + private static final File unitTestRootDirectory = new File("targets" + File.separator + + "unit-test-wd"); - private static final File workingDirectory = - new File(unitTestRootDirectory, "ParserUtilitiesTest"); + private static final File workingDirectory = new File(unitTestRootDirectory, + "ParserUtilitiesTest"); @BeforeClass public final void setUp() @@ -97,7 +97,7 @@ public final class ParserUtilitiesTest { StringUtils.EMPTY, "non-empty line", StringUtils.EMPTY, "hello" }; File file = new File(workingDirectory, "test.txt"); FileUtils.writeLines(file, Arrays.asList(lines)); - Line line = ParserUtilities.tryGetFirstAcceptedLine(file, null); + ILine<String> line = ParserUtilities.tryGetFirstAcceptedLine(file, null); assertEquals(StringUtils.EMPTY, line.getText()); assertEquals(0, line.getNumber()); assert file.delete(); @@ -112,7 +112,7 @@ public final class ParserUtilitiesTest FileUtils.writeLines(file, Arrays.asList(lines)); ParserUtilities.LineSplitter splitter = new ParserUtilities.LineSplitter(file, NonEmptyLineFilter.INSTANCE); - Line line = splitter.tryNextLine(); + ILine<String> line = splitter.tryNextLine(); assertEquals("non-empty line", line.getText()); assertEquals(1, line.getNumber()); line = splitter.tryNextLine(); @@ -129,7 +129,7 @@ public final class ParserUtilitiesTest { StringUtils.EMPTY, "# comment line", StringUtils.EMPTY, "hello" }; File file = new File(workingDirectory, "test.txt"); FileUtils.writeLines(file, Arrays.asList(lines)); - Line line = + ILine<String> line = ParserUtilities.tryGetFirstAcceptedLine(file, ExcludeEmptyAndCommentLineFilter.INSTANCE); assertEquals("hello", line.getText()); diff --git a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/java/MigrationStepExecutor.java b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/java/MigrationStepExecutor.java index c86915657ca..97445fe63d0 100644 --- a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/java/MigrationStepExecutor.java +++ b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/java/MigrationStepExecutor.java @@ -24,7 +24,7 @@ import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; import ch.systemsx.cisd.common.Script; import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; -import ch.systemsx.cisd.common.parser.Line; +import ch.systemsx.cisd.common.parser.ILine; import ch.systemsx.cisd.common.parser.ParserUtilities; import ch.systemsx.cisd.common.parser.filter.ILineFilter; import ch.systemsx.cisd.common.utilities.ClassUtils; @@ -79,13 +79,15 @@ public class MigrationStepExecutor extends SimpleJdbcDaoSupport implements IMigr final ParserUtilities.LineSplitter splitter = new ParserUtilities.LineSplitter(content, new ILineFilter() { - public boolean acceptLine(String line, int lineNumber) + public <T> boolean acceptLine(ILine<T> line) { - return StringUtils.isNotBlank(line) && line.startsWith("--"); + String text = line.getText(); + return StringUtils.isNotBlank(text) && text.startsWith("--"); } + }); IMigrationStep stepOrNull = null; - Line lineOrNull; + ILine<?> lineOrNull; while (stepOrNull == null && (lineOrNull = splitter.tryNextLine()) != null) { stepOrNull = tryExtractMigrationStepFromLine(lineOrNull.getText()); diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/parser/BisExcelFileLoader.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/parser/BisExcelFileLoader.java new file mode 100644 index 00000000000..56556fce0f7 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/parser/BisExcelFileLoader.java @@ -0,0 +1,109 @@ +/* + * 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.openbis.generic.shared.parser; + +import java.util.List; +import java.util.Map; + +import org.apache.poi.ss.usermodel.Sheet; + +import ch.systemsx.cisd.common.exceptions.UserFailureException; +import ch.systemsx.cisd.common.io.NamedReader; +import ch.systemsx.cisd.common.parser.ExcelFileLoader; +import ch.systemsx.cisd.common.parser.IParserObjectFactoryFactory; +import ch.systemsx.cisd.common.parser.ParserException; +import ch.systemsx.cisd.common.parser.ParsingException; +import ch.systemsx.cisd.common.parser.TabFileLoader; + +/** + * A <i>openBIS</i> {@link TabFileLoader} extension which translates a {@link ParsingException} into + * a more user friendly {@link UserFailureException}. + * <p> + * Note that this extension prefers to work with {@link NamedReader}. + * </p> + * + * @author Christian Ribeaud + */ +public final class BisExcelFileLoader<T> extends ExcelFileLoader<T> +{ + + private static final String MESSAGE_FORMAT = + "A problem has occurred while parsing line %d of file '%s':\n %s"; + + private static final String ERROR_IN_FILE_MESSAGE_FORMAT = + "A problem has occurred while parsing file '%s':\n %s"; + + private final boolean acceptEmptyFiles; + + public BisExcelFileLoader(final IParserObjectFactoryFactory<T> factory, boolean acceptEmptyFiles) + { + super(factory); + this.acceptEmptyFiles = acceptEmptyFiles; + } + + private final static void translateParsingException(final ParsingException parsingException, + final String sectionName) + { + final RuntimeException causeException = parsingException.getCauseRuntimeException(); + final String message = + causeException == null ? parsingException.getMessage() : causeException + .getMessage(); + throw UserFailureException.fromTemplate(MESSAGE_FORMAT, parsingException.getLineNumber(), + sectionName, message); + } + + private final static void translateParserException(final ParserException ex, + final String sectionName) + { + final String message = ex.getMessage(); + throw UserFailureException.fromTemplate(ERROR_IN_FILE_MESSAGE_FORMAT, sectionName, message); + } + + // + // TabFileLoader + // + public List<T> load(final Sheet sheet, int begin, int end, String sectionName, + final Map<String, String> defaults) throws ParserException, ParsingException, + IllegalArgumentException + { + assert sheet != null : "Excel Sheet cannot be null."; + try + { + final List<T> list = super.load(sheet, begin, end, defaults); + if (acceptEmptyFiles == false && list.size() == 0) + { + throw new UserFailureException("Given file '" + sectionName + + "' is empty or does not contain any meaningful information."); + } + return list; + } catch (final IllegalArgumentException illegalArgumentException) + { + throw new UserFailureException(illegalArgumentException.getMessage(), + illegalArgumentException); + } catch (final ParsingException e) + { + translateParsingException(e, sectionName); + // Never used. + return null; + } catch (final ParserException e) + { + translateParserException(e, sectionName); + // Never used. + return null; + } + } +} diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/parser/ExcelFileSection.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/parser/ExcelFileSection.java new file mode 100644 index 00000000000..dd4a7c33c0b --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/parser/ExcelFileSection.java @@ -0,0 +1,167 @@ +/* + * Copyright 2011 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.openbis.generic.shared.parser; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; + +import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked; +import ch.systemsx.cisd.common.exceptions.UserFailureException; + +/** + * @author Pawel Glyzewski + */ +public class ExcelFileSection +{ + private static final String SECTION_FILE_DEFAULT = "DEFAULT"; + + private final Sheet sheet; + + private final int begin; + + private final int end; + + private final String sectionName; + + private ExcelFileSection(Sheet sheet, String sectionName, int begin, int end) + { + assert sheet != null; + this.sectionName = sectionName; + this.sheet = sheet; + this.begin = begin; + this.end = end; + } + + public String getSectionName() + { + return sectionName; + } + + public Sheet getSheet() + { + return sheet; + } + + public int getBegin() + { + return begin; + } + + public int getEnd() + { + return end; + } + + public static ExcelFileSection createFromInputStream(InputStream stream, String sectionName) + { + try + { + POIFSFileSystem poifsFileSystem = new POIFSFileSystem(stream); + Workbook wb = new HSSFWorkbook(poifsFileSystem); + Sheet sheet = wb.getSheetAt(0); + return new ExcelFileSection(sheet, sectionName, 0, sheet.getLastRowNum()); + } catch (IOException e) + { + throw new IOExceptionUnchecked(e); + } + } + + public static List<ExcelFileSection> extractSections(InputStream stream) + { + List<ExcelFileSection> sections = new ArrayList<ExcelFileSection>(); + try + { + POIFSFileSystem poifsFileSystem = new POIFSFileSystem(stream); + Workbook wb = new HSSFWorkbook(poifsFileSystem); + Sheet sheet = wb.getSheetAt(0); + String sectionName = null; + Integer begin = null; + for (Row row : sheet) + { + String newSectionName = tryGetSectionName(row); + if (newSectionName != null) + { + if (sectionName != null && begin != null) + { + if (sectionName.equals(newSectionName)) + { + continue; + } + if (newSectionName.equals(SECTION_FILE_DEFAULT)) + { + continue; + } else + { + sections.add(new ExcelFileSection(sheet, sectionName, begin, row + .getRowNum())); + sectionName = newSectionName; + begin = row.getRowNum() + 1; + } + } else + { + sectionName = newSectionName; + begin = row.getRowNum() + 1; + } + } else if (sectionName == null || begin == null) + { + throw new UserFailureException("Discovered the unnamed section in the file"); + } + if (row.getRowNum() == sheet.getLastRowNum()) + { + sections.add(new ExcelFileSection(sheet, sectionName, begin, row.getRowNum())); + } + } + } catch (IOException e) + { + throw new IOExceptionUnchecked(e); + } finally + { + IOUtils.closeQuietly(stream); + } + return sections; + } + + private static String tryGetSectionName(Row row) + { + final String beginSection = "["; + final String endSection = "]"; + if (row == null || row.getCell(0) == null + || row.getCell(0).getCellType() != Cell.CELL_TYPE_STRING) + { + return null; + } + String trimmedCell = row.getCell(0).getStringCellValue(); + + if (trimmedCell.startsWith(beginSection) && trimmedCell.endsWith(endSection)) + { + return trimmedCell.substring(1, trimmedCell.length() - 1); + } else + { + return null; + } + } +} diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/parser/SampleUploadSectionsParser.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/parser/SampleUploadSectionsParser.java index e343d31ed4b..a7ca423d9e4 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/parser/SampleUploadSectionsParser.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/parser/SampleUploadSectionsParser.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import ch.systemsx.cisd.common.io.DelegatedReader; +import ch.systemsx.cisd.common.parser.ExcelFileLoader; import ch.systemsx.cisd.common.parser.IParserObjectFactory; import ch.systemsx.cisd.common.parser.IParserObjectFactoryFactory; import ch.systemsx.cisd.common.parser.IPropertyMapper; @@ -153,6 +154,34 @@ public class SampleUploadSectionsParser return tabFileLoader; } + private static BisExcelFileLoader<NewSample> createSampleLoaderFromExcel( + final SampleType sampleType, final boolean isAutoGenerateCodes, + final boolean allowExperiments, final BatchOperationKind operationKind) + { + final BisExcelFileLoader<NewSample> tabFileLoader = + new BisExcelFileLoader<NewSample>(new IParserObjectFactoryFactory<NewSample>() + { + public final IParserObjectFactory<NewSample> createFactory( + final IPropertyMapper propertyMapper) throws ParserException + { + switch (operationKind) + { + case REGISTRATION: + return new NewSampleParserObjectFactory(sampleType, + propertyMapper, isAutoGenerateCodes == false, + allowExperiments); + case UPDATE: + return new UpdatedSampleParserObjectFactory(sampleType, + propertyMapper, isAutoGenerateCodes == false, + allowExperiments); + } + throw new UnsupportedOperationException(operationKind + + " is not supported"); + } + }, true); + return tabFileLoader; + } + private static List<BatchRegistrationResult> loadSamplesFromFiles( Collection<NamedInputStream> uploadedFiles, SampleType sampleType, boolean isAutoGenerateCodes, final List<NewSamplesWithTypes> newSamples, @@ -162,50 +191,98 @@ public class SampleUploadSectionsParser new ArrayList<BatchRegistrationResult>(uploadedFiles.size()); for (final NamedInputStream multipartFile : uploadedFiles) { - List<FileSection> sampleSections = new ArrayList<FileSection>(); - if (sampleType.isDefinedInFileEntityTypeCode()) + if (multipartFile.getOriginalFilename().toLowerCase().endsWith("xls")) { - sampleSections - .addAll(FileSection.extractSections(multipartFile.getUnicodeReader())); + List<ExcelFileSection> sampleSections = new ArrayList<ExcelFileSection>(); + if (sampleType.isDefinedInFileEntityTypeCode()) + { + sampleSections.addAll(ExcelFileSection.extractSections(multipartFile + .getInputStream())); + } else + { + sampleSections.add(ExcelFileSection.createFromInputStream( + multipartFile.getInputStream(), sampleType.getCode())); + } + int sampleCounter = 0; + Map<String, String> defaults = Collections.emptyMap(); + for (ExcelFileSection fs : sampleSections) + { + if (fs.getSectionName().equals("DEFAULT")) + { + defaults = + Collections.unmodifiableMap(ExcelFileLoader.parseDefaults( + fs.getSheet(), fs.getBegin(), fs.getEnd())); + } else + { + SampleType typeFromSection = new SampleType(); + typeFromSection.setCode(fs.getSectionName()); + final BisExcelFileLoader<NewSample> excelFileLoader = + createSampleLoaderFromExcel(typeFromSection, isAutoGenerateCodes, + allowExperiments, operationKind); + String sectionInFile = + sampleSections.size() == 1 ? "" : " (section:" + + fs.getSectionName() + ")"; + final List<NewSample> loadedSamples = + excelFileLoader.load(fs.getSheet(), fs.getBegin(), fs.getEnd(), + multipartFile.getOriginalFilename() + sectionInFile, + defaults); + if (loadedSamples.size() > 0) + { + newSamples.add(new NewSamplesWithTypes(typeFromSection, loadedSamples)); + sampleCounter += loadedSamples.size(); + } + } + } + results.add(new BatchRegistrationResult(multipartFile.getOriginalFilename(), String + .format("%s of %d sample(s) is complete.", operationKind.getDescription(), + sampleCounter))); } else { - sampleSections.add(FileSection.createFromInputStream( - multipartFile.getInputStream(), sampleType.getCode())); - } - int sampleCounter = 0; - Map<String, String> defaults = Collections.emptyMap(); - for (FileSection fs : sampleSections) - { - if (fs.getSectionName().equals("DEFAULT")) + List<FileSection> sampleSections = new ArrayList<FileSection>(); + if (sampleType.isDefinedInFileEntityTypeCode()) { - defaults = - Collections.unmodifiableMap(TabFileLoader.parseDefaults(fs - .getContentReader())); + sampleSections.addAll(FileSection.extractSections(multipartFile + .getUnicodeReader())); } else { - Reader reader = fs.getContentReader(); - SampleType typeFromSection = new SampleType(); - typeFromSection.setCode(fs.getSectionName()); - final BisTabFileLoader<NewSample> tabFileLoader = - createSampleLoader(typeFromSection, isAutoGenerateCodes, - allowExperiments, operationKind); - String sectionInFile = - sampleSections.size() == 1 ? "" : " (section:" + fs.getSectionName() - + ")"; - final List<NewSample> loadedSamples = - tabFileLoader.load( - new DelegatedReader(reader, multipartFile.getOriginalFilename() - + sectionInFile), defaults); - if (loadedSamples.size() > 0) + sampleSections.add(FileSection.createFromInputStream( + multipartFile.getInputStream(), sampleType.getCode())); + } + int sampleCounter = 0; + Map<String, String> defaults = Collections.emptyMap(); + for (FileSection fs : sampleSections) + { + if (fs.getSectionName().equals("DEFAULT")) + { + defaults = + Collections.unmodifiableMap(TabFileLoader.parseDefaults(fs + .getContentReader())); + } else { - newSamples.add(new NewSamplesWithTypes(typeFromSection, loadedSamples)); - sampleCounter += loadedSamples.size(); + Reader reader = fs.getContentReader(); + SampleType typeFromSection = new SampleType(); + typeFromSection.setCode(fs.getSectionName()); + final BisTabFileLoader<NewSample> tabFileLoader = + createSampleLoader(typeFromSection, isAutoGenerateCodes, + allowExperiments, operationKind); + String sectionInFile = + sampleSections.size() == 1 ? "" : " (section:" + + fs.getSectionName() + ")"; + final List<NewSample> loadedSamples = + tabFileLoader.load( + new DelegatedReader(reader, multipartFile + .getOriginalFilename() + sectionInFile), defaults); + if (loadedSamples.size() > 0) + { + newSamples.add(new NewSamplesWithTypes(typeFromSection, loadedSamples)); + sampleCounter += loadedSamples.size(); + } } } + results.add(new BatchRegistrationResult(multipartFile.getOriginalFilename(), String + .format("%s of %d sample(s) is complete.", operationKind.getDescription(), + sampleCounter))); } - results.add(new BatchRegistrationResult(multipartFile.getOriginalFilename(), String - .format("%s of %d sample(s) is complete.", operationKind.getDescription(), - sampleCounter))); } return results; } @@ -249,5 +326,4 @@ public class SampleUploadSectionsParser } } } - } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/parser/MaterialUploadSectionsParser.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/parser/MaterialUploadSectionsParser.java index 8c1fdd629d9..678bdbd2152 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/parser/MaterialUploadSectionsParser.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/parser/MaterialUploadSectionsParser.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import ch.systemsx.cisd.common.io.DelegatedReader; +import ch.systemsx.cisd.common.parser.ExcelFileLoader; import ch.systemsx.cisd.common.parser.IParserObjectFactory; import ch.systemsx.cisd.common.parser.IParserObjectFactoryFactory; import ch.systemsx.cisd.common.parser.IPropertyMapper; @@ -33,7 +34,9 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.BatchRegistrationResult import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialType; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewMaterial; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewMaterialsWithTypes; +import ch.systemsx.cisd.openbis.generic.shared.parser.BisExcelFileLoader; import ch.systemsx.cisd.openbis.generic.shared.parser.BisTabFileLoader; +import ch.systemsx.cisd.openbis.generic.shared.parser.ExcelFileSection; import ch.systemsx.cisd.openbis.generic.shared.parser.FileSection; import ch.systemsx.cisd.openbis.generic.shared.parser.NamedInputStream; @@ -105,59 +108,117 @@ public class MaterialUploadSectionsParser new ArrayList<BatchRegistrationResult>(uploadedFiles.size()); for (final NamedInputStream multipartFile : uploadedFiles) { - List<FileSection> materialSections = new ArrayList<FileSection>(); - if (materialType.isDefinedInFileEntityTypeCode()) + if (multipartFile.getOriginalFilename().toLowerCase().endsWith("xls")) { - materialSections.addAll(FileSection.extractSections(multipartFile - .getUnicodeReader())); + List<ExcelFileSection> materialSections = new ArrayList<ExcelFileSection>(); + if (materialType.isDefinedInFileEntityTypeCode()) + { + materialSections.addAll(ExcelFileSection.extractSections(multipartFile + .getInputStream())); + } else + { + materialSections.add(ExcelFileSection.createFromInputStream( + multipartFile.getInputStream(), materialType.getCode())); + } + int materialCounter = 0; + Map<String, String> defaults = Collections.emptyMap(); + for (ExcelFileSection fs : materialSections) + { + if (fs.getSectionName().equals("DEFAULT")) + { + defaults = + Collections.unmodifiableMap(ExcelFileLoader.parseDefaults( + fs.getSheet(), fs.getBegin(), fs.getEnd())); + } else + { + MaterialType typeFromSection = new MaterialType(); + typeFromSection.setCode(fs.getSectionName()); + final BisExcelFileLoader<NewMaterial> excelFileLoader = + new BisExcelFileLoader<NewMaterial>( + new IParserObjectFactoryFactory<NewMaterial>() + { + public final IParserObjectFactory<NewMaterial> createFactory( + final IPropertyMapper propertyMapper) + throws ParserException + { + return new NewMaterialParserObjectFactory( + propertyMapper); + } + }, false); + String sectionInFile = + materialSections.size() == 1 ? "" : " (section:" + + fs.getSectionName() + ")"; + final List<NewMaterial> loadedMaterials = + excelFileLoader.load(fs.getSheet(), fs.getBegin(), fs.getEnd(), + multipartFile.getOriginalFilename() + sectionInFile, + defaults); + if (loadedMaterials.size() > 0) + { + newMaterials.add(new NewMaterialsWithTypes(typeFromSection, + loadedMaterials)); + materialCounter += loadedMaterials.size(); + } + } + } + results.add(new BatchRegistrationResult(multipartFile.getOriginalFilename(), String + .format("Registration of %d material(s) is complete.", materialCounter))); } else { - materialSections.add(FileSection.createFromInputStream( - multipartFile.getInputStream(), materialType.getCode())); - } - int materialCounter = 0; - Map<String, String> defaults = Collections.emptyMap(); - for (FileSection fs : materialSections) - { - if (fs.getSectionName().equals("DEFAULT")) + + List<FileSection> materialSections = new ArrayList<FileSection>(); + if (materialType.isDefinedInFileEntityTypeCode()) { - defaults = - Collections.unmodifiableMap(TabFileLoader.parseDefaults(fs - .getContentReader())); + materialSections.addAll(FileSection.extractSections(multipartFile + .getUnicodeReader())); } else { - Reader reader = fs.getContentReader(); - MaterialType typeFromSection = new MaterialType(); - typeFromSection.setCode(fs.getSectionName()); - BisTabFileLoader<NewMaterial> tabFileLoader = - new BisTabFileLoader<NewMaterial>( - new IParserObjectFactoryFactory<NewMaterial>() - { - public final IParserObjectFactory<NewMaterial> createFactory( - final IPropertyMapper propertyMapper) - throws ParserException - { - return new NewMaterialParserObjectFactory( - propertyMapper); - } - }, false); - String sectionInFile = - materialSections.size() == 1 ? "" : " (section:" + fs.getSectionName() - + ")"; - final List<NewMaterial> loadedMaterials = - tabFileLoader.load( - new DelegatedReader(reader, multipartFile.getOriginalFilename() - + sectionInFile), defaults); - if (loadedMaterials.size() > 0) + materialSections.add(FileSection.createFromInputStream( + multipartFile.getInputStream(), materialType.getCode())); + } + int materialCounter = 0; + Map<String, String> defaults = Collections.emptyMap(); + for (FileSection fs : materialSections) + { + if (fs.getSectionName().equals("DEFAULT")) { - newMaterials - .add(new NewMaterialsWithTypes(typeFromSection, loadedMaterials)); - materialCounter += loadedMaterials.size(); + defaults = + Collections.unmodifiableMap(TabFileLoader.parseDefaults(fs + .getContentReader())); + } else + { + Reader reader = fs.getContentReader(); + MaterialType typeFromSection = new MaterialType(); + typeFromSection.setCode(fs.getSectionName()); + BisTabFileLoader<NewMaterial> tabFileLoader = + new BisTabFileLoader<NewMaterial>( + new IParserObjectFactoryFactory<NewMaterial>() + { + public final IParserObjectFactory<NewMaterial> createFactory( + final IPropertyMapper propertyMapper) + throws ParserException + { + return new NewMaterialParserObjectFactory( + propertyMapper); + } + }, false); + String sectionInFile = + materialSections.size() == 1 ? "" : " (section:" + + fs.getSectionName() + ")"; + final List<NewMaterial> loadedMaterials = + tabFileLoader.load( + new DelegatedReader(reader, multipartFile + .getOriginalFilename() + sectionInFile), defaults); + if (loadedMaterials.size() > 0) + { + newMaterials.add(new NewMaterialsWithTypes(typeFromSection, + loadedMaterials)); + materialCounter += loadedMaterials.size(); + } } } + results.add(new BatchRegistrationResult(multipartFile.getOriginalFilename(), String + .format("Registration of %d material(s) is complete.", materialCounter))); } - results.add(new BatchRegistrationResult(multipartFile.getOriginalFilename(), String - .format("Registration of %d material(s) is complete.", materialCounter))); } return results; } -- GitLab