Improve RowSet abstraction.
Extracted a ColumnNameExtractor to be able to remove some logic from the sheet. Increased tests for reading excel files including mapping to objects.
This commit is contained in:
committed by
Michael Minella
parent
5a4aa5d533
commit
f05a429c16
@@ -17,13 +17,13 @@ package org.springframework.batch.item.excel;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.item.excel.support.rowset.*;
|
||||
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;
|
||||
@@ -49,7 +49,8 @@ public abstract class AbstractExcelItemReader<T> extends AbstractItemCountingIte
|
||||
private RowCallbackHandler skippedRowsCallback;
|
||||
private boolean noInput = false;
|
||||
private boolean strict = true;
|
||||
private RowSet rs;
|
||||
private RowSetFactory rowSetFactory = new DefaultRowSetFactory();
|
||||
private RowSet rs;
|
||||
|
||||
public AbstractExcelItemReader() {
|
||||
super();
|
||||
@@ -62,25 +63,25 @@ public abstract class AbstractExcelItemReader<T> extends AbstractItemCountingIte
|
||||
return null;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
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
|
||||
@@ -92,7 +93,7 @@ public abstract class AbstractExcelItemReader<T> extends AbstractItemCountingIte
|
||||
throw new IllegalStateException("Input resource must exist (reader is in 'strict' mode): "
|
||||
+ this.resource);
|
||||
}
|
||||
logger.warn("Input resource does not exist '"+this.resource.getDescription()+"'.");
|
||||
logger.warn("Input resource does not exist '" + this.resource.getDescription() + "'.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -101,7 +102,7 @@ public abstract class AbstractExcelItemReader<T> extends AbstractItemCountingIte
|
||||
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()+"'.");
|
||||
logger.warn("Input resource is not readable '" + this.resource.getDescription() + "'.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,7 +110,7 @@ public abstract class AbstractExcelItemReader<T> extends AbstractItemCountingIte
|
||||
this.openSheet();
|
||||
this.noInput = false;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Opened workbook ["+this.resource.getFilename()+"] with "+this.getNumberOfSheets()+" sheets.");
|
||||
logger.debug("Opened workbook [" + this.resource.getFilename() + "] with " + this.getNumberOfSheets() + " sheets.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,19 +124,20 @@ public abstract class AbstractExcelItemReader<T> extends AbstractItemCountingIte
|
||||
|
||||
private void openSheet() {
|
||||
final Sheet sheet = this.getSheet(this.currentSheet);
|
||||
this.rs = new RowSet(sheet);
|
||||
this.rs =rowSetFactory.create(sheet);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Opening sheet "+sheet.getName()+".");
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Opening sheet " + sheet.getName() + ".");
|
||||
}
|
||||
|
||||
for (int i = 0; i < this.linesToSkip; i++) {
|
||||
for (int i = 0; i < this.linesToSkip; i++) {
|
||||
if (rs.next() && this.skippedRowsCallback != null) {
|
||||
this.skippedRowsCallback.handleRow(rs);
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Openend sheet "+sheet.getName()+", with "+sheet.getNumberOfRows()+" rows.");
|
||||
logger.debug("Openend sheet " + sheet.getName() + ", with " + sheet.getNumberOfRows() + " rows.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -199,6 +201,10 @@ public abstract class AbstractExcelItemReader<T> extends AbstractItemCountingIte
|
||||
this.rowMapper = rowMapper;
|
||||
}
|
||||
|
||||
public void setRowSetFactory(RowSetFactory rowSetFactory) {
|
||||
this.rowSetFactory = rowSetFactory;
|
||||
}
|
||||
|
||||
public void setSkippedRowsCallback(final RowCallbackHandler skippedRowsCallback) {
|
||||
this.skippedRowsCallback = skippedRowsCallback;
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ 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
|
||||
* 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
|
||||
* @since 0.5.0
|
||||
*/
|
||||
@@ -34,16 +34,16 @@ public class ExcelFileParseException extends ParseException {
|
||||
|
||||
/**
|
||||
* 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 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
|
||||
* @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) {
|
||||
final String sheet, final int rowNumber, final String[] row) {
|
||||
super(message, cause);
|
||||
this.filename = filename;
|
||||
this.sheet = sheet;
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
|
||||
package org.springframework.batch.item.excel;
|
||||
|
||||
import org.springframework.batch.item.excel.support.rowset.RowSet;
|
||||
|
||||
/**
|
||||
* Callback to handle skipped lines. Useful for header/footer processing.
|
||||
*
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
|
||||
@@ -15,21 +15,22 @@
|
||||
*/
|
||||
package org.springframework.batch.item.excel;
|
||||
|
||||
import org.springframework.batch.item.excel.support.rowset.RowSet;
|
||||
|
||||
/**
|
||||
* Map rows from an excel sheet to an object.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*
|
||||
* @param <T>
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public interface RowMapper<T> {
|
||||
|
||||
/**
|
||||
* Implementations must implement this method to map the provided row to
|
||||
* 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 rs the RowSet used for mapping.
|
||||
* @return mapped object of type T
|
||||
* @throws Exception if error occured while parsing.
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,46 +17,38 @@
|
||||
package org.springframework.batch.item.excel;
|
||||
|
||||
/**
|
||||
* Interface to wrap different Excel implementations like JExcel, JXL or Apache POI.
|
||||
*
|
||||
* Interface to wrap different Excel implementations like JExcel, JXL or Apache POI.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*
|
||||
*/
|
||||
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();
|
||||
|
||||
@@ -28,11 +28,10 @@ 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
|
||||
* @since 0.5.0
|
||||
*
|
||||
* @param <T> the type
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class JxlItemReader<T> extends AbstractExcelItemReader<T> {
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.springframework.batch.item.excel.Sheet;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.batch.item.excel.Sheet} implementation for JXL.
|
||||
*
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
@@ -32,7 +32,7 @@ public class JxlSheet implements Sheet {
|
||||
|
||||
/**
|
||||
* Constructor which takes the delegate sheet.
|
||||
*
|
||||
*
|
||||
* @param delegate the JXL sheet
|
||||
*/
|
||||
JxlSheet(final jxl.Sheet delegate) {
|
||||
@@ -48,25 +48,17 @@ public class JxlSheet implements Sheet {
|
||||
return this.delegate.getRows();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String[] getHeader() {
|
||||
return this.getRow(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String[] getRow(final int rowNumber) {
|
||||
if (rowNumber < getNumberOfRows()) {
|
||||
final Cell[] row = this.delegate.getRow(rowNumber);
|
||||
return JxlUtils.extractContents(row);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (rowNumber < getNumberOfRows()) {
|
||||
final Cell[] row = this.delegate.getRow(rowNumber);
|
||||
return JxlUtils.extractContents(row);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,13 +32,15 @@ import java.util.List;
|
||||
*/
|
||||
public final class JxlUtils {
|
||||
|
||||
/** Private constructor to prevent easy instantiation. */
|
||||
/**
|
||||
* 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)
|
||||
@@ -68,7 +70,7 @@ public final class JxlUtils {
|
||||
|
||||
/**
|
||||
* Check if the given workbook has any sheets.
|
||||
*
|
||||
*
|
||||
* @param workbook to check
|
||||
* @return true/false
|
||||
*/
|
||||
@@ -77,8 +79,8 @@ public final class JxlUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the content from the given row.
|
||||
*
|
||||
* Extract the content from the given row.
|
||||
*
|
||||
* @param row the row
|
||||
* @return the content as String[]
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* 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.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.item.excel.RowMapper;
|
||||
import org.springframework.batch.item.excel.support.rowset.RowSet;
|
||||
import org.springframework.batch.item.excel.support.rowset.RowSetMetaData;
|
||||
import org.springframework.beans.*;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link RowMapper} implementation that converts a row into a new instance
|
||||
* of the specified mapped target class. The mapped target class must be a
|
||||
* top-level class and it must have a default or no-arg constructor.
|
||||
*
|
||||
* <p>Column values are mapped based on matching the column name as obtained from row set
|
||||
* metadata to public setters for the corresponding properties. The names are matched either
|
||||
* directly or by transforming a name separating the parts with underscores to the same name
|
||||
* using "camel" case.
|
||||
*
|
||||
* <p>Mapping is provided for fields in the target class for many common types, e.g.:
|
||||
* String, boolean, Boolean, byte, Byte, short, Short, int, Integer, long, Long,
|
||||
* float, Float, double, Double, BigDecimal, {@code java.util.Date}, etc.
|
||||
*
|
||||
* <p>For 'null' values read from the Excel document, we will attempt to call the setter, but in the case of
|
||||
* Java primitives, this causes a TypeMismatchException. This class can be configured (using the
|
||||
* primitivesDefaultedForNullValue property) to trap this exception and use the primitives default value.
|
||||
* Be aware that if you use the values from the generated bean to update the database the primitive value
|
||||
* will have been set to the primitive's default value instead of null.
|
||||
*
|
||||
* <p>Please note that this class is designed to provide convenience rather than high performance.
|
||||
* For best performance, consider using a custom {@link RowMapper} implementation.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class BeanPropertyRowMapper<T> implements RowMapper<T>, BeanFactoryAware, InitializingBean {
|
||||
|
||||
/**
|
||||
* Logger available to subclasses
|
||||
*/
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
/**
|
||||
* The class we are mapping to
|
||||
*/
|
||||
private Class<T> type;
|
||||
|
||||
/**
|
||||
* The name of the bean we are mapping to
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Whether we're strictly validating
|
||||
*/
|
||||
private boolean checkFullyPopulated = false;
|
||||
|
||||
/**
|
||||
* Whether we're defaulting primitives when mapping a null value
|
||||
*/
|
||||
private boolean primitivesDefaultedForNullValue = false;
|
||||
|
||||
/**
|
||||
* Map of the fields we provide mapping for
|
||||
*/
|
||||
private Map<String, PropertyDescriptor> mappedFields;
|
||||
|
||||
/**
|
||||
* Set of bean properties we provide mapping for
|
||||
*/
|
||||
private Set<String> mappedProperties;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
/**
|
||||
* Create a new BeanPropertyRowMapper for bean-style configuration.
|
||||
*
|
||||
* @see #setTargetType
|
||||
* @see #setPrototypeBeanName
|
||||
* @see #setCheckFullyPopulated
|
||||
*/
|
||||
public BeanPropertyRowMapper() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The bean name (id) for an object that can be populated from the field set
|
||||
* that will be passed into {@link #mapRow(RowSet)}. Typically a
|
||||
* prototype scoped bean so that a new instance is returned for each field
|
||||
* set mapped.
|
||||
* <p/>
|
||||
* Either this property or the type property must be specified, but not
|
||||
* both.
|
||||
*
|
||||
* @param name the name of a prototype bean in the enclosing BeanFactory
|
||||
*/
|
||||
public void setPrototypeBeanName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the type of bean to create instead of using a prototype
|
||||
* bean. An object of this type will be created from its default constructor
|
||||
* for every call to {@link #mapRow(RowSet)}.<br>
|
||||
* <p/>
|
||||
* Either this property or the prototype bean name must be specified, but
|
||||
* not both.
|
||||
*
|
||||
* @param type the type to set
|
||||
*/
|
||||
public void setTargetType(Class<T> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the mapping metadata for the given class.
|
||||
*
|
||||
* @param mappedClass the mapped class.
|
||||
*/
|
||||
protected void initialize(Class<T> mappedClass) {
|
||||
this.mappedFields = new HashMap<String, PropertyDescriptor>();
|
||||
this.mappedProperties = new HashSet<String>();
|
||||
PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);
|
||||
for (PropertyDescriptor pd : pds) {
|
||||
if (pd.getWriteMethod() != null) {
|
||||
this.mappedFields.put(pd.getName().toLowerCase(), pd);
|
||||
String underscoredName = underscoreName(pd.getName());
|
||||
if (!pd.getName().toLowerCase().equals(underscoredName)) {
|
||||
this.mappedFields.put(underscoredName, pd);
|
||||
}
|
||||
this.mappedProperties.add(pd.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a name in camelCase to an underscored name in lower case.
|
||||
* Any upper case letters are converted to lower case with a preceding underscore.
|
||||
*
|
||||
* @param name the string containing original name
|
||||
* @return the converted name
|
||||
*/
|
||||
private String underscoreName(String name) {
|
||||
if (!StringUtils.hasLength(name)) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(name.substring(0, 1).toLowerCase());
|
||||
for (int i = 1; i < name.length(); i++) {
|
||||
String s = name.substring(i, i + 1);
|
||||
String slc = s.toLowerCase();
|
||||
if (!s.equals(slc)) {
|
||||
result.append("_").append(slc);
|
||||
} else {
|
||||
result.append(s);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether we're strictly validating that all bean properties have been
|
||||
* mapped from corresponding database fields.
|
||||
* <p>Default is {@code false}, accepting unpopulated properties in the
|
||||
* target bean.
|
||||
*/
|
||||
public void setCheckFullyPopulated(boolean checkFullyPopulated) {
|
||||
this.checkFullyPopulated = checkFullyPopulated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether we're strictly validating that all bean properties have been
|
||||
* mapped from corresponding database fields.
|
||||
*/
|
||||
public boolean isCheckFullyPopulated() {
|
||||
return this.checkFullyPopulated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether we're defaulting Java primitives in the case of mapping a null value
|
||||
* from corresponding database fields.
|
||||
* <p>Default is {@code false}, throwing an exception when nulls are mapped to Java primitives.
|
||||
*/
|
||||
public void setPrimitivesDefaultedForNullValue(boolean primitivesDefaultedForNullValue) {
|
||||
this.primitivesDefaultedForNullValue = primitivesDefaultedForNullValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether we're defaulting Java primitives in the case of mapping a null value
|
||||
* from corresponding database fields.
|
||||
*/
|
||||
public boolean isPrimitivesDefaultedForNullValue() {
|
||||
return primitivesDefaultedForNullValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the values for all columns in the current row.
|
||||
* <p>Utilizes public setters and result set metadata.
|
||||
*
|
||||
* @see java.sql.ResultSetMetaData
|
||||
*/
|
||||
@Override
|
||||
public T mapRow(RowSet rs) throws Exception {
|
||||
T mappedObject = getBean();
|
||||
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject);
|
||||
initBeanWrapper(bw);
|
||||
|
||||
RowSetMetaData rsmd = rs.getMetaData();
|
||||
int columnCount = rsmd.getColumnCount();
|
||||
Set<String> populatedProperties = (isCheckFullyPopulated() ? new HashSet<String>() : null);
|
||||
|
||||
for (int index = 0; index < columnCount; index++) {
|
||||
String column = rsmd.getColumnName(index);
|
||||
PropertyDescriptor pd = this.mappedFields.get(column.replaceAll(" ", "").toLowerCase());
|
||||
if (pd != null) {
|
||||
String value = rs.getColumnValue(index);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapping column '" + column + "' to property '" +
|
||||
pd.getName() + "' of type " + pd.getPropertyType());
|
||||
}
|
||||
try {
|
||||
|
||||
bw.setPropertyValue(pd.getName(), value);
|
||||
} catch (TypeMismatchException e) {
|
||||
if (value == null && primitivesDefaultedForNullValue) {
|
||||
logger.debug("Intercepted TypeMismatchException for row " + rs.getCurrentRowIndex() +
|
||||
" on sheet " + rsmd.getSheetName() + " and column '" + column + "' with value " + value +
|
||||
" when setting property '" + pd.getName() + "' of type " + pd.getPropertyType() +
|
||||
" on object: " + mappedObject);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
if (populatedProperties != null) {
|
||||
populatedProperties.add(pd.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (populatedProperties != null && !populatedProperties.equals(this.mappedProperties)) {
|
||||
throw new IllegalStateException("Given RowSet does not contain all fields " +
|
||||
"necessary to populate object of class [" + mappedObject.getClass() + "]: " + this.mappedProperties);
|
||||
}
|
||||
|
||||
return mappedObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the given BeanWrapper to be used for row mapping.
|
||||
* To be called for each row.
|
||||
* <p>The default implementation is empty. Can be overridden in subclasses.
|
||||
*
|
||||
* @param bw the BeanWrapper to initialize
|
||||
*/
|
||||
protected void initBeanWrapper(BeanWrapper bw) {
|
||||
}
|
||||
|
||||
private T getBean() {
|
||||
if (name != null) {
|
||||
return (T) beanFactory.getBean(name);
|
||||
}
|
||||
try {
|
||||
return type.newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
ReflectionUtils.handleReflectionException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
ReflectionUtils.handleReflectionException(e);
|
||||
}
|
||||
// should not happen
|
||||
throw new IllegalStateException("Internal error: could not create bean instance for mapping.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.state(name != null || type != null, "Either name or type must be provided.");
|
||||
Assert.state(name == null || type == null, "Both name and type cannot be specified together.");
|
||||
initialize((Class<T>) getBean().getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method to create a new BeanPropertyRowMapper
|
||||
* (with the mapped class specified only once).
|
||||
*
|
||||
* @param targetType the class that each row should be mapped to
|
||||
*/
|
||||
public static <T> BeanPropertyRowMapper<T> newInstance(Class<T> targetType) {
|
||||
BeanPropertyRowMapper<T> newInstance = new BeanPropertyRowMapper<T>();
|
||||
newInstance.setTargetType(targetType);
|
||||
return newInstance;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
|
||||
/*
|
||||
* 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.RowSet;
|
||||
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
|
||||
* @since 0.5.0
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class DefaultRowMapper<T> implements RowMapper<T>, InitializingBean {
|
||||
|
||||
private RowTokenizer rowTokenizer = new DefaultRowTokenizer();
|
||||
private FieldSetMapper<T> fieldSetMapper;
|
||||
|
||||
@Override
|
||||
public T mapRow(RowSet rs) throws Exception {
|
||||
return this.fieldSetMapper.mapFieldSet(this.rowTokenizer.tokenize(rs));
|
||||
}
|
||||
|
||||
public void setFieldSetMapper(final FieldSetMapper<T> 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.rowTokenizer, "The RowTokenizer must be set");
|
||||
Assert.notNull(this.fieldSetMapper, "The FieldSetMapper must be set");
|
||||
}
|
||||
}
|
||||
@@ -16,16 +16,14 @@
|
||||
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.support.rowset.RowSet;
|
||||
|
||||
/**
|
||||
* Pass through {@link RowMapper} useful for passing the orginal String[]
|
||||
* back directly rather than a mapped object.
|
||||
*
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*
|
||||
*/
|
||||
public class PassThroughRowMapper implements RowMapper<String[]> {
|
||||
|
||||
|
||||
@@ -28,10 +28,9 @@ import org.springframework.core.io.Resource;
|
||||
* 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 <T> the type
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*
|
||||
* @param <T> the type
|
||||
*/
|
||||
public class PoiItemReader<T> extends AbstractExcelItemReader<T> {
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Sheet implementation for Apache POI.
|
||||
*
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
@@ -38,7 +38,7 @@ public class PoiSheet implements Sheet {
|
||||
|
||||
/**
|
||||
* Constructor which takes the delegate sheet.
|
||||
*
|
||||
*
|
||||
* @param delegate the apache POI sheet
|
||||
*/
|
||||
PoiSheet(final org.apache.poi.ss.usermodel.Sheet delegate) {
|
||||
@@ -67,44 +67,36 @@ public class PoiSheet implements Sheet {
|
||||
*/
|
||||
@Override
|
||||
public String[] getRow(final int rowNumber) {
|
||||
if (rowNumber > this.delegate.getLastRowNum()) {
|
||||
final Row row = this.delegate.getRow(rowNumber);
|
||||
if (row == null) {
|
||||
return null;
|
||||
}
|
||||
final Row row = this.delegate.getRow(rowNumber);
|
||||
final List<String> cells = new LinkedList<String>();
|
||||
|
||||
for (int i =0; i< getNumberOfColumns(); i++) {
|
||||
for (int i = 0; i < getNumberOfColumns(); i++) {
|
||||
Cell cell = row.getCell(i);
|
||||
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;
|
||||
case Cell.CELL_TYPE_FORMULA:
|
||||
FormulaEvaluator evaluator = delegate.getWorkbook().getCreationHelper().createFormulaEvaluator();
|
||||
cells.add(evaluator.evaluate(cell).formatAsString());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot handle cells of type " + 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;
|
||||
case Cell.CELL_TYPE_FORMULA:
|
||||
FormulaEvaluator evaluator = delegate.getWorkbook().getCreationHelper().createFormulaEvaluator();
|
||||
cells.add(evaluator.evaluate(cell).formatAsString());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Cannot handle cells of type " + cell.getCellType());
|
||||
}
|
||||
}
|
||||
return cells.toArray(new String[cells.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String[] getHeader() {
|
||||
return this.getRow(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -13,20 +13,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.item.excel.transform;
|
||||
package org.springframework.batch.item.excel.support.rowset;
|
||||
|
||||
import org.springframework.batch.item.excel.RowSet;
|
||||
import org.springframework.batch.item.excel.Sheet;
|
||||
import org.springframework.batch.item.file.transform.FieldSet;
|
||||
|
||||
/**
|
||||
* Interface that is used by framework to convert a row, represented in a String[], into a
|
||||
* {@link org.springframework.batch.item.file.transform.FieldSet}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* Contract for extracting column names for a given {@Sheet sheet}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public interface RowTokenizer {
|
||||
public interface ColumnNameExtractor {
|
||||
|
||||
String[] getColumnNames(Sheet sheet);
|
||||
|
||||
FieldSet tokenize(RowSet rs);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.support.rowset;
|
||||
|
||||
import org.springframework.batch.item.excel.Sheet;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@code RowSet} interface.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*
|
||||
* @see org.springframework.batch.item.excel.support.rowset.DefaultRowSetFactory
|
||||
*/
|
||||
public class DefaultRowSet implements RowSet {
|
||||
|
||||
private final Sheet sheet;
|
||||
private final RowSetMetaData metaData;
|
||||
|
||||
private int currentRowIndex = -1;
|
||||
private String[] currentRow;
|
||||
|
||||
DefaultRowSet(Sheet sheet, RowSetMetaData metaData) {
|
||||
this.sheet = sheet;
|
||||
this.metaData = metaData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowSetMetaData getMetaData() {
|
||||
return metaData;
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
public int getCurrentRowIndex() {
|
||||
return this.currentRowIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data of the current row.
|
||||
*
|
||||
* @return a String[] for the current data
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
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.
|
||||
*/
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.support.rowset;
|
||||
|
||||
import org.springframework.batch.item.excel.Sheet;
|
||||
|
||||
/**
|
||||
* {@code RowSetFactory} implementation which constructs a {@code DefaultRowSet} instance and
|
||||
* {@code DefaultRowSetMetaData} instance. The latter will have the {@code ColumnNameExtractor} configured
|
||||
* on this factory set (default {@code RowNumberColumnNameExtractor}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class DefaultRowSetFactory implements RowSetFactory {
|
||||
|
||||
private ColumnNameExtractor columnNameExtractor = new RowNumberColumnNameExtractor();
|
||||
|
||||
@Override
|
||||
public RowSet create(Sheet sheet) {
|
||||
DefaultRowSetMetaData metaData = new DefaultRowSetMetaData(sheet);
|
||||
metaData.setColumnNameExtractor(columnNameExtractor);
|
||||
return new DefaultRowSet(sheet, metaData);
|
||||
}
|
||||
|
||||
public void setColumnNameExtractor(ColumnNameExtractor columnNameExtractor) {
|
||||
this.columnNameExtractor = columnNameExtractor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.support.rowset;
|
||||
|
||||
import org.springframework.batch.item.excel.Sheet;
|
||||
|
||||
/**
|
||||
* Default implementation for the {@code RowSetMetaData} interface.
|
||||
*
|
||||
* Requires a {@code Sheet} and {@code ColumnNameExtractor} to operate correctly.
|
||||
* Delegates the retrieval of the column names to the {@code ColumnNameExtractor}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class DefaultRowSetMetaData implements RowSetMetaData {
|
||||
|
||||
private final Sheet sheet;
|
||||
|
||||
private ColumnNameExtractor columnNameExtractor;
|
||||
|
||||
DefaultRowSetMetaData(Sheet sheet) {
|
||||
this.sheet = sheet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
return columnNameExtractor.getColumnNames(sheet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int idx) {
|
||||
String[] names = getColumnNames();
|
||||
return names[idx];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return sheet.getNumberOfColumns();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSheetName() {
|
||||
return sheet.getName();
|
||||
}
|
||||
|
||||
public void setColumnNameExtractor(ColumnNameExtractor columnNameExtractor) {
|
||||
this.columnNameExtractor = columnNameExtractor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.support.rowset;
|
||||
|
||||
import org.springframework.batch.item.excel.Sheet;
|
||||
|
||||
/**
|
||||
* {@code ColumnNameExtractor} which returns the values of a given row (default is 0) as the column
|
||||
* names.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class RowNumberColumnNameExtractor implements ColumnNameExtractor {
|
||||
|
||||
private int headerRowNumber;
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames(final Sheet sheet) {
|
||||
return sheet.getRow(headerRowNumber);
|
||||
}
|
||||
|
||||
public void setHeaderRowNumber(int headerRowNumber) {
|
||||
this.headerRowNumber = headerRowNumber;
|
||||
}
|
||||
}
|
||||
@@ -12,30 +12,29 @@
|
||||
* 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;
|
||||
*/
|
||||
package org.springframework.batch.item.excel.support.rowset;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Convert a column name to an attribute name and vice versa.
|
||||
*
|
||||
* Used by the {@code org.springframework.batch.item.excel.AbstractExcelItemReader} to abstract away
|
||||
* the complexities of the underlying Excel API implementations.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public interface ColumnToAttributeConverter {
|
||||
public interface RowSet {
|
||||
|
||||
/**
|
||||
* Convert a column name to an attribute name.
|
||||
*
|
||||
* @param column to convert
|
||||
* @return the attribute name
|
||||
*/
|
||||
String toAttribute(String column);
|
||||
RowSetMetaData getMetaData();
|
||||
|
||||
/**
|
||||
* Convert an attribute name to a column name.
|
||||
*
|
||||
* @param attribute to convert
|
||||
* @return the column name
|
||||
*/
|
||||
String toColumn(String attribute);
|
||||
boolean next();
|
||||
|
||||
int getCurrentRowIndex();
|
||||
|
||||
String[] getCurrentRow();
|
||||
|
||||
String getColumnValue(int idx);
|
||||
|
||||
Properties getProperties();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.support.rowset;
|
||||
|
||||
import org.springframework.batch.item.excel.Sheet;
|
||||
|
||||
/**
|
||||
* Contract for factories which will construct a {@code RowSet} implementation.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public interface RowSetFactory {
|
||||
|
||||
RowSet create(Sheet sheet);
|
||||
}
|
||||
@@ -13,24 +13,21 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.item.excel.transform;
|
||||
package org.springframework.batch.item.excel.support.rowset;
|
||||
|
||||
/**
|
||||
* {@link ColumnToAttributeConverter} which simply returns the given value.
|
||||
*
|
||||
* Interface representing the the metadata associated with an Excel document.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class PassThroughColumnToAttributeConverter implements ColumnToAttributeConverter {
|
||||
public interface RowSetMetaData {
|
||||
|
||||
@Override
|
||||
public String toAttribute(final String column) {
|
||||
return column;
|
||||
}
|
||||
String[] getColumnNames();
|
||||
|
||||
@Override
|
||||
public String toColumn(final String attribute) {
|
||||
return attribute;
|
||||
}
|
||||
String getColumnName(int idx);
|
||||
|
||||
int getColumnCount();
|
||||
|
||||
String getSheetName();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.support.rowset;
|
||||
|
||||
import org.springframework.batch.item.excel.Sheet;
|
||||
|
||||
/**
|
||||
* {@code ColumnNameExtractor} implementation which returns a preset String[] to use as
|
||||
* the column names. Useful for those situations in which an Excel file without a header row
|
||||
* is read
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class StaticColumnNameExtractor implements ColumnNameExtractor {
|
||||
|
||||
private final String[] columnNames;
|
||||
|
||||
public StaticColumnNameExtractor(String[] columnNames) {
|
||||
this.columnNames = columnNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames(Sheet sheet) {
|
||||
return this.columnNames;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* 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.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;
|
||||
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
|
||||
* @since 0.5.0
|
||||
*/
|
||||
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;
|
||||
|
||||
@Override
|
||||
public FieldSet tokenize(RowSet rs) {
|
||||
String[] values = rs.getCurrentRow();
|
||||
|
||||
if (this.includeSheetName) {
|
||||
values = StringUtils.addStringToArray(values, rs.getMetaData().getSheetName());
|
||||
}
|
||||
|
||||
if (this.useColumnHeader) {
|
||||
String[] names = rs.getMetaData().getColumnNames();
|
||||
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 <code>true</code>.
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the name of the sheet be included in the list of columns. Default <code>false</code>
|
||||
*
|
||||
* @param includeSheetName
|
||||
*/
|
||||
public void setIncludeSheetName(final boolean includeSheetName) {
|
||||
this.includeSheetName = includeSheetName;
|
||||
}
|
||||
|
||||
/**
|
||||
* When <code>includeSheetName</code> is <code>true</code> this is the name of the property used to store the name
|
||||
* of the sheet.
|
||||
*
|
||||
* @param attributeForSheetName
|
||||
* @see #setIncludeSheetName(boolean)
|
||||
*/
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* 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.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
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class MappingColumnToAttributeConverter implements ColumnToAttributeConverter {
|
||||
|
||||
private final Map<String, String> mapping = new HashMap<String, String>();
|
||||
|
||||
@Override
|
||||
public String toAttribute(final String column) {
|
||||
if (this.mapping.containsKey(column)) {
|
||||
return this.mapping.get(column);
|
||||
}
|
||||
return column;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toColumn(final String attribute) {
|
||||
if (this.mapping.containsValue(attribute)) {
|
||||
for (Map.Entry<String, String> entry : this.mapping.entrySet()) {
|
||||
if (ObjectUtils.nullSafeEquals(attribute, entry.getValue())) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
return attribute;
|
||||
}
|
||||
|
||||
public void setMappings(final Map<String, String> mappings) {
|
||||
this.mapping.clear();
|
||||
this.mapping.putAll(mappings);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.springframework.batch.item;
|
||||
|
||||
/**
|
||||
* Created by in329dei on 17-9-2014.
|
||||
*/
|
||||
public class Player {
|
||||
|
||||
private String id;
|
||||
private String position;
|
||||
private String lastName;
|
||||
private String firstName;
|
||||
private long birthYear;
|
||||
private int debutYear;
|
||||
private String comment;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(String position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public long getBirthYear() {
|
||||
return birthYear;
|
||||
}
|
||||
|
||||
public void setBirthYear(long birthYear) {
|
||||
this.birthYear = birthYear;
|
||||
}
|
||||
|
||||
public int getDebutYear() {
|
||||
return debutYear;
|
||||
}
|
||||
|
||||
public void setDebutYear(int debutYear) {
|
||||
this.debutYear = debutYear;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ 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.batch.item.excel.support.rowset.RowSet;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -35,7 +36,7 @@ import static org.junit.Assert.assertEquals;
|
||||
*/
|
||||
public abstract class AbstractExcelItemReaderTests {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
protected AbstractExcelItemReader itemReader;
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.springframework.batch.item.excel;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.Player;
|
||||
import org.springframework.batch.item.excel.mapping.BeanPropertyRowMapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Created by in329dei on 17-9-2014.
|
||||
*/
|
||||
public class BeanPropertyItemReaderTest {
|
||||
|
||||
private MockExcelItemReader<Player> reader;
|
||||
|
||||
private ExecutionContext executionContext;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
executionContext = new ExecutionContext();
|
||||
|
||||
List<String[]> rows = new ArrayList<String[]>();
|
||||
rows.add(new String[]{"id", "lastName", "firstName", "position", "birthYear", "debutYear"});
|
||||
rows.add( new String[]{"AbduKa00", "Abdul-Jabbar", "Karim", "rb", "1974", "1996"});
|
||||
rows.add( new String[]{"AbduRa00", "Abdullah", "Rabih", "rb", "1975", "1999"});
|
||||
MockSheet sheet = new MockSheet("players", rows);
|
||||
|
||||
reader = new MockExcelItemReader<Player>(sheet);
|
||||
|
||||
BeanPropertyRowMapper<Player> rowMapper = new BeanPropertyRowMapper<Player>();
|
||||
rowMapper.setTargetType(Player.class);
|
||||
rowMapper.afterPropertiesSet();
|
||||
|
||||
reader.setLinesToSkip(1); // Skip first row as that is the header
|
||||
reader.setRowMapper(rowMapper);
|
||||
|
||||
reader.afterPropertiesSet();
|
||||
reader.open(executionContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readandMapPlayers() throws Exception {
|
||||
Player p1 = reader.read();
|
||||
Player p2 = reader.read();
|
||||
Player p3 = reader.read();
|
||||
assertNotNull(p1);
|
||||
assertNotNull(p2);
|
||||
assertNull(p3);
|
||||
|
||||
// Check first player
|
||||
assertEquals("AbduKa00", p1.getId());
|
||||
assertEquals("Abdul-Jabbar", p1.getLastName());
|
||||
assertEquals("Karim", p1.getFirstName());
|
||||
assertEquals("rb", p1.getPosition());
|
||||
assertEquals(1974, p1.getBirthYear());
|
||||
assertEquals(1996, p1.getDebutYear());
|
||||
// Check second player
|
||||
assertEquals("AbduRa00", p2.getId());
|
||||
assertEquals("Abdullah", p2.getLastName());
|
||||
assertEquals("Rabih", p2.getFirstName());
|
||||
assertEquals("rb", p2.getPosition());
|
||||
assertEquals(1975, p2.getBirthYear());
|
||||
assertEquals(1999, p2.getDebutYear());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.springframework.batch.item.excel;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.Player;
|
||||
import org.springframework.batch.item.excel.MockExcelItemReader;
|
||||
import org.springframework.batch.item.excel.MockSheet;
|
||||
import org.springframework.batch.item.excel.mapping.BeanPropertyRowMapper;
|
||||
import org.springframework.batch.item.excel.support.rowset.DefaultRowSet;
|
||||
import org.springframework.batch.item.excel.support.rowset.DefaultRowSetFactory;
|
||||
import org.springframework.batch.item.excel.support.rowset.StaticColumnNameExtractor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Created by in329dei on 17-9-2014.
|
||||
*/
|
||||
public class BeanPropertyWithStaticHeaderItemReaderTest {
|
||||
|
||||
private MockExcelItemReader<Player> reader;
|
||||
|
||||
private ExecutionContext executionContext;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
executionContext = new ExecutionContext();
|
||||
|
||||
List<String[]> rows = new ArrayList<String[]>();
|
||||
rows.add( new String[]{"AbduKa00", "Abdul-Jabbar", "Karim", "rb", "1974", "1996"});
|
||||
rows.add( new String[]{"AbduRa00", "Abdullah", "Rabih", "rb", "1975", "1999"});
|
||||
MockSheet sheet = new MockSheet("players", rows);
|
||||
|
||||
reader = new MockExcelItemReader<Player>(sheet);
|
||||
|
||||
BeanPropertyRowMapper<Player> rowMapper = new BeanPropertyRowMapper<Player>();
|
||||
rowMapper.setTargetType(Player.class);
|
||||
rowMapper.afterPropertiesSet();
|
||||
|
||||
reader.setRowMapper(rowMapper);
|
||||
|
||||
DefaultRowSetFactory factory = new DefaultRowSetFactory();
|
||||
factory.setColumnNameExtractor(new StaticColumnNameExtractor(new String[]{"id", "lastName", "firstName", "position", "birthYear", "debutYear"}));
|
||||
reader.setRowSetFactory(factory);
|
||||
reader.afterPropertiesSet();
|
||||
reader.open(executionContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readandMapPlayers() throws Exception {
|
||||
Player p1 = reader.read();
|
||||
Player p2 = reader.read();
|
||||
Player p3 = reader.read();
|
||||
assertNotNull(p1);
|
||||
assertNotNull(p2);
|
||||
assertNull(p3);
|
||||
|
||||
// Check first player
|
||||
assertEquals("AbduKa00", p1.getId());
|
||||
assertEquals("Abdul-Jabbar", p1.getLastName());
|
||||
assertEquals("Karim", p1.getFirstName());
|
||||
assertEquals("rb", p1.getPosition());
|
||||
assertEquals(1974, p1.getBirthYear());
|
||||
assertEquals(1996, p1.getDebutYear());
|
||||
// Check second player
|
||||
assertEquals("AbduRa00", p2.getId());
|
||||
assertEquals("Abdullah", p2.getLastName());
|
||||
assertEquals("Rabih", p2.getFirstName());
|
||||
assertEquals("rb", p2.getPosition());
|
||||
assertEquals(1975, p2.getBirthYear());
|
||||
assertEquals(1999, p2.getDebutYear());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.springframework.batch.item.excel;
|
||||
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by in329dei on 17-9-2014.
|
||||
*/
|
||||
public class MockExcelItemReader<T> extends AbstractExcelItemReader<T> {
|
||||
|
||||
|
||||
private final List<MockSheet> sheets;
|
||||
|
||||
public MockExcelItemReader(MockSheet sheet) {
|
||||
this(Collections.singletonList(sheet));
|
||||
}
|
||||
|
||||
public MockExcelItemReader(List<MockSheet> sheets) {
|
||||
this.sheets=sheets;
|
||||
super.setResource(new ByteArrayResource(new byte[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet getSheet(int sheet) {
|
||||
return sheets.get(sheet);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getNumberOfSheets() {
|
||||
return sheets.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void openExcelFile(Resource resource) throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.springframework.batch.item.excel;
|
||||
|
||||
import jxl.Cell;
|
||||
import org.springframework.batch.item.excel.jxl.JxlUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sheet implementation usable for testing. Works in an {@code List} of {@xode String[]}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class MockSheet implements Sheet {
|
||||
|
||||
private final List<String[]> rows;
|
||||
private final String name;
|
||||
|
||||
public MockSheet(String name, List<String[]> rows) {
|
||||
this.name = name;
|
||||
this.rows = rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfRows() {
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getRow(int rowNumber) {
|
||||
if (rowNumber < getNumberOfRows()) {
|
||||
return this.rows.get(rowNumber);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfColumns() {
|
||||
if (rows.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
return rows.get(0).length;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package org.springframework.batch.item.excel.mapping;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.item.Player;
|
||||
import org.springframework.batch.item.excel.MockSheet;
|
||||
import org.springframework.batch.item.excel.support.rowset.DefaultRowSetFactory;
|
||||
import org.springframework.batch.item.excel.support.rowset.RowSet;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Created by in329dei on 17-9-2014.
|
||||
*/
|
||||
public class BeanPropertyRowMapperTest {
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void givenNoNameWhenInitCompleteThenIllegalStateShouldOccur() throws Exception {
|
||||
BeanPropertyRowMapper mapper = new BeanPropertyRowMapper();
|
||||
mapper.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAValidRowWhenMappingThenAValidPlayerShouldBeConstructed() throws Exception {
|
||||
BeanPropertyRowMapper<Player> mapper = new BeanPropertyRowMapper<Player>();
|
||||
mapper.setTargetType(Player.class);
|
||||
mapper.afterPropertiesSet();
|
||||
|
||||
List<String[]> rows = new ArrayList<String[]>();
|
||||
rows.add(new String[]{"id", "lastName", "firstName", "position", "birthYear", "debutYear"});
|
||||
rows.add( new String[]{"AbduKa00", "Abdul-Jabbar", "Karim", "rb", "1974", "1996"});
|
||||
MockSheet sheet = new MockSheet("players", rows);
|
||||
|
||||
|
||||
RowSet rs = new DefaultRowSetFactory().create(sheet);
|
||||
rs.next();
|
||||
rs.next();
|
||||
|
||||
Player p = mapper.mapRow(rs);
|
||||
assertNotNull(p);
|
||||
assertEquals("AbduKa00", p.getId());
|
||||
assertEquals("Abdul-Jabbar", p.getLastName());
|
||||
assertEquals("Karim", p.getFirstName());
|
||||
assertEquals("rb", p.getPosition());
|
||||
assertEquals(1974, p.getBirthYear());
|
||||
assertEquals(1996, p.getDebutYear());
|
||||
assertNull(p.getComment());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenAValidRowWhenMappingThenAValidPlayerShouldBeConstructedBasedOnPrototype() throws Exception {
|
||||
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(TestConfig.class);
|
||||
BeanPropertyRowMapper<Player> mapper = new BeanPropertyRowMapper<Player>();
|
||||
mapper.setPrototypeBeanName("player");
|
||||
mapper.setBeanFactory(ctx);
|
||||
mapper.afterPropertiesSet();
|
||||
|
||||
List<String[]> rows = new ArrayList<String[]>();
|
||||
rows.add(new String[]{"id", "lastName", "firstName", "position", "birthYear", "debutYear"});
|
||||
rows.add( new String[]{"AbduKa00", "Abdul-Jabbar", "Karim", "rb", "1974", "1996"});
|
||||
MockSheet sheet = new MockSheet("players", rows);
|
||||
|
||||
RowSet rs = new DefaultRowSetFactory().create(sheet);
|
||||
rs.next();
|
||||
rs.next();
|
||||
Player p = mapper.mapRow(rs);
|
||||
|
||||
assertNotNull(p);
|
||||
assertEquals("AbduKa00", p.getId());
|
||||
assertEquals("Abdul-Jabbar", p.getLastName());
|
||||
assertEquals("Karim", p.getFirstName());
|
||||
assertEquals("rb", p.getPosition());
|
||||
assertEquals(1974, p.getBirthYear());
|
||||
assertEquals(1996, p.getDebutYear());
|
||||
assertEquals("comment from context", p.getComment());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class TestConfig {
|
||||
|
||||
@Bean
|
||||
@Scope(value = "prototype")
|
||||
public Player player() {
|
||||
Player p = new Player();
|
||||
p.setComment("comment from context");
|
||||
return p;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* 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.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.RowSet;
|
||||
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(RowSet.class))).thenReturn(fs);
|
||||
Mockito.when(this.fieldSetMapper.mapFieldSet(fs)).thenReturn(result);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,33 +16,31 @@
|
||||
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 org.springframework.batch.item.excel.MockSheet;
|
||||
import org.springframework.batch.item.excel.support.rowset.DefaultRowSetFactory;
|
||||
import org.springframework.batch.item.excel.support.rowset.RowSet;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
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}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
*
|
||||
* @author Marten Deinum
|
||||
*/
|
||||
public class PassThroughRowMapperTests {
|
||||
public class PassThroughRowMapperTest {
|
||||
|
||||
private final PassThroughRowMapper rowMapper = new PassThroughRowMapper();
|
||||
|
||||
@Test
|
||||
public void mapRowShouldReturnSameValues() throws Exception {
|
||||
final String[] row = new String[] { "foo", "bar", "baz" };
|
||||
Sheet sheet = mock(Sheet.class);
|
||||
when(sheet.getRow(0)).thenReturn(row);
|
||||
when(sheet.getNumberOfRows()).thenReturn(1);
|
||||
RowSet rs = new RowSet(sheet);
|
||||
assertTrue(rs.next());
|
||||
|
||||
final String[] row = new String[]{"foo", "bar", "baz"};
|
||||
MockSheet sheet = new MockSheet("mock", Collections.singletonList( row));
|
||||
RowSet rs = new DefaultRowSetFactory().create(sheet);
|
||||
assertTrue(rs.next());
|
||||
assertArrayEquals(row, this.rowMapper.mapRow(rs));
|
||||
}
|
||||
|
||||
@@ -20,11 +20,7 @@ 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;
|
||||
public class PoiItemReaderXlsTest extends AbstractExcelItemReaderTests {
|
||||
|
||||
@Override
|
||||
protected AbstractExcelItemReader createExcelItemReader() {
|
||||
@@ -19,7 +19,7 @@ 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 {
|
||||
public class PoiItemReaderXlsxTest extends AbstractExcelItemReaderTests {
|
||||
|
||||
@Override
|
||||
protected void configureItemReader(AbstractExcelItemReader itemReader) {
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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.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<String, String> mappings = new HashMap<String, String>();
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* 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.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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
</appender>
|
||||
|
||||
<category name="org.springframework.batch">
|
||||
<level value="DEBUG" />
|
||||
<level value="DEBUG"/>
|
||||
</category>
|
||||
|
||||
<root>
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user