package nl.bstoi.poiparser.core.strategy; import nl.bstoi.poiparser.api.strategy.converter.Converter; import nl.bstoi.poiparser.core.exception.PoiParserException; import nl.bstoi.poiparser.core.exception.RequiredFieldPoiParserException; import nl.bstoi.poiparser.core.strategy.factory.DefaultConverterFactory; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.poi.ss.usermodel.*; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; /** * User: Hylke Stapersma * E-mail:[ hylke.stapersma@gmail.com] * Date: 23-06-13 * Time: 21:42 */ public abstract class AbstractReadPoiParser<T> { private final static Log log = LogFactory.getLog(AbstractReadPoiParser.class); private final Class<T> clazz; private final Set<CellDescriptor> cellDescriptors; private final Sheet sheet; private final boolean ignoreEmptyRows = true; private boolean ignoreFirstRow = false; private final DefaultConverterFactory DEFAULTCONVERTERFACTORY = new DefaultConverterFactory(); private DefaultConverterFactory converterFactory = DEFAULTCONVERTERFACTORY; public AbstractReadPoiParser(final Set<CellDescriptor> cellDescriptors, final Sheet sheet, final Class<T> clazz) { if (null == cellDescriptors) throw new IllegalArgumentException("Cell descriptors cannot be null"); if (null == sheet) throw new IllegalArgumentException("Sheet cannot be null"); if (null == clazz) throw new IllegalArgumentException("Clazz cannot be null"); this.cellDescriptors = cellDescriptors; this.sheet = sheet; this.clazz = clazz; } public Set<CellDescriptor> getCellDescriptors() { return cellDescriptors; } protected List<T> readSheet() throws PoiParserException { try { final List<T> dimensionList = new ArrayList<T>(); if (null != getSheet()) { final Iterator<Row> rowIterator = sheet.rowIterator(); if (rowIterator.hasNext()) { // Skip first row if (ignoreFirstRow) rowIterator.next(); while (rowIterator.hasNext()) { final Row row = (Row) rowIterator.next(); if (!ignoreRow(row)) { T rowObject = readRow(sheet.getSheetName(), row, clazz.newInstance()); dimensionList.add(rowObject); } } } } return dimensionList; } catch (InstantiationException e) { throw new PoiParserException("Error while creating instance of clazz", e); } catch (IllegalAccessException e) { throw new PoiParserException("Error while accessing clazz", e); } } protected List<T> readSheet(final int startRow,final int endRow) throws PoiParserException { try { List<T> dimensionList = new ArrayList<T>(); if (null != sheet) { for (int i = startRow; i <= endRow; i++) { Row row = (Row) sheet.getRow(i); if (!ignoreRow(row)) { T rowObject = readRow(sheet.getSheetName(), row, clazz.newInstance()); dimensionList.add(rowObject); } } } return dimensionList; } catch (InstantiationException e) { throw new PoiParserException("Error while creating instance of clazz", e); } catch (IllegalAccessException e) { throw new PoiParserException("Error while accessing clazz", e); } } public boolean ignoreRow(final Row row) { if (isEmptyRow(row)) { return ignoreEmptyRows; } return false; } public boolean isEmptyRow(final Row row) { if (null != row) { for (final CellDescriptor cellDescriptor : getCellDescriptors()) { final Integer columnNumber = cellDescriptor.getColumnNumber(); if (!isEmptyValue(row.getCell(columnNumber, Row.RETURN_BLANK_AS_NULL))) return false; } } return true; } public boolean isEmptyValue(final Cell cell) { if (null == cell) return true; return false; } protected T readRow(final String sheetName, final Row row,final T rowDimension) throws PoiParserException { if (null != row) { log.debug("Read row with number: " + row.getRowNum()); for (CellDescriptor cellDescriptor : getCellDescriptors()) { readField(sheetName, row, rowDimension, cellDescriptor); } } return rowDimension; } protected void readField(final String sheetName,final Row row,final T rowDimension,final CellDescriptor cellDescriptor) throws PoiParserException { try { if (cellDescriptor.isReadIgnore()) { return; // When field must be ignored when reading then do nothing. } final Cell cell = row.getCell(cellDescriptor.getColumnNumber(), Row.RETURN_BLANK_AS_NULL); if (null != cell) { log.trace("Reading field " + cellDescriptor.getFieldName() + " on row " + row.getRowNum() + " that is mapped on column " + cellDescriptor.getColumnNumber() + " with value: " + cell.toString()); Converter converter = converterFactory.getConverter(cellDescriptor.getType()); if (null != converter) { try { populateDimensionAsField(rowDimension, cellDescriptor.getFieldName(), converter, cell); } catch (PoiParserException e) { populateDimensionAsProperty(rowDimension, cellDescriptor.getFieldName(), converter, cell); } } else { // Unsupported type } } else { log.trace("Reading field " + cellDescriptor.getFieldName() + " on row " + row.getRowNum() + " that is mapped on column " + cellDescriptor.getColumnNumber() + " is empty."); if (cellDescriptor.isRequired()) { throw new RequiredFieldPoiParserException(sheetName, row.getRowNum(), cellDescriptor.getColumnNumber()); } } } catch (final PoiParserException e) { throw e; } catch (final InstantiationException e) { throw new PoiParserException("Error while setting field/property", e); } catch (final IllegalAccessException e) { throw new PoiParserException("Error while setting field/property", e); } } private void populateDimensionAsField(final T rowDimension,final String fieldName,final Converter<T> converter,final Cell cell) throws PoiParserException { try { rowDimension.getClass().getDeclaredField(fieldName).setAccessible(true); final Field field = rowDimension.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(rowDimension, converter.readCell(cell)); field.setAccessible(false); } catch (IllegalAccessException e) { throw new PoiParserException("Unable set field " + fieldName, e); } catch (NoSuchFieldException e) { throw new PoiParserException("Unable set field " + fieldName, e); } } private void populateDimensionAsProperty(final T rowDimension, final String propertyName,final Converter<T> converter, final Cell cell) throws PoiParserException { try { createRequiredUnderlyingInstancesForNestedProperties(rowDimension, propertyName); PropertyUtils.setNestedProperty(rowDimension, propertyName, converter.readCell(cell)); } catch (IllegalAccessException e) { throw new PoiParserException("Unable set property " + propertyName, e); } catch (InvocationTargetException e) { throw new PoiParserException("Unable set property " + propertyName, e); } catch (NoSuchMethodException e) { throw new PoiParserException("Unable set property " + propertyName, e); } } private void createRequiredUnderlyingInstancesForNestedProperties(final T rowDimension,final String fieldName) { String concatName = null; final String[] propertyNames = fieldName.split("\\."); for (final String createdInstanceName : propertyNames) { if (fieldName.endsWith(createdInstanceName)) return; if (null == concatName) { concatName = createdInstanceName; } else { concatName += "." + createdInstanceName; } try { if (null == PropertyUtils.getProperty(rowDimension, concatName)) { final Object x = PropertyUtils.getPropertyDescriptor(rowDimension, concatName).getPropertyType().newInstance(); PropertyUtils.setNestedProperty(rowDimension, concatName, x); } } catch (final IllegalAccessException e) { log.trace("Error creating underlying instance", e); } catch (final InvocationTargetException e) { log.trace("Error creating underlying instance", e); } catch (final NoSuchMethodException e) { log.trace("Error creating underlying instance", e); } catch (final InstantiationException e) { log.trace("Error creating underlying instance", e); } } } public Sheet getSheet() { return sheet; } public void setIgnoreFirstRow(final boolean ignoreFirstRow){ this.ignoreFirstRow = ignoreFirstRow; } }