errorHandler) {
+ return onEachRow(sheetNum, calcCallback, skipFirstRowDefault, errorHandler);
+ }
+
+ /**
+ * This is the work horse for row-level worksheet processing.
+ *
+ * 1) Read data from file.
+ * 2) Find specific worksheet.
+ * 3) Create an empty List.
+ * 4) Iterate over the worksheet, building up the list.
+ * 5) Return the list.
+ *
+ * @param - type of the object to be returned
+ * @param sheetNum - integer index into the row of the spreadsheet
+ * @param calcCallback - callback defining how to process a row of data
+ * @param skipFirstRow - override default setting of whether or not to skip the first row
+ * @param errorHandler - custom error handler
+ * @return list of T objects
+ */
+ public List onEachRow(int sheetNum, CalcRowCallback calcCallback, boolean skipFirstRow, CalcTemplateErrorHandler errorHandler) {
+ try {
+ SpreadSheet spreadsheet = SpreadSheet.createFromFile(file);
+ Sheet sheet = spreadsheet.getSheet(sheetNum);
+
+ List results = new ArrayList();
+
+ if (skipFirstRow) {
+ logger.debug("Skipping first row...");
+ for (int row=1; row < sheet.getRowCount(); row++) {
+ processRow(calcCallback, results, sheet, row, errorHandler);
+ }
+ } else {
+ logger.debug("Skipping nuthin'!");
+ for (int row=0; row < sheet.getRowCount(); row++) {
+ processRow(calcCallback, results, sheet, row, errorHandler);
+ }
+ }
+
+ return results;
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * This utility method is used to invoke the row-level callback. It also traps any
+ * runtime exceptions, and runs them through the error handler.
+ *
+ * If the callback returns null
, the row is NOT added to the list.
+ *
+ * @param - type of the object to be returned
+ * @param calcCallback - callback defining how to process a row of data
+ * @param results - list that is being built up by iteration
+ * @param sheet - worksheet that is being processed
+ * @param row - index into spreadsheet row
+ * @param errorHandler - error handler callback
+ */
+ private void processRow(CalcRowCallback calcCallback, List results, Sheet sheet, int row, CalcTemplateErrorHandler errorHandler) {
+ T rowResult = null;
+ try {
+ rowResult = calcCallback.mapRow(sheet, row);
+ } catch (RuntimeException e) {
+ rowResult = errorHandler.handleException(sheet, row, e);
+ }
+ if (rowResult != null) {
+ results.add(rowResult);
+ }
+ }
+
+ public boolean isSkipFirstRowDefault() {
+ return skipFirstRowDefault;
+ }
+
+ public void setSkipFirstRowDefault(boolean skipFirstRowDefault) {
+ this.skipFirstRowDefault = skipFirstRowDefault;
+ }
+
+}
diff --git a/src/main/java/org/springframework/batch/spreadsheet/CalcTemplateErrorHandler.java b/src/main/java/org/springframework/batch/spreadsheet/CalcTemplateErrorHandler.java
new file mode 100644
index 0000000..b6abad5
--- /dev/null
+++ b/src/main/java/org/springframework/batch/spreadsheet/CalcTemplateErrorHandler.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2002-2009 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.spreadsheet;
+
+import org.jopendocument.dom.spreadsheet.Sheet;
+
+/**
+ * This interface defines a strategy for handling exceptions thrown while iterating over
+ * an Open Office Calc spreadsheet.
+ *
+ * @since 11/2/2009
+ * @author Greg Turnquist
+ * @see CalcTemplate
+ */
+public interface CalcTemplateErrorHandler {
+
+ public T handleException(Sheet sheet, int row, RuntimeException e);
+
+}
diff --git a/src/main/java/org/springframework/batch/spreadsheet/CalcUtil.java b/src/main/java/org/springframework/batch/spreadsheet/CalcUtil.java
new file mode 100644
index 0000000..6d4d1dd
--- /dev/null
+++ b/src/main/java/org/springframework/batch/spreadsheet/CalcUtil.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2002-2009 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.spreadsheet;
+
+import org.jdom.Attribute;
+import org.jopendocument.dom.spreadsheet.MutableCell;
+import org.jopendocument.dom.spreadsheet.Sheet;
+import org.jopendocument.dom.spreadsheet.SpreadSheet;
+
+/**
+ * This class provides some convenience functions to help with processing Open Office Calc spreadsheets.
+ *
+ * @since 11/2/2009
+ * @author Greg Turnquist
+ * @see CalcTemplate
+ */
+public class CalcUtil {
+
+ /**
+ * Fetches the value of a cell found at (row, column).
+ *
+ * @param sheet - worksheet where the cell is located
+ * @param column
+ * @param row
+ * @return the value stored at the given cell coordinates
+ */
+ public static String getAttr(Sheet sheet, int column, int row) {
+ MutableCell cell = sheet.getCellAt(column, row);
+ String results = cell.getValue().toString();
+ if (results == null || results.equals("")) {
+ results = ((Attribute)cell.getElement().getAttributes().get(0)).getValue();
+ }
+ return results.equals("") ? null : results;
+ }
+
+}
diff --git a/src/main/java/org/springframework/batch/spreadsheet/DefaultCalcTemplateErrorHandler.java b/src/main/java/org/springframework/batch/spreadsheet/DefaultCalcTemplateErrorHandler.java
new file mode 100644
index 0000000..0d49acd
--- /dev/null
+++ b/src/main/java/org/springframework/batch/spreadsheet/DefaultCalcTemplateErrorHandler.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2002-2009 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.spreadsheet;
+
+import org.jopendocument.dom.spreadsheet.Sheet;
+
+/**
+ * This simple policy of error handling simply returns a null when an error is discovered.
+ *
+ * @since 11/2/2009
+ * @author Greg Turnquist
+ * @see CalcTemplate
+ */
+public class DefaultCalcTemplateErrorHandler implements CalcTemplateErrorHandler {
+
+ public T handleException(Sheet sheet, int row, RuntimeException e) {
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/springframework/batch/spreadsheet/DefaultExcelTemplateErrorHandler.java b/src/main/java/org/springframework/batch/spreadsheet/DefaultExcelTemplateErrorHandler.java
new file mode 100644
index 0000000..c5ac573
--- /dev/null
+++ b/src/main/java/org/springframework/batch/spreadsheet/DefaultExcelTemplateErrorHandler.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2002-2009 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.spreadsheet;
+
+import org.apache.poi.ss.usermodel.Row;
+
+/**
+ * This simple policy of error handling simply returns a null when an error is discovered.
+ *
+ * @since 11/2/2009
+ * @author Greg Turnquist
+ * @see ExcelTemplate
+ */
+public class DefaultExcelTemplateErrorHandler implements ExcelTemplateErrorHandler {
+
+ public T handleException(Row row, RuntimeException e) {
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/springframework/batch/spreadsheet/ExcelCallbackWithoutResult.java b/src/main/java/org/springframework/batch/spreadsheet/ExcelCallbackWithoutResult.java
new file mode 100644
index 0000000..951d4cf
--- /dev/null
+++ b/src/main/java/org/springframework/batch/spreadsheet/ExcelCallbackWithoutResult.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2002-2009 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.spreadsheet;
+
+import org.apache.poi.ss.usermodel.Row;
+
+/**
+ * @since 11/2/2009
+ * @author Greg Turnquist
+ * @see ExcelTemplate
+ */
+public abstract class ExcelCallbackWithoutResult implements ExcelRowCallback {
+
+ public final T mapRow(Row row) {
+ mapRowWithoutResult(row);
+ return null;
+ }
+
+ protected abstract void mapRowWithoutResult(Row row);
+
+}
diff --git a/src/main/java/org/springframework/batch/spreadsheet/ExcelRowCallback.java b/src/main/java/org/springframework/batch/spreadsheet/ExcelRowCallback.java
new file mode 100644
index 0000000..e7fea7c
--- /dev/null
+++ b/src/main/java/org/springframework/batch/spreadsheet/ExcelRowCallback.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2002-2009 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.spreadsheet;
+
+import org.apache.poi.ss.usermodel.Row;
+
+/**
+ * This simple interface defines a callback for processing one row of a spreadsheet.
+ *
+ * @since 11/2/2009
+ * @author Greg Turnquist
+ * @see ExcelTemplate
+ */
+public interface ExcelRowCallback {
+
+ public T mapRow(Row row);
+
+}
diff --git a/src/main/java/org/springframework/batch/spreadsheet/ExcelTemplate.java b/src/main/java/org/springframework/batch/spreadsheet/ExcelTemplate.java
new file mode 100644
index 0000000..9937c0a
--- /dev/null
+++ b/src/main/java/org/springframework/batch/spreadsheet/ExcelTemplate.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2002-2009 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.spreadsheet;
+
+import java.awt.Point;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.ss.usermodel.Row;
+
+/**
+ * This utility class provides easy access to processing Microsoft Office Excel worksheets. Code using this
+ * only needs to implement the callback interfaces.
+ *
+ * @since 11/2/2009
+ * @author Greg Turnquist
+ * @see CalcTemplate
+ */
+public class ExcelTemplate {
+
+ /**
+ * The file that this instance of ExcelTemplate processes.
+ */
+ private File file;
+
+ /**
+ * Option to skip the first row (usually due to a header being there).
+ */
+ private boolean skipFirstRowDefault;
+
+ /**
+ * Standard policy is to NOT skip the first row of a worksheet.
+ */
+ public ExcelTemplate(File file) {
+ this(file, false);
+ }
+
+ /**
+ * Set whether or not to skip the first row by default for a particular worksheet.
+ */
+ public ExcelTemplate(File file, boolean skipFirstRowDefault) {
+ this.file = file;
+ this.skipFirstRowDefault = skipFirstRowDefault;
+ }
+
+ /**
+ * Process each row of the worksheet using the default error handler.
+ *
+ * @param - type of the object to be returned
+ * @param worksheetName - name of the worksheet to process
+ * @param excelCallback - callback defining how to process a row of data
+ * @return list of T objects
+ */
+ public List onEachRow(String worksheetName, ExcelRowCallback excelCallback) {
+ return onEachRow(worksheetName, excelCallback, skipFirstRowDefault, new DefaultExcelTemplateErrorHandler());
+ }
+
+ /**
+ * Process each row of the worksheet using a customized error handler.
+ *
+ * @param - type of the object to be returned
+ * @param worksheetName - name of the worksheet to process
+ * @param excelCallback - callback defining how to process a row of data
+ * @param errorHandler
+ * @return list of T objects
+ */
+ public List onEachRow(String worksheetName, ExcelRowCallback excelCallback, ExcelTemplateErrorHandler errorHandler) {
+ return onEachRow(worksheetName, excelCallback, skipFirstRowDefault, errorHandler);
+ }
+
+ /**
+ * This is the work horse for row-level worksheet processing.
+ *
+ * 1) Read data from file.
+ * 3) Create an empty List.
+ * 4) Iterate over the specific worksheet, building up the list.
+ * 5) Return the list.
+ *
+ * @param - type of the object to be returned
+ * @param worksheetName - name of the worksheet to process
+ * @param excelCallback - callback defining how to process a row of data
+ * @param skipFirstRow
+ * @param errorHandler
+ * @return list of T objects
+ */
+ public List onEachRow(String worksheetName, ExcelRowCallback excelCallback, boolean skipFirstRow, ExcelTemplateErrorHandler errorHandler) {
+ try {
+ InputStream inp = new FileInputStream(file);
+ HSSFWorkbook wb = new HSSFWorkbook(new POIFSFileSystem(inp));
+
+ List results = new ArrayList();
+
+ if (skipFirstRow) {
+ boolean firstRow = true;
+ for (Row row : wb.getSheet(worksheetName)) {
+ if (firstRow) {
+ firstRow = false;
+ continue;
+ }
+
+ processRow(excelCallback, errorHandler, results, row);
+ }
+ } else {
+ for (Row row : wb.getSheet(worksheetName)) {
+ processRow(excelCallback, errorHandler, results, row);
+ }
+ }
+
+ return results;
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * This utility method is used to invoke the row-level callback. It also traps any
+ * runtime exceptions, and runs them through the error handler.
+ *
+ * If the callback returns null
, the row is NOT added to the list.
+ *
+ * @param - type of the object to be returned
+ * @param excelCallback - callback defining how to process a row of data
+ * @param errorHandler
+ * @param results
+ * @param row
+ */
+ private void processRow(ExcelRowCallback excelCallback,
+ ExcelTemplateErrorHandler errorHandler, List results, Row row) {
+ T rowResult = null;
+ try {
+ rowResult = excelCallback.mapRow(row);
+ } catch (RuntimeException e) {
+ rowResult = errorHandler.handleException(row, e);
+ }
+ if (rowResult != null) {
+ results.add(rowResult);
+ }
+ }
+
+ public boolean isSkipFirstRowDefault() {
+ return skipFirstRowDefault;
+ }
+
+ public void setSkipFirstRowDefault(boolean skipFirstRowDefault) {
+ this.skipFirstRowDefault = skipFirstRowDefault;
+ }
+
+ /**
+ * The following block includes a search algorithm, where you can scan a worksheet for certain expression, and harvest
+ * the cell its found in.
+ * TODO: Write some tests for this!
+ */
+ private enum Direction {
+ GOING_UP,
+ GOING_DOWN,
+ TRANSITION_DOWN,
+ TRANSITION_ACROSS;
+ }
+
+ /**
+ * This function uses a search algorithm to find a cell, and return the coordinates of
+ * that cell. It uses a 2-dimensional search algorithm, scanning (0,0), (1,0), (0,1),
+ * (0,2), (1,1), (2,0), (3,0), (2,1), (1,2), (0,3)...
+ *
+ * It is limited by a finite number of steps so it won't run forever. There is
+ * a default, and user can increase the default, but there is still a limit.
+ *
+ * TODO: Test this.
+ *
+ * @param sheet
+ * @param keyPhrase
+ * @return Point(x=column, y=row)
+ */
+ private Point searchForCell(HSSFSheet sheet, String keyPhrase) {
+ return searchForCell(sheet, keyPhrase, 1000);
+ }
+
+ /**
+ * This is the core method, and used when the user needs to override the number of steps
+ * to take in searching for the cell.
+ *
+ * TODO: Test this.
+ *
+ * @param sheet
+ * @param keyPhrase
+ * @param maxSteps
+ * @return
+ */
+ private Point searchForCell(HSSFSheet sheet, String keyPhrase, int maxSteps) {
+ int row = 0;
+ int column = 0;
+ Direction parseDirection = Direction.TRANSITION_DOWN;
+ int steps = 0;
+ while (steps < maxSteps) {
+ HSSFCell cell = sheet.getRow(row).getCell(column);
+ if (cell != null) {
+ if (cell.toString().equals(keyPhrase)) {
+ return new Point(column, row);
+ }
+ }
+ switch (parseDirection) {
+ case GOING_DOWN:
+ row++; column--;
+ steps++;
+ if (column == 0) parseDirection = Direction.TRANSITION_DOWN;
+ break;
+ case GOING_UP:
+ row--; column++;
+ steps++;
+ if (row == 0) parseDirection = Direction.TRANSITION_ACROSS;
+ break;
+ case TRANSITION_DOWN:
+ row++;
+ steps++;
+ parseDirection = Direction.GOING_UP;
+ break;
+ case TRANSITION_ACROSS:
+ column++;
+ steps++;
+ parseDirection = Direction.GOING_DOWN;
+ break;
+ }
+ }
+ throw new RuntimeException("Could not find '" + keyPhrase + "' in less than " + maxSteps + " steps.");
+ }
+
+
+}
diff --git a/src/main/java/org/springframework/batch/spreadsheet/ExcelTemplateErrorHandler.java b/src/main/java/org/springframework/batch/spreadsheet/ExcelTemplateErrorHandler.java
new file mode 100644
index 0000000..7128d76
--- /dev/null
+++ b/src/main/java/org/springframework/batch/spreadsheet/ExcelTemplateErrorHandler.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2002-2009 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.spreadsheet;
+
+import org.apache.poi.ss.usermodel.Row;
+
+/**
+ * This interface defines a strategy for handling exceptions thrown while iterating over
+ * a Microsoft Office Excel spreadsheet.
+ *
+ * @since 11/2/2009
+ * @author Greg Turnquist
+ * @see ExcelTemplate
+ */
+public interface ExcelTemplateErrorHandler {
+
+ public T handleException(Row row, RuntimeException e);
+}
diff --git a/src/site/apt/notes.apt b/src/site/apt/notes.apt
new file mode 100755
index 0000000..e69de29
diff --git a/src/site/site.xml b/src/site/site.xml
new file mode 100755
index 0000000..4aac572
--- /dev/null
+++ b/src/site/site.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/java/org/springframework/batch/spreadsheet/TestCalcTemplate.java b/src/test/java/org/springframework/batch/spreadsheet/TestCalcTemplate.java
new file mode 100644
index 0000000..cfe3ea2
--- /dev/null
+++ b/src/test/java/org/springframework/batch/spreadsheet/TestCalcTemplate.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2002-2009 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.spreadsheet;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableModel;
+
+import org.apache.log4j.Logger;
+import org.apache.poi.ss.usermodel.Row;
+import org.jopendocument.dom.spreadsheet.Sheet;
+import org.jopendocument.dom.spreadsheet.SpreadSheet;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.batch.spreadsheet.support.EmptyPhoneBookEntry;
+import org.springframework.batch.spreadsheet.support.PhoneBookEntry;
+
+
+/**
+ * @author Greg Turnquist
+ */
+public class TestCalcTemplate {
+
+ private static final Logger logger = Logger.getLogger(TestCalcTemplate.class);
+
+ private String pathname = "src" + File.separator + "test" + File.separator + "resources";
+
+ // TODO: Workaround (11/2/2009 GLT) - Replace this method with a real Calc spreadsheet.
+ @Before
+ public void copySpreadsheets() throws FileNotFoundException, IOException {
+ logger.debug("TODO: Workaround (11/2/2009 GLT) - Replace this method with a real Calc spreadsheet.");
+ logger.debug("Porting Excel spreadsheet to Calc...");
+ File input = new File(pathname + File.separator + "phonebook_with_holes.xls");
+
+ ExcelTemplate et = new ExcelTemplate(input);
+ List entries = et.onEachRow("Sheet1", new ExcelRowCallback() {
+ public PhoneBookEntry mapRow(Row row) {
+ String name = "";
+ try { name = row.getCell(0).getStringCellValue(); } catch (Exception e) {}
+
+ String address = "";
+ try { address = row.getCell(1).getStringCellValue(); } catch (Exception e) {}
+
+ String phone = "";
+ try { phone = row.getCell(2).getStringCellValue(); } catch (Exception e) {}
+
+ return new PhoneBookEntry(name, address, phone);
+ }
+ });
+
+ PhoneBookEntry header = entries.get(0);
+ String[] columns = new String[]{header.getName(), header.getAddress(), header.getPhone()};
+
+ final Object[][] data = new Object[entries.size()-1][3];
+
+ for (int i=1; i < entries.size(); i++) {
+ PhoneBookEntry entry = entries.get(i);
+ logger.debug("Adding " + entry + " to the items to be stored in this spreadsheet.");
+ data[i-1] = new Object[]{entry.getName(), entry.getAddress(), entry.getPhone()};
+ }
+
+ TableModel model = new DefaultTableModel(data, columns);
+
+ final File file = new File(pathname + File.separator + "phonebook_with_holes.ods");
+ SpreadSheet.createEmpty(model).saveAs(file);
+ logger.debug("Done porting file.");
+ }
+
+ @Test
+ public void testReadingSimpleCalcSpreadsheet() throws IOException {
+ File file = new File(pathname + File.separator + "phonebook.ods");
+ CalcTemplate ct = new CalcTemplate(file);
+
+ List results =
+ ct.onEachRow(0, new CalcRowCallback() {
+ public PhoneBookEntry mapRow(Sheet sheet, int row) {
+ return new PhoneBookEntry(
+ CalcUtil.getAttr(sheet, 0, row),
+ CalcUtil.getAttr(sheet, 1, row),
+ CalcUtil.getAttr(sheet, 2, row));
+ }
+ });
+
+ Assert.assertEquals(2, results.size());
+
+ Assert.assertEquals("Name", results.get(0).getName());
+ Assert.assertEquals("Address", results.get(0).getAddress());
+ Assert.assertEquals("Phone", results.get(0).getPhone());
+
+ Assert.assertEquals("Peter Gibbons", results.get(1).getName());
+ Assert.assertEquals("123 ABC Drive", results.get(1).getAddress());
+ Assert.assertEquals("555-821-2123", results.get(1).getPhone());
+ }
+
+ @Test
+ public void testReadingSimpleCalcSpreadsheetSkippingHeader() {
+ File file = new File(pathname + File.separator + "phonebook.ods");
+ CalcTemplate et = new CalcTemplate(file, true);
+ List results =
+ et.onEachRow(0, new CalcRowCallback() {
+ public PhoneBookEntry mapRow(Sheet sheet, int row) {
+ return new PhoneBookEntry(
+ CalcUtil.getAttr(sheet, 0, row),
+ CalcUtil.getAttr(sheet, 1, row),
+ CalcUtil.getAttr(sheet, 2, row)
+ );
+ }
+ });
+
+ Assert.assertEquals(1, results.size());
+
+ Assert.assertEquals("Peter Gibbons", results.get(0).getName());
+ Assert.assertEquals("123 ABC Drive", results.get(0).getAddress());
+ Assert.assertEquals("555-821-2123", results.get(0).getPhone());
+ }
+
+ @Test
+ public void testReadingCalcSpreadsheetWithHolesUsingDefaultErrorHandling() {
+ File file = new File(pathname + File.separator + "phonebook_with_holes.ods");
+ CalcTemplate et = new CalcTemplate(file, true);
+ List results =
+ et.onEachRow(0, new CalcRowCallback() {
+ public PhoneBookEntry mapRow(Sheet sheet, int row) {
+ PhoneBookEntry entry = new PhoneBookEntry(
+ CalcUtil.getAttr(sheet, 0, row),
+ CalcUtil.getAttr(sheet, 1, row),
+ CalcUtil.getAttr(sheet, 2, row)
+ );
+ return entry;
+ }
+ });
+
+ Assert.assertEquals(1, results.size());
+
+ Assert.assertEquals("Peter Gibbons", results.get(0).getName());
+ Assert.assertEquals("123 ABC Drive", results.get(0).getAddress());
+ Assert.assertEquals("555-821-2123", results.get(0).getPhone());
+ }
+
+ @Test
+ public void testReadingCalcSpreadsheetWithHoles2() {
+ File file = new File(pathname + File.separator + "phonebook_with_holes.ods");
+ CalcTemplate et = new CalcTemplate(file, true);
+ List results =
+ et.onEachRow(0, new CalcRowCallback() {
+ public PhoneBookEntry mapRow(Sheet sheet, int row) {
+ String name = null;
+ try { name = CalcUtil.getAttr(sheet, 0, row); } catch (Exception e) {}
+
+ String address = null;
+ try { address = CalcUtil.getAttr(sheet, 1, row); } catch (Exception e) {}
+
+ String phone = null;
+ try { phone = CalcUtil.getAttr(sheet, 2, row); } catch (Exception e) {}
+
+ return new PhoneBookEntry(name, address, phone);
+ }
+ });
+
+ Assert.assertEquals(4, results.size());
+
+ Assert.assertEquals("Peter Gibbons", results.get(0).getName());
+ Assert.assertEquals("123 ABC Drive", results.get(0).getAddress());
+ Assert.assertEquals("555-821-2123", results.get(0).getPhone());
+
+ Assert.assertEquals("Joanna", results.get(1).getName());
+ Assert.assertNull(results.get(1).getAddress());
+ Assert.assertEquals("555-915-9900", results.get(1).getPhone());
+
+ Assert.assertNull(results.get(2).getName());
+ Assert.assertEquals("Corp HQ", results.get(2).getAddress());
+ Assert.assertEquals("555-321-9502", results.get(2).getPhone());
+
+ Assert.assertEquals("Bill Lumbergh", results.get(3).getName());
+ Assert.assertEquals("his cubicle", results.get(3).getAddress());
+ Assert.assertNull(results.get(3).getPhone());
+ }
+
+ @Test
+ public void testReadingCalcSpreadsheetWithHolesUsingSpecialErrorHandling() {
+ File file = new File(pathname + File.separator + "phonebook_with_holes.ods");
+ CalcTemplate et = new CalcTemplate(file, true);
+
+ List results =
+ et.onEachRow(0,
+ new CalcRowCallback() {
+ public PhoneBookEntry mapRow(Sheet sheet, int row) {
+ return new PhoneBookEntry(
+ CalcUtil.getAttr(sheet, 0, row),
+ CalcUtil.getAttr(sheet, 1, row),
+ CalcUtil.getAttr(sheet, 2, row)
+ );
+ }
+ },
+ new CalcTemplateErrorHandler() {
+ public PhoneBookEntry handleException(Sheet sheet, int row, RuntimeException e) {
+ return new EmptyPhoneBookEntry();
+ }
+ }
+ );
+
+ Assert.assertEquals(4, results.size());
+
+ Assert.assertEquals("Peter Gibbons", results.get(0).getName());
+ Assert.assertEquals("123 ABC Drive", results.get(0).getAddress());
+ Assert.assertEquals("555-821-2123", results.get(0).getPhone());
+
+ Assert.assertEquals(EmptyPhoneBookEntry.NAME, results.get(1).getName());
+ Assert.assertEquals(EmptyPhoneBookEntry.ADDRESS, results.get(1).getAddress());
+ Assert.assertEquals(EmptyPhoneBookEntry.PHONE, results.get(1).getPhone());
+
+ Assert.assertEquals(EmptyPhoneBookEntry.NAME, results.get(2).getName());
+ Assert.assertEquals(EmptyPhoneBookEntry.ADDRESS, results.get(2).getAddress());
+ Assert.assertEquals(EmptyPhoneBookEntry.PHONE, results.get(2).getPhone());
+
+ Assert.assertEquals(EmptyPhoneBookEntry.NAME, results.get(3).getName());
+ Assert.assertEquals(EmptyPhoneBookEntry.ADDRESS, results.get(3).getAddress());
+ Assert.assertEquals(EmptyPhoneBookEntry.PHONE, results.get(3).getPhone());
+ }
+
+}
diff --git a/src/test/java/org/springframework/batch/spreadsheet/TestExcelTemplate.java b/src/test/java/org/springframework/batch/spreadsheet/TestExcelTemplate.java
new file mode 100644
index 0000000..205396f
--- /dev/null
+++ b/src/test/java/org/springframework/batch/spreadsheet/TestExcelTemplate.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2002-2009 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.spreadsheet;
+
+import java.io.File;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.apache.poi.ss.usermodel.Row;
+import org.junit.Test;
+import org.springframework.batch.spreadsheet.support.EmptyPhoneBookEntry;
+import org.springframework.batch.spreadsheet.support.PhoneBookEntry;
+
+/**
+ * @author Greg Turnquist
+ */
+public class TestExcelTemplate {
+
+ private String pathname = "src" + File.separator + "test" + File.separator + "resources";
+
+ @Test
+ public void testReadingSimpleExcelSpreadsheetWithoutHeaderHandling() {
+ File file = new File(pathname + File.separator + "phonebook.xls");
+ ExcelTemplate et = new ExcelTemplate(file);
+ List results =
+ et.onEachRow("Sheet1", new ExcelRowCallback() {
+ public PhoneBookEntry mapRow(Row row) {
+ return new PhoneBookEntry(
+ row.getCell(0).getStringCellValue(),
+ row.getCell(1).getStringCellValue(),
+ row.getCell(2).getStringCellValue()
+ );
+ }
+ });
+
+ Assert.assertEquals(2, results.size());
+
+ Assert.assertEquals("Name", results.get(0).getName());
+ Assert.assertEquals("Address", results.get(0).getAddress());
+ Assert.assertEquals("Phone", results.get(0).getPhone());
+
+ Assert.assertEquals("Peter Gibbons", results.get(1).getName());
+ Assert.assertEquals("123 ABC Drive", results.get(1).getAddress());
+ Assert.assertEquals("555-821-2123", results.get(1).getPhone());
+ }
+
+ @Test
+ public void testReadingSimpleExcelSpreadsheetSkippingHeader() {
+ File file = new File(pathname + File.separator + "phonebook.xls");
+ ExcelTemplate et = new ExcelTemplate(file, true);
+ List results =
+ et.onEachRow("Sheet1", new ExcelRowCallback() {
+ public PhoneBookEntry mapRow(Row row) {
+ return new PhoneBookEntry(
+ row.getCell(0).getStringCellValue(),
+ row.getCell(1).getStringCellValue(),
+ row.getCell(2).getStringCellValue()
+ );
+ }
+ });
+
+ Assert.assertEquals(1, results.size());
+
+ Assert.assertEquals("Peter Gibbons", results.get(0).getName());
+ Assert.assertEquals("123 ABC Drive", results.get(0).getAddress());
+ Assert.assertEquals("555-821-2123", results.get(0).getPhone());
+ }
+
+ @Test
+ public void testReadingExcelSpreadsheetWithHolesUsingDefaultErrorHandling() {
+ File file = new File(pathname + File.separator + "phonebook_with_holes.xls");
+ ExcelTemplate et = new ExcelTemplate(file, true);
+ List results =
+ et.onEachRow("Sheet1", new ExcelRowCallback() {
+ public PhoneBookEntry mapRow(Row row) {
+ return new PhoneBookEntry(
+ row.getCell(0).getStringCellValue(),
+ row.getCell(1).getStringCellValue(),
+ row.getCell(2).getStringCellValue()
+ );
+ }
+ });
+
+ Assert.assertEquals(1, results.size());
+
+ Assert.assertEquals("Peter Gibbons", results.get(0).getName());
+ Assert.assertEquals("123 ABC Drive", results.get(0).getAddress());
+ Assert.assertEquals("555-821-2123", results.get(0).getPhone());
+ }
+
+ @Test
+ public void testReadingExcelSpreadsheetWithHoles2() {
+ File file = new File(pathname + File.separator + "phonebook_with_holes.xls");
+ ExcelTemplate et = new ExcelTemplate(file, true);
+ List results =
+ et.onEachRow("Sheet1", new ExcelRowCallback() {
+ public PhoneBookEntry mapRow(Row row) {
+ String name = null;
+ try { name = row.getCell(0).getStringCellValue(); } catch (Exception e) {}
+
+ String address = null;
+ try { address = row.getCell(1).getStringCellValue(); } catch (Exception e) {}
+
+ String phone = null;
+ try { phone = row.getCell(2).getStringCellValue(); } catch (Exception e) {}
+
+ return new PhoneBookEntry(name, address, phone);
+ }
+ });
+
+ Assert.assertEquals(4, results.size());
+
+ Assert.assertEquals("Peter Gibbons", results.get(0).getName());
+ Assert.assertEquals("123 ABC Drive", results.get(0).getAddress());
+ Assert.assertEquals("555-821-2123", results.get(0).getPhone());
+
+ Assert.assertEquals("Joanna", results.get(1).getName());
+ Assert.assertNull(results.get(1).getAddress());
+ Assert.assertEquals("555-915-9900", results.get(1).getPhone());
+
+ Assert.assertNull(results.get(2).getName());
+ Assert.assertEquals("Corp HQ", results.get(2).getAddress());
+ Assert.assertEquals("555-321-9502", results.get(2).getPhone());
+
+ Assert.assertEquals("Bill Lumbergh", results.get(3).getName());
+ Assert.assertEquals("his cubicle", results.get(3).getAddress());
+ Assert.assertNull(results.get(3).getPhone());
+ }
+
+ @Test
+ public void testReadingExcelSpreadsheetWithHolesUsingSpecialErrorHandling() {
+ File file = new File(pathname + File.separator + "phonebook_with_holes.xls");
+ ExcelTemplate et = new ExcelTemplate(file, true);
+
+ List results =
+ et.onEachRow("Sheet1",
+ new ExcelRowCallback() {
+ public PhoneBookEntry mapRow(Row row) {
+ return new PhoneBookEntry(
+ row.getCell(0).getStringCellValue(),
+ row.getCell(1).getStringCellValue(),
+ row.getCell(2).getStringCellValue()
+ );
+ }
+ },
+ new ExcelTemplateErrorHandler() {
+ public PhoneBookEntry handleException(Row row, RuntimeException e) {
+ return new EmptyPhoneBookEntry();
+ }
+ }
+ );
+
+ Assert.assertEquals(4, results.size());
+
+ Assert.assertEquals("Peter Gibbons", results.get(0).getName());
+ Assert.assertEquals("123 ABC Drive", results.get(0).getAddress());
+ Assert.assertEquals("555-821-2123", results.get(0).getPhone());
+
+ Assert.assertEquals(EmptyPhoneBookEntry.NAME, results.get(1).getName());
+ Assert.assertEquals(EmptyPhoneBookEntry.ADDRESS, results.get(1).getAddress());
+ Assert.assertEquals(EmptyPhoneBookEntry.PHONE, results.get(1).getPhone());
+
+ Assert.assertEquals(EmptyPhoneBookEntry.NAME, results.get(2).getName());
+ Assert.assertEquals(EmptyPhoneBookEntry.ADDRESS, results.get(2).getAddress());
+ Assert.assertEquals(EmptyPhoneBookEntry.PHONE, results.get(2).getPhone());
+
+ Assert.assertEquals(EmptyPhoneBookEntry.NAME, results.get(3).getName());
+ Assert.assertEquals(EmptyPhoneBookEntry.ADDRESS, results.get(3).getAddress());
+ Assert.assertEquals(EmptyPhoneBookEntry.PHONE, results.get(3).getPhone());
+ }
+
+}
diff --git a/src/test/java/org/springframework/batch/spreadsheet/support/EmptyPhoneBookEntry.java b/src/test/java/org/springframework/batch/spreadsheet/support/EmptyPhoneBookEntry.java
new file mode 100644
index 0000000..f35f2c6
--- /dev/null
+++ b/src/test/java/org/springframework/batch/spreadsheet/support/EmptyPhoneBookEntry.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2002-2009 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.spreadsheet.support;
+
+import org.springframework.batch.spreadsheet.CalcTemplate;
+import org.springframework.batch.spreadsheet.ExcelTemplate;
+
+/**
+ * Simple POJO to represent an empty phone book entry.
+ *
+ * @since 11/2/2009
+ * @author Greg Turnquist
+ * @see CalcTemplate
+ * @see ExcelTemplate
+ */
+public class EmptyPhoneBookEntry extends PhoneBookEntry {
+
+ public static final String NAME = "No name";
+ public static final String ADDRESS = "No address";
+ public static final String PHONE = "No phone number";
+
+ public EmptyPhoneBookEntry() {
+ super(NAME, ADDRESS, PHONE);
+ }
+}
diff --git a/src/test/java/org/springframework/batch/spreadsheet/support/PhoneBookEntry.java b/src/test/java/org/springframework/batch/spreadsheet/support/PhoneBookEntry.java
new file mode 100644
index 0000000..848c3a9
--- /dev/null
+++ b/src/test/java/org/springframework/batch/spreadsheet/support/PhoneBookEntry.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2002-2009 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.spreadsheet.support;
+
+import org.springframework.batch.spreadsheet.CalcTemplate;
+import org.springframework.batch.spreadsheet.ExcelTemplate;
+
+/**
+ * Simple POJO to represent an empty phone book entry.
+ *
+ * @since 11/2/2009
+ * @author Greg Turnquist
+ * @see CalcTemplate
+ * @see ExcelTemplate
+ */
+public class PhoneBookEntry {
+
+ private String name;
+ private String address;
+ private String phone;
+
+ public PhoneBookEntry() {
+ }
+
+ public PhoneBookEntry(String name, String address, String phone) {
+ this.name = name;
+ this.address = address;
+ this.phone = phone;
+ }
+
+ public String toString() {
+ return "Name = " + name + " Address = " + address + " Phone = " + phone;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public String getPhone() {
+ return phone;
+ }
+
+ public void setPhone(String phone) {
+ this.phone = phone;
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/resources/phonebook.ods b/src/test/resources/phonebook.ods
new file mode 100644
index 0000000..c7bd0f2
Binary files /dev/null and b/src/test/resources/phonebook.ods differ
diff --git a/src/test/resources/phonebook.xls b/src/test/resources/phonebook.xls
new file mode 100644
index 0000000..9f1419f
Binary files /dev/null and b/src/test/resources/phonebook.xls differ
diff --git a/src/test/resources/phonebook_with_holes.ods b/src/test/resources/phonebook_with_holes.ods
new file mode 100644
index 0000000..415bab1
Binary files /dev/null and b/src/test/resources/phonebook_with_holes.ods differ
diff --git a/src/test/resources/phonebook_with_holes.xls b/src/test/resources/phonebook_with_holes.xls
new file mode 100644
index 0000000..cebf89e
Binary files /dev/null and b/src/test/resources/phonebook_with_holes.xls differ