Index: pom.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/pom.xml b/pom.xml --- a/pom.xml (revision 65a9395cd55cf65436ad114a07b65357079685c4) +++ b/pom.xml (revision eae239508e48579f286db2c29880864fa5f51db1) @@ -6,7 +6,7 @@ com.github.ozlerhakan poiji - 3.1.7 + 3.1.8-SNAPSHOT jar poiji Index: src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java --- a/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java (revision 65a9395cd55cf65436ad114a07b65357079685c4) +++ b/src/main/java/com/poiji/bind/mapping/HSSFUnmarshaller.java (revision eae239508e48579f286db2c29880864fa5f51db1) @@ -10,6 +10,8 @@ import com.poiji.config.Casting; import com.poiji.config.Formatting; import com.poiji.exception.IllegalCastException; +import com.poiji.exception.PoijiMultiRowException; +import com.poiji.exception.PoijiMultiRowException.PoijiRowSpecificException; import com.poiji.option.PoijiOptions; import com.poiji.util.AnnotationUtil; import com.poiji.util.ReflectUtil; @@ -79,6 +81,7 @@ void processRowsToObjects(Sheet sheet, Class type, Consumer consumer) { int skip = options.skip(); int maxPhysicalNumberOfRows = sheet.getPhysicalNumberOfRows() + 1 - skip; + List errors = new ArrayList<>(); loadColumnTitles(sheet, maxPhysicalNumberOfRows); AnnotationUtil.validateMandatoryNameColumns(options, formatting, type, titleToIndex, indexToTitle); @@ -90,9 +93,19 @@ if (limit != 0 && internalCount > limit) return; - T instance = deserializeRowToInstance(currentRow, type); - consumer.accept(instance); + try { + T instance = deserializeRowToInstance(currentRow, type); + consumer.accept(instance); + } catch (PoijiMultiRowException poijiRowException) { + errors.add(poijiRowException); + } } + } + if (!errors.isEmpty()) { + List allErrors = errors.stream() + .flatMap((PoijiMultiRowException e) -> e.getErrors().stream()) + .collect(Collectors.toList()); + throw new PoijiMultiRowException("Problem(s) occurred while reading data", allErrors); } } @@ -163,6 +176,7 @@ private T tailSetFieldValue(Row currentRow, Class type, T instance) { List mappedColumnIndices = new ArrayList<>(); List unknownCells = new ArrayList<>(); + List errors = new ArrayList<>(); for (Field field : type.getDeclaredFields()) { if (field.getAnnotation(ExcelRow.class) != null) { @@ -174,15 +188,29 @@ Class fieldType = field.getType(); Object fieldInstance = ReflectUtil.newInstanceOf(fieldType); for (Field fieldField : fieldType.getDeclaredFields()) { - mappedColumnIndices.add(tailSetFieldValue(currentRow, fieldInstance, fieldField)); + try { + mappedColumnIndices.add( + tailSetFieldValue(currentRow, fieldInstance, fieldField) + ); + } catch (PoijiRowSpecificException poijiRowException) { + errors.add(poijiRowException); + } } setFieldData(instance, field, fieldInstance); } else if (field.getAnnotation(ExcelUnknownCells.class) != null) { unknownCells.add(field); } else { - mappedColumnIndices.add(tailSetFieldValue(currentRow, instance, field)); + try { + mappedColumnIndices.add(tailSetFieldValue(currentRow, instance, field)); + } catch (PoijiRowSpecificException poijiRowException) { + errors.add(poijiRowException); + } } } + + if (!errors.isEmpty()) { + throw new PoijiMultiRowException("Problem(s) occurred while reading data", errors); + } Map excelUnknownCellsMap = StreamSupport .stream(Spliterators.spliteratorUnknownSize(currentRow.cellIterator(), Spliterator.ORDERED), false) @@ -216,10 +244,19 @@ annotationDetail.setDisabledCellFormat(disableCellFormat.value()); } + ExcelCellName excelCellName = field.getAnnotation(ExcelCellName.class); + ExcelCell excelCell = field.getAnnotation(ExcelCell.class); + if (options.isMandatoryColumnsApplyToCellValues()) { + if (excelCellName != null) { + annotationDetail.setMandatoryCell(excelCellName.mandatory()); + annotationDetail.setColumnName(excelCellName.value()); + } else if (excelCell != null) { + annotationDetail.setMandatoryCell(excelCell.mandatory()); + } + } if (index != null) { annotationDetail.setColumn(index.value()); } else { - ExcelCellName excelCellName = field.getAnnotation(ExcelCellName.class); if (excelCellName != null) { final String titleName = formatting.transform(options, excelCellName.value()); Integer column = titleToIndex.get(titleName); @@ -244,6 +281,12 @@ } Object data = casting.castValue(field, value, currentRow.getRowNum(), annotationDetail.getColumn(), options); setFieldData(instance, field, data); + } else if (annotationDetail.isMandatoryCell()) { + throw new PoijiRowSpecificException( + "Cell value of column '" + annotationDetail.getColumnName() + "' is null," + + " so cannot be applied to mandatory field '" + field.getName() + "'.", + currentRow.getRowNum() + ); } } @@ -278,23 +321,41 @@ private static class FieldAnnotationDetail { private Integer column; + private String columnName; private boolean disabledCellFormat; + private boolean mandatoryCell; Integer getColumn() { return column; } + void setColumn(Integer column) { + this.column = column; + } + + public String getColumnName() { + return columnName; + } + + public void setColumnName(String columnName) { + this.columnName = columnName; + } + boolean isDisabledCellFormat() { return disabledCellFormat; } - void setColumn(Integer column) { - this.column = column; - } - void setDisabledCellFormat(boolean disabledCellFormat) { this.disabledCellFormat = disabledCellFormat; } + + public boolean isMandatoryCell() { + return mandatoryCell; + } + + public void setMandatoryCell(boolean mandatoryCell) { + this.mandatoryCell = mandatoryCell; + } } } Index: src/main/java/com/poiji/exception/PoijiMultiRowException.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/main/java/com/poiji/exception/PoijiMultiRowException.java b/src/main/java/com/poiji/exception/PoijiMultiRowException.java new file mode 100644 --- /dev/null (revision eae239508e48579f286db2c29880864fa5f51db1) +++ b/src/main/java/com/poiji/exception/PoijiMultiRowException.java (revision eae239508e48579f286db2c29880864fa5f51db1) @@ -0,0 +1,38 @@ +package com.poiji.exception; + +import java.util.List; + +/** + * PoijiMultiRowException is the superclass of RuntimeException that + * can be thrown during the mapping process of excel service. + * + * @since Poiji 3.1.8 + */ +@SuppressWarnings("serial") +public class PoijiMultiRowException extends PoijiException { + + private final List errors; + + public PoijiMultiRowException(String message, List errors) { + super(message); + this.errors = errors; + } + + public List getErrors() { + return errors; + } + + public static class PoijiRowSpecificException extends RuntimeException { + + private final Integer rowNum; + + public PoijiRowSpecificException(String message, Integer rowNum) { + super(message); + this.rowNum = rowNum; + } + + public Integer getRowNum() { + return rowNum; + } + } +} Index: src/main/java/com/poiji/option/PoijiOptions.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/main/java/com/poiji/option/PoijiOptions.java b/src/main/java/com/poiji/option/PoijiOptions.java --- a/src/main/java/com/poiji/option/PoijiOptions.java (revision 65a9395cd55cf65436ad114a07b65357079685c4) +++ b/src/main/java/com/poiji/option/PoijiOptions.java (revision eae239508e48579f286db2c29880864fa5f51db1) @@ -49,6 +49,7 @@ private Formatting formatting; private Locale locale; private boolean rawData; + private boolean mandatoryColumnsApplyToCellValues; public PoijiNumberFormat getPoijiNumberFormat() { return numberFormat; @@ -298,6 +299,15 @@ this.rawData = rawData; return this; } + + public boolean isMandatoryColumnsApplyToCellValues() { + return mandatoryColumnsApplyToCellValues; + } + + public PoijiOptions setMandatoryColumnsApplyToCellValues(boolean mandatoryColumnsApplyToCellValues) { + this.mandatoryColumnsApplyToCellValues = mandatoryColumnsApplyToCellValues; + return this; + } public static class PoijiOptionsBuilder { @@ -327,6 +337,7 @@ private String listDelimiter = "\\s*,\\s*"; private Locale locale = Locale.US; private boolean rawData; + private boolean mandatoryColumnsApplyToCellValues; private PoijiOptionsBuilder() { } @@ -440,7 +451,8 @@ .setListDelimiter(listDelimiter) .setFormatting(formatting) .setLocale(locale) - .setRawData(rawData); + .setRawData(rawData) + .setMandatoryColumnsApplyToCellValues(mandatoryColumnsApplyToCellValues); } @@ -620,6 +632,7 @@ * Default - false. * * @param caseInsensitive true or false + * @return this */ public PoijiOptionsBuilder caseInsensitive(final boolean caseInsensitive) { this.caseInsensitive = caseInsensitive; @@ -631,6 +644,7 @@ * Default - false. * * @param ignoreWhitespaces true or false + * @return this */ public PoijiOptionsBuilder ignoreWhitespaces(final boolean ignoreWhitespaces) { this.ignoreWhitespaces = ignoreWhitespaces; @@ -642,6 +656,7 @@ * This option should be enabled for debugging purpose. * * @param cellFormat poiji cell format instance + * @return this */ public PoijiOptionsBuilder poijiLogCellFormat(final PoijiLogCellFormat cellFormat) { this.cellFormat = cellFormat; @@ -652,6 +667,7 @@ * Change the default cell formats of a xlsx excel file by overriding * * @param numberFormat poiji number format instance + * @return this */ public PoijiOptionsBuilder poijiNumberFormat(final PoijiNumberFormat numberFormat) { this.numberFormat = numberFormat; @@ -703,6 +719,17 @@ this.rawData = status; return this; } + + /** + * Use this option to apply the mandatory column constraint to cell values as well. + * + * @param mandatoryColumnsApplyToCellValues set true to apply the mandatory column constraint to cell values as well. + * @return this + */ + public PoijiOptionsBuilder mandatoryColumnsApplyToCellValues(boolean mandatoryColumnsApplyToCellValues) { + this.mandatoryColumnsApplyToCellValues = mandatoryColumnsApplyToCellValues; + return this; + } } }