Introduce RowSet abstraction.

This commit is contained in:
Marten Deinum
2014-09-03 14:41:17 +02:00
committed by Michael Minella
parent f486e83eb8
commit 5a4aa5d533
13 changed files with 188 additions and 59 deletions

View File

@@ -49,6 +49,7 @@ public abstract class AbstractExcelItemReader<T> extends AbstractItemCountingIte
private RowCallbackHandler skippedRowsCallback;
private boolean noInput = false;
private boolean strict = true;
private RowSet rs;
public AbstractExcelItemReader() {
super();
@@ -57,31 +58,29 @@ public abstract class AbstractExcelItemReader<T> extends AbstractItemCountingIte
@Override
protected T doRead() throws Exception {
if (this.noInput) {
if (this.noInput || this.rs == null) {
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);
}
}
if (rs.next()) {
try {
return this.rowMapper.mapRow(rs);
} catch (final Exception e) {
throw new ExcelFileParseException("Exception parsing Excel file.", e, this.resource.getDescription(),
rs.getMetaData().getSheetName(), rs.getCurrentRowIndex(), rs.getCurrentRow());
}
} else {
this.currentSheet++;
if (this.currentSheet >= this.getNumberOfSheets()) {
if (logger.isDebugEnabled() ) {
logger.debug("No more sheets in '" + this.resource.getDescription() + "'.");
}
return null;
} else {
this.openSheet();
return this.doRead();
}
}
}
@Override
@@ -124,13 +123,15 @@ public abstract class AbstractExcelItemReader<T> extends AbstractItemCountingIte
private void openSheet() {
final Sheet sheet = this.getSheet(this.currentSheet);
if (logger.isDebugEnabled()) {
this.rs = new RowSet(sheet);
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);
for (int i = 0; i < this.linesToSkip; i++) {
if (rs.next() && this.skippedRowsCallback != null) {
this.skippedRowsCallback.handleRow(rs);
}
}
if (logger.isDebugEnabled()) {

View File

@@ -24,6 +24,6 @@ package org.springframework.batch.item.excel;
*/
public interface RowCallbackHandler {
void handleRow(Sheet sheet, String[] row);
void handleRow(RowSet rs);
}

View File

@@ -30,12 +30,10 @@ public interface RowMapper<T> {
* 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
* @param rs the RowSet used for mapping.
* @return mapped object of type T
* @throws Exception if error occured while parsing.
*/
T mapRow(Sheet sheet, String[] row, int rowNum) throws Exception;
T mapRow(RowSet rs) throws Exception;
}

View File

@@ -0,0 +1,87 @@
package org.springframework.batch.item.excel;
import java.util.Properties;
/**
* @author Marten Deinum
*/
public class RowSet {
private final Sheet sheet;
private final RowSetMetaData metaData;
private int currentRowIndex = -1;
private String[] currentRow;
public RowSet(Sheet sheet) {
this.sheet=sheet;
this.metaData = new RowSetMetaData(sheet);
}
public RowSetMetaData getMetaData() {
return metaData;
}
public boolean next() {
currentRow = null;
currentRowIndex++;
if (currentRowIndex <= sheet.getNumberOfRows()) {
currentRow = sheet.getRow(currentRowIndex);
return true;
}
return false;
}
/**
* The current row index represents the number of rows
* into a {@link Sheet} the current line resides
*/
public int getCurrentRowIndex() {
return this.currentRowIndex;
}
/**
* Get the data of the current row.
*
* @return a String[] for the current data
*/
public String[] getCurrentRow() {
return this.currentRow;
}
/**
* Get the value of the given column
*
* @param idx index of the column to get, 0 based
* @return the value
* @throws java.lang.ArrayIndexOutOfBoundsException
*/
public String getColumnValue(int idx) {
return currentRow[idx];
}
/**
* Construct name-value pairs from the column names and string values. Null
* values are omitted.
*
* @return some properties representing the row set.
*
* @throws IllegalStateException if the column name meta data is not
* available.
*/
public Properties getProperties() {
final String[] names = metaData.getColumnNames();
if (names == null) {
throw new IllegalStateException("Cannot create properties without meta data");
}
Properties props = new Properties();
for (int i = 0; i < currentRow.length; i++) {
String value = currentRow[i];
if (value != null) {
props.setProperty(names[i], value);
}
}
return props;
}
}

View File

@@ -0,0 +1,31 @@
package org.springframework.batch.item.excel;
/**
* Created by in329dei on 3-9-2014.
*/
public class RowSetMetaData {
private final Sheet sheet;
RowSetMetaData(Sheet sheet) {
this.sheet = sheet;
}
public String[] getColumnNames() {
return sheet.getHeader();
}
public String getColumnName(int idx) {
String[] names = getColumnNames();
return names[idx];
}
public int getColumnCount() {
return sheet.getNumberOfColumns();
}
public String getSheetName() {
return sheet.getName();
}
}

View File

@@ -61,8 +61,12 @@ public class JxlSheet implements Sheet {
*/
@Override
public String[] getRow(final int rowNumber) {
final Cell[] row = this.delegate.getRow(rowNumber);
return JxlUtils.extractContents(row);
if (rowNumber < getNumberOfRows()) {
final Cell[] row = this.delegate.getRow(rowNumber);
return JxlUtils.extractContents(row);
} else {
return null;
}
}
/**

View File

@@ -18,6 +18,7 @@
package org.springframework.batch.item.excel.mapping;
import org.springframework.batch.item.excel.RowMapper;
import org.springframework.batch.item.excel.RowSet;
import org.springframework.batch.item.excel.Sheet;
import org.springframework.batch.item.excel.transform.DefaultRowTokenizer;
import org.springframework.batch.item.excel.transform.RowTokenizer;
@@ -40,8 +41,8 @@ public class DefaultRowMapper<T> implements RowMapper<T>, InitializingBean {
private FieldSetMapper<T> fieldSetMapper;
@Override
public T mapRow(final Sheet sheet, final String[] row, final int rowNum) throws Exception {
return this.fieldSetMapper.mapFieldSet(this.rowTokenizer.tokenize(sheet, row));
public T mapRow(RowSet rs) throws Exception {
return this.fieldSetMapper.mapFieldSet(this.rowTokenizer.tokenize(rs));
}
public void setFieldSetMapper(final FieldSetMapper<T> fieldSetMapper) {

View File

@@ -16,6 +16,7 @@
package org.springframework.batch.item.excel.mapping;
import org.springframework.batch.item.excel.RowMapper;
import org.springframework.batch.item.excel.RowSet;
import org.springframework.batch.item.excel.Sheet;
/**
@@ -29,8 +30,8 @@ import org.springframework.batch.item.excel.Sheet;
public class PassThroughRowMapper implements RowMapper<String[]> {
@Override
public String[] mapRow(final Sheet sheet, final String[] row, final int rowNum) throws Exception {
return row;
public String[] mapRow(final RowSet rs) throws Exception {
return rs.getCurrentRow();
}
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.batch.item.excel.transform;
import org.springframework.batch.item.excel.RowSet;
import org.springframework.batch.item.excel.Sheet;
import org.springframework.batch.item.file.transform.DefaultFieldSetFactory;
import org.springframework.batch.item.file.transform.FieldSet;
@@ -41,16 +42,15 @@ public class DefaultRowTokenizer implements RowTokenizer, InitializingBean {
private String attributeForSheetName = null;
@Override
public FieldSet tokenize(final Sheet sheet, final String[] row) {
String[] values = new String[sheet.getNumberOfColumns()];
System.arraycopy(row, 0, values, 0, row.length);
public FieldSet tokenize(RowSet rs) {
String[] values = rs.getCurrentRow();
if (this.includeSheetName) {
values = StringUtils.addStringToArray(values, sheet.getName());
values = StringUtils.addStringToArray(values, rs.getMetaData().getSheetName());
}
if (this.useColumnHeader) {
String[] names = sheet.getHeader();
String[] names = rs.getMetaData().getColumnNames();
if (this.includeSheetName) {
names = StringUtils.addStringToArray(names, this.attributeForSheetName);
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.batch.item.excel.transform;
import org.springframework.batch.item.excel.RowSet;
import org.springframework.batch.item.excel.Sheet;
import org.springframework.batch.item.file.transform.FieldSet;
@@ -27,5 +28,5 @@ import org.springframework.batch.item.file.transform.FieldSet;
*/
public interface RowTokenizer {
FieldSet tokenize(Sheet sheet, String[] row);
FieldSet tokenize(RowSet rs);
}

View File

@@ -49,8 +49,8 @@ public abstract class AbstractExcelItemReaderTests {
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));
public void handleRow(RowSet rs) {
logger.info("Skipping: " + StringUtils.arrayToCommaDelimitedString(rs.getCurrentRow()));
}
});
configureItemReader(this.itemReader);
@@ -70,7 +70,7 @@ public abstract class AbstractExcelItemReaderTests {
@Test
public void readExcelFile() throws Exception {
assertEquals(3, this.itemReader.getNumberOfSheets());
String[] row = null;
String[] row;
do {
row = (String[]) this.itemReader.read();
this.logger.debug("Read: " + StringUtils.arrayToCommaDelimitedString(row));
@@ -79,7 +79,7 @@ public abstract class AbstractExcelItemReaderTests {
}
} while (row != null);
int readCount = (Integer) ReflectionTestUtils.getField(this.itemReader, "currentItemCount" );
assertEquals(4320, readCount); // File contains 4321 lines, first is header 4321-1=4320 records read.
assertEquals(4321, readCount);
}
@Test(expected = IllegalArgumentException.class)

View File

@@ -21,6 +21,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.batch.item.excel.RowSet;
import org.springframework.batch.item.excel.Sheet;
import org.springframework.batch.item.excel.transform.RowTokenizer;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
@@ -65,10 +66,10 @@ public class DefaultRowMapperTests {
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.rowTokenizer.tokenize(any(RowSet.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));
Assert.assertEquals(result, mapper.mapRow(null));
Mockito.verify(this.rowTokenizer, Mockito.times(1)).tokenize(any(RowSet.class));
Mockito.verify(this.fieldSetMapper, Mockito.times(1)).mapFieldSet(fs);
}

View File

@@ -16,9 +16,14 @@
package org.springframework.batch.item.excel.mapping;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.batch.item.excel.RowSet;
import org.springframework.batch.item.excel.Sheet;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
/**
* Tests for {@link PassThroughRowMapper}.
@@ -33,13 +38,12 @@ public class PassThroughRowMapperTests {
@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));
Sheet sheet = mock(Sheet.class);
when(sheet.getRow(0)).thenReturn(row);
when(sheet.getNumberOfRows()).thenReturn(1);
RowSet rs = new RowSet(sheet);
assertTrue(rs.next());
assertArrayEquals(row, this.rowMapper.mapRow(rs));
}
}