diff --git a/spring-batch-excel/pom.xml b/spring-batch-excel/pom.xml
new file mode 100644
index 0000000..4721a62
--- /dev/null
+++ b/spring-batch-excel/pom.xml
@@ -0,0 +1,77 @@
+
+
+ 4.0.0
+
+ org.springframewor.batch
+ spring-batch-excel
+ 1.3.0.BUILD-SNAPSHOT
+
+
+ 2.2.5.RELEASE
+ 2.6.12
+ 3.10-FINAL
+
+
+
+
+ org.springframework.batch
+ spring-batch-core
+ ${spring.batch.version}
+
+
+ org.springframework.batch
+ spring-batch-infrastructure
+ ${spring.batch.version}
+
+
+ net.sourceforge.jexcelapi
+ jxl
+ ${jxl.version}
+ compile
+ true
+
+
+ org.apache.poi
+ poi
+ ${poi.version}
+ true
+
+
+ org.apache.poi
+ poi-ooxml
+ ${poi.version}
+ true
+
+
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+ org.mockito
+ mockito-all
+ 1.9.5
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.5.1
+
+ 1.6
+ 1.6
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/AbstractExcelItemReader.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/AbstractExcelItemReader.java
new file mode 100644
index 0000000..c329b19
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/AbstractExcelItemReader.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.batch.item.file.ResourceAwareItemReaderItemStream;
+import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.core.io.Resource;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ObjectUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * {@link org.springframework.batch.item.ItemReader} implementation which uses the JExcelApi to read an Excel
+ * file. It will read the file sheet for sheet and row for row. It is based on
+ * the {@link org.springframework.batch.item.file.FlatFileItemReader}
+ *
+ * @param the type
+ * @author Marten Deinum
+ */
+public abstract class AbstractExcelItemReader extends AbstractItemCountingItemStreamItemReader implements
+ ResourceAwareItemReaderItemStream, InitializingBean {
+
+ protected final Log logger = LogFactory.getLog(getClass());
+ private Resource resource;
+ private int linesToSkip = 0;
+ private int currentRow = 0;
+ private int currentSheet = 0;
+ private RowMapper rowMapper;
+ private RowCallbackHandler skippedRowsCallback;
+ private boolean noInput = false;
+ private boolean strict = true;
+
+ public AbstractExcelItemReader() {
+ super();
+ this.setName(ClassUtils.getShortName(this.getClass()));
+ }
+
+ @Override
+ protected T doRead() throws Exception {
+ if (this.noInput) {
+ return null;
+ }
+ final Sheet sheet = this.getSheet(this.currentSheet);
+ final String[] row = this.readRow(sheet);
+ if (ObjectUtils.isEmpty(row)) {
+ this.currentSheet++;
+ if (this.currentSheet >= this.getNumberOfSheets()) {
+ if (logger.isDebugEnabled() ) {
+ logger.debug("No more sheets in '" + this.resource.getDescription() + "'.");
+ }
+ return null;
+ } else {
+ this.currentRow = 0;
+ this.openSheet();
+ return this.doRead();
+ }
+ } else {
+ try {
+ return this.rowMapper.mapRow(sheet, row, this.currentRow);
+ } catch (final Exception e) {
+ throw new ExcelFileParseException("Exception parsing Excel file.", e, this.resource.getDescription(),
+ sheet.getName(), this.currentRow, row);
+ }
+ }
+ }
+
+ @Override
+ protected void doOpen() throws Exception {
+ Assert.notNull(this.resource, "Input resource must be set");
+ this.noInput = true;
+ if (!this.resource.exists()) {
+ if (this.strict) {
+ throw new IllegalStateException("Input resource must exist (reader is in 'strict' mode): "
+ + this.resource);
+ }
+ logger.warn("Input resource does not exist '"+this.resource.getDescription()+"'.");
+ return;
+ }
+
+ if (!this.resource.isReadable()) {
+ if (this.strict) {
+ throw new IllegalStateException("Input resource must be readable (reader is in 'strict' mode): "
+ + this.resource);
+ }
+ logger.warn("Input resource is not readable '"+this.resource.getDescription()+"'.");
+ return;
+ }
+
+ this.noInput = false;
+ this.openExcelFile(this.resource);
+ this.openSheet();
+ if (logger.isDebugEnabled()) {
+ logger.debug("Opened workbook ["+this.resource.getFilename()+"] with "+this.getNumberOfSheets()+" sheets.");
+ }
+ }
+
+ private String[] readRow(final Sheet sheet) {
+ this.currentRow++;
+ if (this.currentRow < sheet.getNumberOfRows()) {
+ return sheet.getRow(this.currentRow);
+ }
+ return null;
+ }
+
+ private void openSheet() {
+ final Sheet sheet = this.getSheet(this.currentSheet);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Opening sheet "+sheet.getName()+".");
+ }
+ for (int i = 0; i < this.linesToSkip; i++) {
+ final String[] row = this.readRow(sheet);
+ if (this.skippedRowsCallback != null) {
+ this.skippedRowsCallback.handleRow(sheet, row);
+ }
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("Openend sheet "+sheet.getName()+", with "+sheet.getNumberOfRows()+" rows.");
+ }
+
+ }
+
+ @Override
+ protected final void doClose() throws Exception {
+ doCloseWorkbook();
+ if (getResource() != null) {
+ try {
+ InputStream is = getResource().getInputStream();
+ is.close();
+ } catch (IOException ioe) {
+ logger.warn("Exception whilst obtaining or closing the inputstream.", ioe);
+ }
+ }
+ }
+
+ /**
+ * Method which can be overriden by subclasses to do cleanup additional resources.
+ *
+ * @throws Exception
+ */
+ protected void doCloseWorkbook() throws Exception {
+ }
+
+ protected Resource getResource() {
+ return this.resource;
+ }
+
+ public void setResource(final Resource resource) {
+ this.resource = resource;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Assert.notNull(this.rowMapper, "RowMapper must be set");
+ }
+
+ /**
+ * Set the number of lines to skip. This number is applied to all worksheet
+ * in the excel file! default to 0
+ *
+ * @param linesToSkip
+ */
+ public void setLinesToSkip(final int linesToSkip) {
+ this.linesToSkip = linesToSkip;
+ }
+
+ protected abstract Sheet getSheet(int sheet);
+
+ protected abstract int getNumberOfSheets();
+
+ protected abstract void openExcelFile(Resource resource) throws Exception;
+
+ /**
+ * In strict mode the reader will throw an exception on
+ * {@link #open(org.springframework.batch.item.ExecutionContext)} if the input resource does not exist.
+ *
+ * @param strict true by default
+ */
+ public void setStrict(final boolean strict) {
+ this.strict = strict;
+ }
+
+ public void setRowMapper(final RowMapper rowMapper) {
+ this.rowMapper = rowMapper;
+ }
+
+ public void setSkippedRowsCallback(final RowCallbackHandler skippedRowsCallback) {
+ this.skippedRowsCallback = skippedRowsCallback;
+ }
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/ExcelFileParseException.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/ExcelFileParseException.java
new file mode 100644
index 0000000..21283b2
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/ExcelFileParseException.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel;
+
+import org.springframework.batch.item.ParseException;
+
+/**
+ * Exception thrown when parsing excel files. The name of the sheet, the row number on that sheet and the
+ * name of the excel file can be passed in so that in exception handling we can reuse it. This class only has
+ * simply dependencies to make it is generic as possible.
+ *
+ * @author Marten Deinum
+ *
+ */
+public class ExcelFileParseException extends ParseException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3939056060545496492L;
+
+ private final String filename;
+ private final String sheet;
+ private final String[] row;
+ private final int rowNumber;
+
+ /**
+ * Construct an {@link ExcelFileParseException}.
+ *
+ * @param message the message
+ * @param cause the root cause
+ * @param filename the name of the excel file
+ * @param sheet the name of the sheet
+ * @param rowNumber the row number in the current sheet
+ * @param row the row data as text
+ */
+ public ExcelFileParseException(final String message, final Throwable cause, final String filename,
+ final String sheet, final int rowNumber, final String[] row) {
+ super(message, cause);
+ this.filename = filename;
+ this.sheet = sheet;
+ this.rowNumber = rowNumber;
+ this.row = row;
+ }
+
+ public String getFilename() {
+ return this.filename;
+ }
+
+ public String getSheet() {
+ return this.sheet;
+ }
+
+ public int getRowNumber() {
+ return this.rowNumber;
+ }
+
+ public String[] getRow() {
+ return this.row;
+ }
+
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/RowCallbackHandler.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/RowCallbackHandler.java
new file mode 100644
index 0000000..450e611
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/RowCallbackHandler.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.batch.item.excel;
+
+/**
+ * Callback to handle skipped lines. Useful for header/footer processing.
+ *
+ * @author Marten Deinum
+ */
+public interface RowCallbackHandler {
+
+ void handleRow(Sheet sheet, String[] row);
+
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/RowMapper.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/RowMapper.java
new file mode 100644
index 0000000..6001450
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/RowMapper.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel;
+
+/**
+ * Map rows from an excel sheet to an object.
+ *
+ * @author Marten Deinum
+ *
+ * @param
+ */
+public interface RowMapper {
+
+ /**
+ * Implementations must implement this method to map the provided row to
+ * the parameter type T. The row number represents the number of rows
+ * into a {@link Sheet} the current line resides.
+ *
+ * @param sheet the current sheet
+ * @param row to be mapped
+ * @param rowNum of the current row
+ * @return mapped object of type T
+ * @throws Exception if error occured while parsing.
+ */
+ T mapRow(Sheet sheet, String[] row, int rowNum) throws Exception;
+
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/Sheet.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/Sheet.java
new file mode 100644
index 0000000..248cc6f
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/Sheet.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel;
+
+/**
+ * Interface to wrap different Excel implementations like JExcel, JXL or Apache POI.
+ *
+ * @author Marten Deinum
+ *
+ */
+public interface Sheet {
+
+ /**
+ * Get the number of rows in this sheet.
+ *
+ * @return the number of rows.
+ */
+ int getNumberOfRows();
+
+ /**
+ * Get the name of the sheet.
+ *
+ * @return the name of the sheet.
+ */
+ String getName();
+
+ /**
+ * Get the row as a String[]. Returns null if the row doesn't exist.
+ *
+ * @param rowNumber the row number to read.
+ * @return a String[] or null
+ */
+ String[] getRow(int rowNumber);
+
+ /**
+ * Gets the first row of the sheet and use it as header.
+ *
+ * @return String[] of row 0
+ */
+ String[] getHeader();
+
+ /**
+ * The number of columns in this sheet.
+ *
+ * @return number of columns
+ */
+ int getNumberOfColumns();
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/jxl/JxlItemReader.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/jxl/JxlItemReader.java
new file mode 100644
index 0000000..4215160
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/jxl/JxlItemReader.java
@@ -0,0 +1,73 @@
+
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.batch.item.excel.jxl;
+
+import jxl.Workbook;
+import jxl.read.biff.WorkbookParser;
+import org.springframework.batch.item.excel.AbstractExcelItemReader;
+import org.springframework.batch.item.excel.Sheet;
+import org.springframework.core.io.Resource;
+import org.springframework.util.ClassUtils;
+
+/**
+ * {@link org.springframework.batch.item.ItemReader} implementation which uses the JExcelApi to read an Excel
+ * file. It will read the file sheet for sheet and row for row. It is based on
+ * the {@link org.springframework.batch.item.file.FlatFileItemReader}
+ *
+ * @author Marten Deinum
+ *
+ * @param the type
+ */
+public class JxlItemReader extends AbstractExcelItemReader {
+
+ private Workbook workbook;
+
+ public JxlItemReader() {
+ super();
+ this.setName(ClassUtils.getShortName(JxlItemReader.class));
+ }
+
+ @Override
+ protected void openExcelFile(final Resource resource) throws Exception {
+ this.workbook = WorkbookParser.getWorkbook(resource.getInputStream());
+ }
+
+ @Override
+ protected void doCloseWorkbook() throws Exception {
+ if (this.workbook != null) {
+ this.workbook.close();
+ }
+ }
+
+ @Override
+ protected Sheet getSheet(final int sheet) {
+ if (sheet < this.workbook.getNumberOfSheets()) {
+ return new JxlSheet(this.workbook.getSheet(sheet));
+ }
+ return null;
+ }
+
+ @Override
+ protected int getNumberOfSheets() {
+ if (this.workbook == null) {
+ throw new IllegalStateException("Workbook file not ready for reading!");
+ }
+ return this.workbook.getNumberOfSheets();
+ }
+
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/jxl/JxlSheet.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/jxl/JxlSheet.java
new file mode 100644
index 0000000..9867cbb
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/jxl/JxlSheet.java
@@ -0,0 +1,79 @@
+
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.batch.item.excel.jxl;
+
+import jxl.Cell;
+import org.springframework.batch.item.excel.Sheet;
+
+/**
+ * {@link org.springframework.batch.item.excel.Sheet} implementation for JXL.
+ *
+ * @author Marten Deinum
+ *
+ */
+public class JxlSheet implements Sheet {
+
+ private final jxl.Sheet delegate;
+
+ /**
+ * Constructor which takes the delegate sheet.
+ *
+ * @param delegate the JXL sheet
+ */
+ JxlSheet(final jxl.Sheet delegate) {
+ super();
+ this.delegate = delegate;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getNumberOfRows() {
+ return this.delegate.getRows();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String[] getHeader() {
+ return this.getRow(0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String[] getRow(final int rowNumber) {
+ final Cell[] row = this.delegate.getRow(rowNumber);
+ return JxlUtils.extractContents(row);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return this.delegate.getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getNumberOfColumns() {
+ return this.delegate.getColumns();
+ }
+
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/jxl/JxlUtils.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/jxl/JxlUtils.java
new file mode 100644
index 0000000..6f67c8f
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/jxl/JxlUtils.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.batch.item.excel.jxl;
+
+import jxl.Cell;
+import jxl.Workbook;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class containing utility methods to work with JXL.
+ *
+ * @author Marten Deinum
+ *
+ */
+public final class JxlUtils {
+
+ /** Private constructor to prevent easy instantiation. */
+ private JxlUtils() {
+ }
+
+ /**
+ * Checks if the given cell is emtpy. The cell is empty if it contains no characters, it will trim spaces.
+
+ * @param cell to check
+ * @return true/false
+ * @see org.springframework.util.StringUtils#hasText(String)
+ */
+ public static boolean isEmpty(final Cell cell) {
+ return cell == null || !StringUtils.hasText(cell.getContents());
+ }
+
+ /**
+ * Check if the given row (Cell[]) is empty. It is considered empty when the row is null, the array is empty or all
+ * the cells in the row are empty.
+ *
+ * @param row to check
+ * @return true/false
+ */
+ public static boolean isEmpty(final Cell[] row) {
+ if (row == null || row.length == 0) {
+ return true;
+ }
+ for (final Cell cell : row) {
+ if (!isEmpty(cell)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check if the given workbook has any sheets.
+ *
+ * @param workbook to check
+ * @return true/false
+ */
+ public static boolean hasSheets(final Workbook workbook) {
+ return workbook != null && workbook.getNumberOfSheets() > 0;
+ }
+
+ /**
+ * Extract the content from the given row.
+ *
+ * @param row the row
+ * @return the content as String[]
+ */
+ public static String[] extractContents(final Cell[] row) {
+ final List values = new ArrayList();
+ for (final Cell cell : row) {
+ if (!isEmpty(cell)) {
+ values.add(cell.getColumn(), cell.getContents());
+ } else {
+ values.add(cell.getColumn(), "");
+ }
+ }
+ return values.toArray(new String[values.size()]);
+ }
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/mapping/DefaultRowMapper.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/mapping/DefaultRowMapper.java
new file mode 100644
index 0000000..1155445
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/mapping/DefaultRowMapper.java
@@ -0,0 +1,62 @@
+
+/*
+ * Copyright 2006-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.batch.item.excel.mapping;
+
+import org.springframework.batch.item.excel.RowMapper;
+import org.springframework.batch.item.excel.Sheet;
+import org.springframework.batch.item.excel.transform.DefaultRowTokenizer;
+import org.springframework.batch.item.excel.transform.RowTokenizer;
+import org.springframework.batch.item.file.mapping.FieldSetMapper;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+
+/**
+ * {@link RowMapper} implementation which delegates to a {@link RowTokenizer} and a {@link org.springframework.batch.item.file.mapping.FieldSetMapper} the mapping
+ * of fields and construction of objects.
+ *
+ * @author Marten Deinum
+ *
+ * @param
+ */
+public class DefaultRowMapper implements RowMapper, InitializingBean {
+
+ private RowTokenizer rowTokenizer = new DefaultRowTokenizer();
+ private FieldSetMapper fieldSetMapper;
+
+ public T mapRow(final Sheet sheet, final String[] row, final int rowNum) throws Exception {
+ return this.fieldSetMapper.mapFieldSet(this.rowTokenizer.tokenize(sheet, row));
+ }
+
+ public void setFieldSetMapper(final FieldSetMapper fieldSetMapper) {
+ this.fieldSetMapper = fieldSetMapper;
+ }
+
+ /**
+ * Set the {@link RowTokenizer} to use to create a {@link org.springframework.batch.item.file.transform.FieldSet}. Default uses the {@link DefaultRowTokenizer}.
+ *
+ * @param rowTokenizer to use
+ */
+ public void setRowTokenizer(final RowTokenizer rowTokenizer) {
+ this.rowTokenizer = rowTokenizer;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Assert.notNull(this.rowTokenizer, "The RowTokenizer must be set");
+ Assert.notNull(this.fieldSetMapper, "The FieldSetMapper must be set");
+ }
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/mapping/PassThroughRowMapper.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/mapping/PassThroughRowMapper.java
new file mode 100644
index 0000000..ef1cc36
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/mapping/PassThroughRowMapper.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel.mapping;
+
+import org.springframework.batch.item.excel.RowMapper;
+import org.springframework.batch.item.excel.Sheet;
+
+/**
+ * Pass through {@link RowMapper} useful for passing the orginal String[]
+ * back directly rather than a mapped object.
+ *
+ * @author Marten Deinum
+ *
+ */
+public class PassThroughRowMapper implements RowMapper {
+
+ public String[] mapRow(final Sheet sheet, final String[] row, final int rowNum) throws Exception {
+ return row;
+ }
+
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/poi/PoiItemReader.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/poi/PoiItemReader.java
new file mode 100644
index 0000000..90c4eec
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/poi/PoiItemReader.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2006-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.batch.item.excel.poi;
+
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.springframework.batch.item.excel.AbstractExcelItemReader;
+import org.springframework.batch.item.excel.Sheet;
+import org.springframework.core.io.Resource;
+
+/**
+ * {@link org.springframework.batch.item.ItemReader} implementation which uses apache POI to read an Excel
+ * file. It will read the file sheet for sheet and row for row. It is based on
+ * the {@link org.springframework.batch.item.file.FlatFileItemReader}
+ *
+ * @author Marten Deinum
+ *
+ * @param the type
+ */
+public class PoiItemReader extends AbstractExcelItemReader {
+
+ private Workbook workbook;
+
+ @Override
+ protected Sheet getSheet(final int sheet) {
+ return new PoiSheet(this.workbook.getSheetAt(sheet));
+ }
+
+ @Override
+ protected int getNumberOfSheets() {
+ return this.workbook.getNumberOfSheets();
+ }
+
+ @Override
+ protected void openExcelFile(final Resource resource) throws Exception {
+ this.workbook = WorkbookFactory.create(resource.getInputStream());
+ }
+
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/poi/PoiSheet.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/poi/PoiSheet.java
new file mode 100644
index 0000000..ea38de3
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/poi/PoiSheet.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.batch.item.excel.poi;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.springframework.batch.item.excel.Sheet;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Sheet implementation for Apache POI.
+ *
+ * @author Marten Deinum
+ *
+ */
+public class PoiSheet implements Sheet {
+
+ private final org.apache.poi.ss.usermodel.Sheet delegate;
+
+ /**
+ * Constructor which takes the delegate sheet.
+ *
+ * @param delegate the apache POI sheet
+ */
+ PoiSheet(final org.apache.poi.ss.usermodel.Sheet delegate) {
+ super();
+ this.delegate = delegate;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getNumberOfRows() {
+ return this.delegate.getLastRowNum() + 1;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return this.delegate.getSheetName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String[] getRow(final int rowNumber) {
+ if (rowNumber > this.delegate.getLastRowNum()) {
+ return null;
+ }
+ final Row row = this.delegate.getRow(rowNumber);
+ final List cells = new LinkedList();
+
+ final Iterator cellIter = row.iterator();
+ while (cellIter.hasNext()) {
+ final Cell cell = cellIter.next();
+ switch (cell.getCellType()) {
+ case Cell.CELL_TYPE_NUMERIC:
+ cells.add(String.valueOf(cell.getNumericCellValue()));
+ break;
+ case Cell.CELL_TYPE_BOOLEAN:
+ cells.add(String.valueOf(cell.getBooleanCellValue()));
+ break;
+ case Cell.CELL_TYPE_STRING:
+ case Cell.CELL_TYPE_BLANK:
+ cells.add(cell.getStringCellValue());
+ break;
+ default:
+ throw new IllegalArgumentException("Cannot handle cells of type " + cell.getCellType());
+ }
+ }
+ return cells.toArray(new String[cells.size()]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String[] getHeader() {
+ return this.getRow(0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getNumberOfColumns() {
+ final String[] columns = this.getHeader();
+ if (columns != null) {
+ return columns.length;
+ }
+ return 0;
+ }
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/ColumnToAttributeConverter.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/ColumnToAttributeConverter.java
new file mode 100644
index 0000000..1779c7b
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/ColumnToAttributeConverter.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */package org.springframework.batch.item.excel.transform;
+
+/**
+ * Convert a column name to an attribute name and vice versa.
+ *
+ * @author Marten Deinum
+ */
+public interface ColumnToAttributeConverter {
+
+ /**
+ * Convert a column name to an attribute name.
+ *
+ * @param column to convert
+ * @return the attribute name
+ */
+ String toAttribute(String column);
+
+ /**
+ * Convert an attribute name to a column name.
+ *
+ * @param attribute to convert
+ * @return the column name
+ */
+ String toColumn(String attribute);
+
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/DefaultRowTokenizer.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/DefaultRowTokenizer.java
new file mode 100644
index 0000000..56463b1
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/DefaultRowTokenizer.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel.transform;
+
+import org.springframework.batch.item.excel.Sheet;
+import org.springframework.batch.item.file.transform.DefaultFieldSetFactory;
+import org.springframework.batch.item.file.transform.FieldSet;
+import org.springframework.batch.item.file.transform.FieldSetFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link RowTokenizer} which assumes the column names are on the first row in the sheet.
+ *
+ * @author Marten Deinum
+ */
+public class DefaultRowTokenizer implements RowTokenizer, InitializingBean {
+
+ private FieldSetFactory fieldSetFactory = new DefaultFieldSetFactory();
+
+ private ColumnToAttributeConverter converter = new PassThroughColumnToAttributeConverter();
+
+ private boolean useColumnHeader = true;
+
+ private boolean includeSheetName = false;
+ private String attributeForSheetName = null;
+
+ public FieldSet tokenize(final Sheet sheet, final String[] row) {
+ String[] values = new String[sheet.getNumberOfColumns()];
+ System.arraycopy(row, 0, values, 0, row.length);
+
+ if (this.includeSheetName) {
+ values = StringUtils.addStringToArray(values, sheet.getName());
+ }
+
+ if (this.useColumnHeader) {
+ String[] names = sheet.getHeader();
+ if (this.includeSheetName) {
+ names = StringUtils.addStringToArray(names, this.attributeForSheetName);
+ }
+ for (int i = 0; i < names.length; i++) {
+ names[i] = this.converter.toAttribute(names[i]);
+ }
+
+ return this.fieldSetFactory.create(values, names);
+ } else {
+ return this.fieldSetFactory.create(values);
+ }
+
+ }
+
+ /**
+ * Set the {@link org.springframework.batch.item.file.transform.FieldSetFactory} to use. The {@link org.springframework.batch.item.file.transform.DefaultFieldSetFactory} is used by default.
+ *
+ * @param fieldSetFactory to set
+ */
+ public void setFieldSetFactory(final FieldSetFactory fieldSetFactory) {
+ this.fieldSetFactory = fieldSetFactory;
+ }
+
+ /**
+ * Indication to use the column header, the default is true.
+ * @param useColumnHeader
+ */
+ public void setUseColumnHeader(final boolean useColumnHeader) {
+ this.useColumnHeader = useColumnHeader;
+ }
+
+ /**
+ * Set the {@link ColumnToAttributeConverter}.
+ *
+ * @param converter to set
+ */
+ public void setConverter(final ColumnToAttributeConverter converter) {
+ this.converter = converter;
+ }
+
+ public void setIncludeSheetName(final boolean includeSheetName) {
+ this.includeSheetName = includeSheetName;
+ }
+
+ public void setAttributeForSheetName(final String attributeForSheetName) {
+ this.attributeForSheetName = attributeForSheetName;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ if (this.includeSheetName && this.useColumnHeader) {
+ Assert.hasText(this.attributeForSheetName,
+ "When using column header as attributes and including the sheetname an attribute name for the sheetname is required.");
+ }
+ }
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/MappingColumnToAttributeConverter.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/MappingColumnToAttributeConverter.java
new file mode 100644
index 0000000..649454a
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/MappingColumnToAttributeConverter.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel.transform;
+
+import org.springframework.util.ObjectUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * {@link ColumnToAttributeConverter} which maps the names to columns and vice versa based on the provide mapping
+ * configuration. If a mapping cannot be found it returns the name as is.
+ *
+ * @author Marten Deinum
+ */
+public class MappingColumnToAttributeConverter implements ColumnToAttributeConverter {
+
+ private final Map mapping = new HashMap();
+
+ public String toAttribute(final String column) {
+ if (this.mapping.containsKey(column)) {
+ return this.mapping.get(column);
+ }
+ return column;
+ }
+
+ public String toColumn(final String attribute) {
+ if (this.mapping.containsValue(attribute)) {
+ for (Map.Entry entry : this.mapping.entrySet()) {
+ if (ObjectUtils.nullSafeEquals(attribute, entry.getValue())) {
+ return entry.getKey();
+ }
+ }
+ }
+ return attribute;
+ }
+
+ public void setMappings(final Map mappings) {
+ this.mapping.clear();
+ this.mapping.putAll(mappings);
+ }
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/PassThroughColumnToAttributeConverter.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/PassThroughColumnToAttributeConverter.java
new file mode 100644
index 0000000..398e93a
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/PassThroughColumnToAttributeConverter.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel.transform;
+
+/**
+ * {@link ColumnToAttributeConverter} which simply returns the given value.
+ *
+ * @author Marten Deinum
+ */
+public class PassThroughColumnToAttributeConverter implements ColumnToAttributeConverter {
+
+ public String toAttribute(final String column) {
+ return column;
+ }
+
+ public String toColumn(final String attribute) {
+ return attribute;
+ }
+
+}
diff --git a/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/RowTokenizer.java b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/RowTokenizer.java
new file mode 100644
index 0000000..8603989
--- /dev/null
+++ b/spring-batch-excel/src/main/java/org/springframework/batch/item/excel/transform/RowTokenizer.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel.transform;
+
+import org.springframework.batch.item.excel.Sheet;
+import org.springframework.batch.item.file.transform.FieldSet;
+
+/**
+ * Interface that is used by framework to convert a Cell[] into a {@link org.springframework.batch.item.file.transform.FieldSet}.
+ *
+ * @author Marten Deinum
+ */
+
+public interface RowTokenizer {
+
+ FieldSet tokenize(Sheet sheet, String[] row);
+}
diff --git a/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/AbstractExcelItemReaderTests.java b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/AbstractExcelItemReaderTests.java
new file mode 100644
index 0000000..95ae28c
--- /dev/null
+++ b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/AbstractExcelItemReaderTests.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.batch.item.ExecutionContext;
+import org.springframework.batch.item.excel.mapping.PassThroughRowMapper;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.util.StringUtils;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Base class for testing Excel based item readers.
+ *
+ * @author Marten Deinum
+ */
+public abstract class AbstractExcelItemReaderTests {
+
+ private final Log logger = LogFactory.getLog(this.getClass());
+
+ protected AbstractExcelItemReader itemReader;
+
+ @Before
+ public void setup() throws Exception {
+ this.itemReader = createExcelItemReader();
+ this.itemReader.setLinesToSkip(1); //First line is column names
+ this.itemReader.setResource(new ClassPathResource("org/springframework/batch/item/excel/player.xls"));
+ this.itemReader.setRowMapper(new PassThroughRowMapper());
+ this.itemReader.setSkippedRowsCallback(new RowCallbackHandler() {
+
+ public void handleRow(final Sheet sheet, final String[] row) {
+ logger.info("Skipping: " + StringUtils.arrayToCommaDelimitedString(row));
+ }
+ });
+ configureItemReader(this.itemReader);
+ this.itemReader.afterPropertiesSet();
+ this.itemReader.open(new ExecutionContext());
+ }
+
+ protected void configureItemReader(AbstractExcelItemReader itemReader) {
+ }
+
+ @After
+ public void after() throws Exception {
+ this.itemReader.close();
+ }
+
+ @Test
+ public void readExcelFile() throws Exception {
+ assertEquals(3, this.itemReader.getNumberOfSheets());
+ String[] row = null;
+ do {
+ row = (String[]) this.itemReader.read();
+ this.logger.debug("Read: "+ StringUtils.arrayToCommaDelimitedString(row));
+ } while (row != null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRequiredProperties() throws Exception {
+ final AbstractExcelItemReader reader = createExcelItemReader();
+ reader.afterPropertiesSet();
+ }
+
+ protected abstract AbstractExcelItemReader createExcelItemReader();
+
+}
diff --git a/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/jxl/JxlItemReaderTest.java b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/jxl/JxlItemReaderTest.java
new file mode 100644
index 0000000..54b8aa1
--- /dev/null
+++ b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/jxl/JxlItemReaderTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.batch.item.excel.jxl;
+
+import org.springframework.batch.item.excel.AbstractExcelItemReader;
+import org.springframework.batch.item.excel.AbstractExcelItemReaderTests;
+
+/**
+ * Test
+ */
+public class JxlItemReaderTest extends AbstractExcelItemReaderTests {
+
+ @Override
+ protected AbstractExcelItemReader createExcelItemReader() {
+ return new JxlItemReader();
+ }
+
+}
diff --git a/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/jxl/JxlUtilsTests.java b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/jxl/JxlUtilsTests.java
new file mode 100644
index 0000000..3e4f150
--- /dev/null
+++ b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/jxl/JxlUtilsTests.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel.jxl;
+
+import jxl.Cell;
+import jxl.Workbook;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ * Tests for {@link org.springframework.batch.item.excel.jxl.JxlUtils}.
+ *
+ * @author Marten Deinum
+ *
+ */
+public class JxlUtilsTests {
+
+ private final Cell cell1 = Mockito.mock(Cell.class);
+ private final Cell cell2 = Mockito.mock(Cell.class);
+ private final Cell cell3 = Mockito.mock(Cell.class);
+ private final Cell cell4 = Mockito.mock(Cell.class);
+
+ private final Workbook workbook = Mockito.mock(Workbook.class);
+
+ @Before
+ public void setup() {
+ Mockito.when(this.cell1.getContents()).thenReturn("foo");
+ Mockito.when(this.cell2.getContents()).thenReturn(" ");
+ Mockito.when(this.cell3.getContents()).thenReturn("");
+ Mockito.when(this.cell4.getContents()).thenReturn(null);
+ }
+
+ /**
+ * Test the {@link org.springframework.batch.item.excel.jxl.JxlUtils#isEmpty( jxl.Cell)} method.
+ */
+ @Test
+ public void checkIfCellsAreEmpty() {
+ Assert.assertFalse("Cell1 should not be empty", JxlUtils.isEmpty(this.cell1));
+ Assert.assertTrue("Cell2 should be empty", JxlUtils.isEmpty(this.cell2));
+ Assert.assertTrue("Cell3 should be empty", JxlUtils.isEmpty(this.cell3));
+ Assert.assertTrue("Cell4 should be empty", JxlUtils.isEmpty(this.cell4));
+ Assert.assertTrue("[null] should be empty", JxlUtils.isEmpty((Cell) null));
+ }
+
+ /**
+ * Test the {@link JxlUtils#isEmpty( jxl.Cell[])} method.
+ */
+ @Test
+ public void checkIfRowIsEmpty() {
+ Assert.assertTrue("[null] should be empty", JxlUtils.isEmpty((Cell[]) null));
+ Assert.assertTrue("[null] should be empty", JxlUtils.isEmpty(new Cell[0]));
+ Assert.assertFalse("Cell[]1 should not be empty",
+ JxlUtils.isEmpty(new Cell[]{this.cell1, this.cell2, this.cell3}));
+ Assert.assertTrue("Cell[]2 should be empty", JxlUtils.isEmpty(new Cell[]{this.cell2, this.cell3, null}));
+ }
+
+ /**
+ * Test the {@link JxlUtils#hasSheets( jxl.Workbook)} method.
+ */
+ @Test
+ public void checkIfWorkbookHasSheets() {
+ Assert.assertFalse("[null] doesn't have sheets.", JxlUtils.hasSheets(null));
+ Mockito.when(this.workbook.getNumberOfSheets()).thenReturn(5);
+ Assert.assertTrue("Workbook should have sheets.", JxlUtils.hasSheets(this.workbook));
+ Mockito.when(this.workbook.getNumberOfSheets()).thenReturn(0);
+ Assert.assertFalse("Workbook shouldn't have sheets.", JxlUtils.hasSheets(this.workbook));
+
+ }
+
+}
diff --git a/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/mapping/DefaultRowMapperTests.java b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/mapping/DefaultRowMapperTests.java
new file mode 100644
index 0000000..c86cd49
--- /dev/null
+++ b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/mapping/DefaultRowMapperTests.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel.mapping;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.batch.item.excel.Sheet;
+import org.springframework.batch.item.excel.transform.RowTokenizer;
+import org.springframework.batch.item.file.mapping.FieldSetMapper;
+import org.springframework.batch.item.file.transform.FieldSet;
+
+import static org.mockito.Matchers.any;
+
+/**
+ * Tests for {@link DefaultRowMapper}.
+ * @author Marten Deinum
+ *
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class DefaultRowMapperTests {
+
+ @Mock
+ private FieldSetMapper fieldSetMapper;
+
+ @Mock
+ private RowTokenizer rowTokenizer;
+
+ @Test(expected = IllegalArgumentException.class)
+ public void nullRowTokenizerShouldLeadToException() throws Exception {
+ final DefaultRowMapper mapper = new DefaultRowMapper();
+ mapper.setRowTokenizer(null);
+ mapper.setFieldSetMapper(this.fieldSetMapper);
+ mapper.afterPropertiesSet();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void nullFieldSetMapperShouldLeadToException() throws Exception {
+ final DefaultRowMapper mapper = new DefaultRowMapper();
+ mapper.setRowTokenizer(this.rowTokenizer);
+ mapper.setFieldSetMapper(null);
+ mapper.afterPropertiesSet();
+ }
+
+ @Test
+ public void foo() throws Exception {
+ final DefaultRowMapper mapper = new DefaultRowMapper();
+ mapper.setRowTokenizer(this.rowTokenizer);
+ mapper.setFieldSetMapper(this.fieldSetMapper);
+ final FieldSet fs = Mockito.mock(FieldSet.class);
+ final Object result = new Object();
+ Mockito.when(this.rowTokenizer.tokenize(any(Sheet.class), any(String[].class))).thenReturn(fs);
+ Mockito.when(this.fieldSetMapper.mapFieldSet(fs)).thenReturn(result);
+ Assert.assertEquals(result, mapper.mapRow(null, null, 0));
+ Mockito.verify(this.rowTokenizer, Mockito.times(1)).tokenize(any(Sheet.class), any(String[].class));
+ Mockito.verify(this.fieldSetMapper, Mockito.times(1)).mapFieldSet(fs);
+ }
+
+}
diff --git a/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/mapping/PassThroughRowMapperTests.java b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/mapping/PassThroughRowMapperTests.java
new file mode 100644
index 0000000..77fdbf6
--- /dev/null
+++ b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/mapping/PassThroughRowMapperTests.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel.mapping;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Tests for {@link PassThroughRowMapper}.
+ *
+ * @author Marten Deinum
+ *
+ */
+public class PassThroughRowMapperTests {
+
+ private final PassThroughRowMapper rowMapper = new PassThroughRowMapper();
+
+ @Test
+ public void mapRowShouldReturnSameValues() throws Exception {
+ final String[] row = new String[] { "foo", "bar", "baz" };
+
+ assertArrayEquals(row, this.rowMapper.mapRow(null, row, 0));
+ }
+
+ @Test
+ public void mapRowShouldReturnNull() throws Exception {
+ assertNull(this.rowMapper.mapRow(null, null, 0));
+ }
+
+}
diff --git a/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/poi/PoiItemReaderXlsTests.java b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/poi/PoiItemReaderXlsTests.java
new file mode 100644
index 0000000..3831721
--- /dev/null
+++ b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/poi/PoiItemReaderXlsTests.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel.poi;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.batch.item.excel.AbstractExcelItemReader;
+import org.springframework.batch.item.excel.AbstractExcelItemReaderTests;
+
+public class PoiItemReaderXlsTests extends AbstractExcelItemReaderTests {
+
+ private final Log logger = LogFactory.getLog(this.getClass());
+
+ private PoiItemReader itemReader;
+
+ @Override
+ protected AbstractExcelItemReader createExcelItemReader() {
+ return new PoiItemReader();
+ }
+
+}
diff --git a/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/poi/PoiItemReaderXlsxTests.java b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/poi/PoiItemReaderXlsxTests.java
new file mode 100644
index 0000000..a6e613a
--- /dev/null
+++ b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/poi/PoiItemReaderXlsxTests.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel.poi;
+
+import org.springframework.batch.item.excel.AbstractExcelItemReader;
+import org.springframework.batch.item.excel.AbstractExcelItemReaderTests;
+import org.springframework.core.io.ClassPathResource;
+
+public class PoiItemReaderXlsxTests extends AbstractExcelItemReaderTests {
+
+ @Override
+ protected void configureItemReader(AbstractExcelItemReader itemReader) {
+ itemReader.setResource(new ClassPathResource("org/springframework/batch/item/excel/player.xlsx"));
+ }
+
+ @Override
+ protected AbstractExcelItemReader createExcelItemReader() {
+ return new PoiItemReader();
+ }
+}
diff --git a/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/transform/MappingColumnToAttributeConverterTests.java b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/transform/MappingColumnToAttributeConverterTests.java
new file mode 100644
index 0000000..e7f9ba4
--- /dev/null
+++ b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/transform/MappingColumnToAttributeConverterTests.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel.transform;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class MappingColumnToAttributeConverterTests {
+
+ private final MappingColumnToAttributeConverter converter = new MappingColumnToAttributeConverter();
+
+ @Before
+ public void setup() {
+ final Map mappings = new HashMap();
+ mappings.put("foo", "bar1");
+ mappings.put("baz", "bar2");
+ mappings.put("with spaces", "noSpaces");
+ this.converter.setMappings(mappings);
+ }
+
+ @Test
+ public void convertColumnToAttribtue() {
+ assertEquals("bar1", this.converter.toAttribute("foo"));
+ assertEquals("noSpaces", this.converter.toAttribute("with spaces"));
+ assertEquals("not existing", this.converter.toAttribute("not existing"));
+ assertNull(this.converter.toAttribute(null));
+ }
+
+ @Test
+ public void convertAttributeToColumn() {
+ assertEquals("baz", this.converter.toColumn("bar2"));
+ assertEquals("with spaces", this.converter.toColumn("noSpaces"));
+ assertEquals("not existing", this.converter.toColumn("not existing"));
+ assertNull(this.converter.toAttribute(null));
+ }
+
+}
diff --git a/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/transform/PassThroughColumnToAttributeConverterTests.java b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/transform/PassThroughColumnToAttributeConverterTests.java
new file mode 100644
index 0000000..32932ca
--- /dev/null
+++ b/spring-batch-excel/src/test/java/org/springframework/batch/item/excel/transform/PassThroughColumnToAttributeConverterTests.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.item.excel.transform;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class PassThroughColumnToAttributeConverterTests {
+
+ private final PassThroughColumnToAttributeConverter converter = new PassThroughColumnToAttributeConverter();
+
+ @Test
+ public void columnNameShouldRemainTheSame() {
+ final String column = "column";
+ assertEquals(column, this.converter.toAttribute(column));
+ assertNull(this.converter.toAttribute(null));
+ }
+
+ @Test
+ public void attributeNameShouldRemainTheSame() {
+ final String attribute = "attribute";
+ assertEquals(attribute, this.converter.toColumn(attribute));
+ assertNull(this.converter.toColumn(null));
+ }
+
+}
diff --git a/spring-batch-excel/src/test/resources/log4j.xml b/spring-batch-excel/src/test/resources/log4j.xml
new file mode 100644
index 0000000..8ae1229
--- /dev/null
+++ b/spring-batch-excel/src/test/resources/log4j.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-batch-excel/src/test/resources/org/springframework/batch/item/excel/games.xls b/spring-batch-excel/src/test/resources/org/springframework/batch/item/excel/games.xls
new file mode 100644
index 0000000..a61f016
Binary files /dev/null and b/spring-batch-excel/src/test/resources/org/springframework/batch/item/excel/games.xls differ
diff --git a/spring-batch-excel/src/test/resources/org/springframework/batch/item/excel/player.xls b/spring-batch-excel/src/test/resources/org/springframework/batch/item/excel/player.xls
new file mode 100644
index 0000000..6d5b08d
Binary files /dev/null and b/spring-batch-excel/src/test/resources/org/springframework/batch/item/excel/player.xls differ
diff --git a/spring-batch-excel/src/test/resources/org/springframework/batch/item/excel/player.xlsx b/spring-batch-excel/src/test/resources/org/springframework/batch/item/excel/player.xlsx
new file mode 100644
index 0000000..358688f
Binary files /dev/null and b/spring-batch-excel/src/test/resources/org/springframework/batch/item/excel/player.xlsx differ